精华内容
下载资源
问答
  • Solidity教程

    2018-05-23 13:40:55
    Solidity是以太坊智能...Solidity教程会是一系列文章,本文是第一篇:介绍Solidity的变量类型。 本文前半部分是参考Solidity官方文档(当前最新版本:0.4.20)进行翻译,后半部分是结合实际合约代码实例说明类型的使用
  • solidity教程:solidity语言入门

    万次阅读 多人点赞 2018-09-28 14:19:00
    这篇关于Solidity教程的博客展示了很多Solidity特性。本教程假定你对以太坊虚拟机和编程有一定的了解。 以太坊,“世界计算机”提供了一个非常强大的全球共享基础设施,使用名为Solidity的编程语言构建去中心化应用...

    这篇关于Solidity教程的博客展示了很多Solidity特性。本教程假定你对以太坊虚拟机和编程有一定的了解。

    以太坊,“世界计算机”提供了一个非常强大的全球共享基础设施,使用名为Solidity的编程语言构建去中心化应用程序。

    让我们开始我们的Solidity教程,介绍Solidity。

    什么是Solidity?

    以太坊Solidity是一种面向智能合约的高级语言,其语法与JavaScript类似。solidity是用于生成在EVM上执行的机器级代码的工具。solidity编译器获取高级代码并将其分解为更简单的指令。Solidity代码封装在Contracts中。

    以太坊合约中的solidity

    合约是以太坊去中心化应用程序的基本构建模块。所有变量和函数都是合约的一部分,这是所有项目的起点。一个名为MyFirst的空合约看起来像这样:

    version pragma ^0.4.19;
    contract MyFirst{
    }
    

    11831773-5210aaa8a79bc143.jpg

    image

    盯紧你的屏幕因为接下来在我们的Solidity教程中我们将开始编码......

    Solidity文件的布局

    源文件可以包含任意数量的合约定义,包括指令和pragma指令。

    Version Pragma

    Version Pragma是定义代码使用的Solidity编译器版本的声明。

    version pragma ^0.4.00;
    

    注意:上面显示的源文件不会使用早于版本0.4.0的编译器进行编译,也不能在从版本0.5.0开始的编译器上运行。

    导入其他源文件

    Ethereum Solidity支持与JavaScript中可用的导入语句非常相似的导入语句,尽管Solidity不知道default export的概念。

    在全局级别,可以使用以下形式的import语句:

    import "filename";
    

    上述语句将所有全局符号从filename导入当前全局范围。

    import * as symbolName from "filename";
    

    注释

    就像任何其他语言一样,Solidity可以使用单行和多行注释。

    // This is a single-line comment.
    /*
    This is a
    multi-line comment
    */
    

    现在,在我们进一步深入了解Solidity教程之前,应该知道以太坊有三个可以存储项目的区域。

    • 存储Storage:所有合约状态变量所在的位置。每个合约都有自己的存储,并且在函数调用之间是持久的。
    • 内存Memory:保存临时值并在(外部)函数调用之间擦除,并且使用起来更便宜。
    • 堆栈Stack:保存小的局部变量并且几乎可以免费使用,但只能保存有限数量的值。

    对于几乎所有类型,都无法指定它们应存储的位置,因为它们在每次使用时都会被复制。

    好了,既然你已经知道以太坊Solidity中的存储位置,那么让我告诉你一般的值类型。

    solidity中的值类型

    以下类型也称为值类型,因为这些类型的变量将始终按值传递。

    11831773-db337755867d1e66.png

    image

    布尔

    关键词:bool

    值是常数,即true或false。

    整型

    关键字:int/uint(uint8到uint256,步长为8(无符号,最多为256位),int8为int256)

    各种大小的有符号和无符号整数。

    例:

    contract MySample{
    uint UnsignedInt =50;
    }
    

    在上面的语句中,我们创建了一个名为InsignedInt的uint并将其设置为50。

    地址

    关键字:address

    保存一个20字节的值(以太坊地址的大小)。地址类型也有members,并作为所有合约的基础。

    地址成员:balance与transfer

    可以使用属性balance查询地址的余额,并使用transfer函数将以太网发送到地址。

    address x = 0x123;
    address myAddress = this;
    if  (x.balance < 10 && myAddress.balance > = 10)
    x.transfer(10);
    

    字符串

    String:字符串文字用双引号或单引号如“foo”或'bar'编写。

    用于任意长度的UTF数据。

    string language ="Solidity";
    

    这些值类型可以在包含运算符的表达式中相互交互。接下来,在我们的Solidity教程中,让我告诉你各种运算符。

    运算符

    solidity的运算符与JavaScript相同。Solidity有四种类型的运算符:

    11831773-9dc8c733c3bc5b76.png

    image

    算术运算符

    Solidity具有非常简单的数学运算。以下与大多数编程语言类似:

    • 增加:x + y
    • 减法:x - y
    • 乘法:x * y
    • 除法:x / y
    • 取整/求余:x%y

    Solidity还提供了使用指数运算符的选项,具体如下:

    uint x = 10 **  3; // equal to 10^3 = 1000
    

    增量运算符

    增量运算符的稳定性:a++,a- ,++a,-a,a+=1,a=a+1

    适用于其他编程语言的规则也是类似的。

    按位运算符

    以下是运算符:(按位OR)'|',(按位异或),(按位求反)'~',(按位右移)'>>',(按位左移)'<<'

    逻辑运算符

    Solidity中的逻辑运算符:!(逻辑否定),&&(逻辑和),||(逻辑或),==(相等),!=(不相等)

    例:

    contract operators {
    // Arithmetic Operators
    // +,-,*,/, %, **
    // Incremental Operators
    // a++, a--, a+=1, a=a+1,++a,--a;
    a=10;
    a= a++; //here, output will be 10, because the value is first returned and then then increment is done
    a=++a;
    //Logical Operators
    !, &&, ||, ==, !=
    isOwner = true && false;
    var orValue= 0x02 | 0x01; // output would be 0x03
    //Bitwise Operators~,>>, <<;
    function Operators() {
    // Initialize state variables here}}
    

    现在有时需要更复杂的数据类型。为此,Solidity提供结构。

    solidity数据结构

    Solidity提供三种类型的数据结构:

    11831773-01cea2a550393737.png

    image

    结构Structs

    Solidity提供了一种以Structs形式定义新类型的方法。Structs是自定义类型,可以对多个变量进行分组。

    pragma solidity ^0.4.0;
    contract Ballot {
    struct Voter { // Struct
    uint weight1, weight2, weight3;
    bool voted;
    address delegate1, delegate2, delegate3, delegate4;
    string name;
    uint vote1, vote2, vote3, vote4, vote5;
    uint height1, height2, height3   } }
    

    注意:结构只能有16个成员,超过该成员可能会发生以下错误:Stack too Deep 堆栈太深。

    结构允许创建具有多个属性的更复杂的数据类型。

    现在,如果你需要一些集合,比如说地址,那该怎么办?好吧,就像大多数语言一样,Solidity也有数组。

    数组Arrays

    Solidity中的数组可以具有编译时固定大小,也可以是动态的。

    uint[3] fixed;  //array of fixed length 3
    uint[] dynamic; //a dynamic array has no fixed size, it can keep growing
    

    还可以创建一个结构数组。使用以前创建的Voter结构:

    Voter[] voting;
    

    注意:将数组声明为public将自动为其创建getter方法。

    Voter[] public voting;
    

    映射mappings

    映射可以看作是哈希表,它们被虚拟地初始化,使得每个可能的键都存在并被映射到其字节表示全为零的值:类型的默认值。

    映射声明为:

    Mapping(_Keytype => _ValueType )
    

    注意:_Keytype几乎可以是任何类型,除了动态大小的数组,合约,枚举和结构。

    例:

    contract MappingExample {
    mapping(address => uint) public balances;
    function update(uint newBalance) {
    balances[msg.sender] = newBalance;  }}
    contract MappingUser {
    function f() returns (uint) {
    MappingExample m = new MappingExample();
    m.update(100);
    return m.balances(this);
    }}
    

    控制结构

    除了switch和goto之外,JavaScript中的大多数控制结构都在Solidity中可用。

    所以有:if,else,while,do,for,break,continue,return,? :,使用从C或JavaScript中已知的通常语义。

    注意:没有像C和JavaScript那样从非布尔类型到布尔类型的类型转换。

    现在让我们看看这些控制结构如何在Solidity中使用。

    contract ControlStructure {
    address public a;
    function ControlStructure>){
    // if-else can be used like this
    if(input1==2)
    a=1;
    else
    a=0;
    // while can be used like this
    while(input1>=0){
    if(input1==5)
    continue;
    input1=input1-1;
    a++;}
    // for loop can be used like this
    for(uint i=0;i<=50;i++) { a++; if(a==4) break; } //do while can be used like this do { a--; } (while a>0);
    // Conditional Operator can be used like this
    bool IsTrue = (a == 1)?true: false;
    /*will show an error because
    there is no type conversion from non-boolean to boolean
    */
    if(1)
    {
    }
    

    继续我们的Solidity教程博客,让我们谈谈合约中可执行的代码单元。这些被称为函数。

    函数

    以下是在Solidity中声明函数的方式。

    function sampleFunc(string name, uint amount) {
    }
    

    上面声明的是一个空体函数,它有两个参数:一个字符串和一个uint。

    可以这样调用此函数:

    sampleFunc("Shashank", 10000);
    

    谈到函数,Solidity还提供函数修饰符。

    函数修饰符

    它用于轻松更改函数的行为。甚至在进行函数调用之前也可以检查这些条件,因为它们已在智能合约的函数定义中声明。

    示例:如果要仅通过函数的所有者或创建者调用kill contract函数。

    contract FunctionModifiers{
    address public creator;
    function FunctionModifiers() {
    creator = msg.sender;}
    Modifier onlyCreator() {
    if(msg.sender!=creator){
    throw; }
    _; //resumes the function wherever the access modifier is used
    }
    function killContract() onlyCreator{ //function will not execute if an exception occurs
    self-destruct(creator); }}
    

    继承

    Solidity通过复制包含多态的代码来支持多重继承。

    contract Owned {
    address Owner ;
    function owned() {
    owner = msg.sender;
    }}
    contract Mortal is Owned {  // 'is' keyword is used for inheritance
    function kill(){
    self-destruct(owner);   }}
    contract User is Owned, Mortal //Multiple inheritance
    {
    string public UserName;
    function User(string _name){
    UserName = _name;
    }}
    

    好吧,我觉得上面讨论的概念足以让你开始使用Solidity编程。

    去写代码吧!!

    有了这个,我结束了这个Solidity Tutorial博客。我希望你喜欢阅读这篇博客并发现它内容丰富。到目前为止,必须对Solidity Programming Language的理解有所了解。现在去练习吧。

    如果希望快速进行以太坊开发,那请看我们精心打造的教程:

    以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。

    其他区块链教程如下:

    • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
    • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
    • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
    • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
    • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
    • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
    • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
    • EOS入门教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。

    汇智网原创翻译,转载请标明出处。这里是原文

    展开全文
  • 看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。 如果你想了解更多有关于机器学习、深度学习、区块链、计算机视觉等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们...
  • title: solidity教程(四)交易 tags: solidity,eth 声明:本系列教程是整理cryptozombies 而来 https://cryptozombies.io/zh Lesson 4: 僵尸作战系统 这一刻终于来了, 人类…… 是时候让你的僵尸战斗了! 不过...

    title: solidity教程(四)僵尸作战系统
    tags: solidity,eth

    声明:本系列教程是整理cryptozombies 而来

    https://cryptozombies.io/zh


    Lesson 4: 僵尸作战系统
    这一刻终于来了, 人类……

    是时候让你的僵尸战斗了!

    不过僵尸大战并不适合胆小的人……

    在这一课, 我们将综合利用在前面课程中学到的许多知识,创建一个僵尸作战系统。 我们也将学习 payable 函数,学习如何开发可以接收其他玩家付款的DApp。

    少年,准备开始了吗?

    第1章: 可支付

    截至目前,我们只接触到很少的函数修饰符。 要记住所有的东西很难,所以我们来个概览:

    我们有决定函数何时和被谁调用的可见性修饰符: private 意味着它只能被合约内部调用; internal 就像private但是也能被继承的合约调用; external 只能从合约外部调用;最后public 可以在任何地方调用,不管是内部还是外部。

    我们也有状态修饰符, 告诉我们函数如何和区块链交互:view 告诉我们运行这个函数不会更改和保存任何数据;pure告诉我们这个函数不但不会往区块链写数据,它甚至不从区块链读取数据。这两种在被从合约外部调用的时候都不花费任何gas(但是它们在被内部其他函数调用的时候将会耗费gas)。

    然后我们有了自定义的 modifiers,例如在第三课学习的: onlyOwneraboveLevel。 对于这些修饰符我们可以自定义其对函数的约束逻辑。

    这些修饰符可以同时作用于一个函数定义上:

    function test() external view onlyOwner anotherModifier { /* ... */ }
    

    在这一章,我们来学习一个新的修饰符 payable.

    payable 修饰符

    payable 方法是让 Solidity 和以太坊变得如此酷的一部分 —— 它们是一种可以接收以太的特殊函数。

    当你在调用一个普通网站服务器上的API函数的时候,你无法用你的函数传送美元——你也不能传送比特币。

    但是在以太坊中, 因为钱 (以太), 数据 (事务负载), 以及合约代码本身都存在于以太坊。你可以在同时调用函数 并付钱给另外一个合约。

    这就允许出现很多有趣的逻辑, 比如向一个合约要求支付一定的钱来运行一个函数。

    来看个例子

    contract OnlineStore {
      function buySomething() external payable {
        // 检查以确定0.001以太发送出去来运行函数:
        require(msg.value == 0.001 ether);
        // 如果为真,一些用来向函数调用者发送数字内容的逻辑
        transferThing(msg.sender);
      }
    }
    

    在这里,msg.value是一种可以查看向合约发送了多少以太的方法,另外ether 是一个內建单元。

    这里发生的事是,一些人会从 web3.js 调用这个函数 (从DApp的前端), 像这样 :

    // 假设 `OnlineStore` 在以太坊上指向你的合约:
    OnlineStore.buySomething().send(from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001))
    

    注意这个 value 字段, JavaScript 调用来指定发送多少(0.001)以太。如果把事务想象成一个信封,你发送到函数的参数就是信的内容。 添加一个 value 很像在信封里面放钱 —— 信件内容和钱同时发送给了接收者。

    注意: 如果一个函数没标记为payable, 而你尝试利用上面的方法发送以太,函数将拒绝你的事务。

    实战演习

    我们来在僵尸游戏里面创建一个payable 函数。

    假定在我们的游戏中,玩家可以通过支付ETH来升级他们的僵尸。ETH将存储在你拥有的合约中 —— 一个简单明了的例子,向你展示你可以通过自己的游戏赚钱。

    1.定义一个 uint ,命名为 levelUpFee, 将值设定为 0.001 ether。

    2.定义一个名为 levelUp 的函数。 它将接收一个 uint 参数 _zombieId。 函数应该修饰为 external 以及 payable

    3.这个函数首先应该 require 确保 msg.value 等于 levelUpFee

    4.然后它应该增加僵尸的 level: zombies[_zombieId].level++

    答案:

    pragma solidity ^0.4.19;
    
    import "./zombiefeeding.sol";
    
    contract ZombieHelper is ZombieFeeding {
    
      // 1. 在这里定义 levelUpFee
      uint levelUpFee = 0.001 ether;
    
      modifier aboveLevel(uint _level, uint _zombieId) {
        require(zombies[_zombieId].level >= _level);
        _;
      }
    
      // 2. 在这里插入 levelUp 函数 
      function levelUp(uint _zombieId) external payable{
        require(msg.value ==levelUpFee);
        zombies[_zombieId].level++;
        
      }
      function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) {
        require(msg.sender == zombieToOwner[_zombieId]);
        zombies[_zombieId].name = _newName;
      }
    
      function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) {
        require(msg.sender == zombieToOwner[_zombieId]);
        zombies[_zombieId].dna = _newDna;
      }
    
      function getZombiesByOwner(address _owner) external view returns(uint[]) {
        uint[] memory result = new uint[](ownerZombieCount[_owner]);
        uint counter = 0;
        for (uint i = 0; i < zombies.length; i++) {
          if (zombieToOwner[i] == _owner) {
            result[counter] = i;
            counter++;
          }
        }
        return result;
      }
    
    }
    
    

    第2章: 提现

    在上一章,我们学习了如何向合约发送以太,那么在发送之后会发生什么呢?

    在你发送以太之后,它将被存储进以合约的以太坊账户中, 并冻结在哪里 —— 除非你添加一个函数来从合约中把以太提现。

    你可以写一个函数来从合约中提现以太,类似这样:

    contract GetPaid is Ownable {
      function withdraw() external onlyOwner {
        owner.transfer(this.balance);
      }
    }
    

    注意我们使用Ownable 合约中的owneronlyOwner,假定它已经被引入了。

    你可以通过transfer 函数向一个地址发送以太, 然后 this.balance 将返回当前合约存储了多少以太。 所以如果100个用户每人向我们支付1以太, this.balance将是100以太。

    你可以通过transfer 向任何以太坊地址付钱。 比如,你可以有一个函数在msg.sender超额付款的时候给他们退钱:

    uint itemFee = 0.001 ether;
    msg.sender.transfer(msg.value - itemFee);
    

    或者在一个有卖家和卖家的合约中, 你可以把卖家的地址存储起来, 当有人买了它的东西的时候,把买家支付的钱发送给它 seller.transfer(msg.value)。

    有很多例子来展示什么让以太坊编程如此之酷 —— 你可以拥有一个不被任何人控制的去中心化市场。

    实战演习

    1.在我们的合约里创建一个 withdraw 函数,它应该几乎和上面的GetPaid一样。

    2.以太的价格在过去几年内翻了十几倍,在我们写这个教程的时候 0.01 以太相当于1美元,如果它再翻十倍 0.001 以太将是10美元,那我们的游戏就太贵了。
    所以我们应该再创建一个函数,允许我们以合约拥有者的身份来设置 levelUpFee

    a. 创建一个函数,名为 setLevelUpFee, 其接收一个参数uint _fee,是external 并使用修饰符 onlyOwner。

    b. 这个函数应该设置 levelUpFee 等于 _fee

    答案:

    pragma solidity ^0.4.19;
    
    import "./zombiefeeding.sol";
    
    contract ZombieHelper is ZombieFeeding {
    
      uint levelUpFee = 0.001 ether;
    
      modifier aboveLevel(uint _level, uint _zombieId) {
        require(zombies[_zombieId].level >= _level);
        _;
      }
    
      // 1. 在这里创建 withdraw 函数
      function withdraw()external onlyOwner{
        owner.transfer(this.balance);
      }
      // 2. 在这里创建 setLevelUpFee 函数 
      function setLevelUpFee(uint _fee) external onlyOwner {
        levelUpFee =_fee;
      }
      function levelUp(uint _zombieId) external payable {
        require(msg.value == levelUpFee);
        zombies[_zombieId].level++;
      }
    
      ...
    
    }
    
    

    第3章: 僵尸战斗

    在我们学习了可支付函数和合约余额之后,是时候为僵尸战斗添加功能了。

    遵循上一章的格式,我们新建一个攻击功能合约,并将代码放进新的文件中,引入上一个合约。

    实战演习

    再来新建一个合约吧。熟能生巧。

    如果你不记得怎么做了, 查看一下 zombiehelper.sol — 不过最好先试着做一下,检查一下你掌握的情况。

    1.在文件开头定义 Solidity 的版本^0.4.19.

    2.import 自 zombiehelper.sol .

    3.声明一个新的 contract,命名为ZombieBattle, 继承自ZombieHelper。函数体就先空着吧。

    答案:

    pragma solidity ^0.4.19;
    import "./zombiehelper.sol";
    contract ZombieBattle is ZombieHelper{
    
    }
    

    第4章: 随机数

    你太棒了!接下来我们梳理一下战斗逻辑。

    优秀的游戏都需要一些随机元素,那么我们在 Solidity 里如何生成随机数呢?

    真正的答案是你不能,或者最起码,你无法安全地做到这一点。

    我们来看看为什么

    用 keccak256 来制造随机数。

    Solidity 中最好的随机数生成器是keccak256 哈希函数.

    我们可以这样来生成一些随机数

    // 生成一个0到100的随机数:
    uint randNonce = 0;
    uint random = uint(keccak256(now, msg.sender, randNonce)) % 100;
    randNonce++;
    uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;
    

    这个方法首先拿到 now 的时间戳、 msg.sender、 以及一个自增数 nonce (一个仅会被使用一次的数,这样我们就不会对相同的输入值调用一次以上哈希函数了)。

    然后利用 keccak 把输入的值转变为一个哈希值, 再将哈希值转换为 uint, 然后利用 % 100 来取最后两位, 就生成了一个0到100之间随机数了。

    这个方法很容易被不诚实的节点攻击

    在以太坊上, 当你在和一个合约上调用函数的时候, 你会把它广播给一个节点或者在网络上的transaction 节点们。 网络上的节点将收集很多事务, 试着成为第一个解决计算密集型数学问题的人,作为“工作证明”,然后将“工作证明”(Proof of Work, PoW)和事务一起作为一个 block 发布在网络上。

    一旦一个节点解决了一个PoW, 其他节点就会停止尝试解决这个 PoW, 并验证其他节点的事务列表是有效的,然后接受这个节点转而尝试解决下一个节点。

    这就让我们的随机数函数变得可利用了

    我们假设我们有一个硬币翻转合约——正面你赢双倍钱,反面你输掉所有的钱。假如它使用上面的方法来决定是正面还是反面 (random >= 50 算正面, random < 50 算反面)。

    如果我正运行一个节点,我可以 只对我自己的节点 发布一个事务,且不分享它。 我可以运行硬币翻转方法来偷窥我的输赢 — 如果我输了,我就不把这个事务包含进我要解决的下一个区块中去。我可以一直运行这个方法,直到我赢得了硬币翻转并解决了下一个区块,然后获利。

    所以我们该如何在以太坊上安全地生成随机数呢

    因为区块链的全部内容对所有参与者来说是透明的, 这就让这个问题变得很难,它的解决方法不在本课程讨论范围,你可以阅读 这个StackOverflow上的讨论 来获得一些主意。 一个方法是利用 oracle 来访问以太坊区块链之外的随机数函数。

    当然, 因为网络上成千上万的以太坊节点都在竞争解决下一个区块,我能成功解决下一个区块的几率非常之低。 这将花费我们巨大的计算资源来开发这个获利方法 — 但是如果奖励异常地高(比如我可以在硬币翻转函数中赢得 1个亿), 那就很值得去攻击了。

    所以尽管这个方法在以太坊上不安全,在实际中,除非我们的随机函数有一大笔钱在上面,你游戏的用户一般是没有足够的资源去攻击的。

    因为在这个教程中,我们只是在编写一个简单的游戏来做演示,也没有真正的钱在里面,所以我们决定接受这个不足之处,使用这个简单的随机数生成函数。但是要谨记它是不安全的

    实战演习

    我们来实现一个随机数生成函数,好来计算战斗的结果。虽然这个函数一点儿也不安全。

    1.给我们合约一个名为 randNonce 的 uint,将其值设置为 0。

    2.建立一个函数,命名为 randMod (random-modulus)。它将作为internal 函数,传入一个名为_modulus的 uint,并 returns 一个 uint。

    3.这个函数首先将为randNonce加一, (使用 randNonce++ 语句)。

    4.最后,它应该 (在一行代码中) 计算 now, msg.sender, 以及 randNonce 的 keccak256 哈希值并转换为 uint—— 最后 return % _modulus 的值。 (天! 听起来太拗口了。如果你有点理解不过来,看一下我们上面计算随机数的例子,它们的逻辑非常相似)

    答案:

    pragma solidity ^0.4.19;
    
    import "./zombiehelper.sol";
    
    contract ZombieBattle is ZombieHelper {
      // 在这里开始
      uint randNonce =0;
      function randMod (uint _modulus) internal returns (uint){
        randNonce++;
        return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
      }
    }
    
    

    第5章: 僵尸对战

    我们的合约已经有了一些随机性的来源,可以用进我们的僵尸战斗中去计算结果。

    我们的僵尸战斗看起来将是这个流程:

    • 你选择一个自己的僵尸,然后选择一个对手的僵尸去攻击。
    • 如果你是攻击方,你将有70%的几率获胜,防守方将有30%的几率获胜。
    • 所有的僵尸(攻守双方)都将有一个 winCount 和一个 lossCount,这两个值都将根据战斗结果增长。
    • 若攻击方获胜,这个僵尸将升级并产生一个新僵尸。
    • 如果攻击方失败,除了失败次数将加一外,什么都不会发生。
    • 无论输赢,当前僵尸的冷却时间都将被激活。

    这有一大堆的逻辑需要处理,我们将把这些步骤分解到接下来的课程中去。

    实战演习

    1.给我们合约一个 uint 类型的变量,命名为 attackVictoryProbability, 将其值设定为 70。

    2.创建一个名为 attack的函数。它将传入两个参数: _zombieId(uint 类型) 以及 _targetId (也是 uint)。它将是一个 external 函数。

    函数体先留空吧。
    答案:

    pragma solidity ^0.4.19;
    
    import "./zombiehelper.sol";
    
    contract ZombieBattle is ZombieHelper {
      uint randNonce = 0;
      // 在这里创建 attackVictoryProbability
      uint attackVictoryProbability =70;
      function randMod(uint _modulus) internal returns(uint) {
        randNonce++;
        return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
      }
    
      // 在这里创建新函数
      function attack (uint _zombieId,uint _targetId) external{
        
      }
    }
    

    第6章: 重构通用逻辑

    不管谁调用我们的 attack 函数 —— 我们想确保用户的确拥有他们用来攻击的僵尸。如果你能用其他人的僵尸来攻击将是一个很大的安全问题。

    你能想一下我们如何添加一个检查步骤来看看调用这个函数的人就是他们传入的 _zombieId的拥有者么?

    想一想,看看你能不能自己找到一些答案。

    花点时间…… 参考我们前面课程的代码来获得灵感。

    答案在下面,在你有一些想法之前不要继续阅读。

    答案
    我们在前面的课程里面已经做过很多次这样的检查了。 在 changeName(), changeDna(), 和 feedAndMultiply()里,我们做过这样的检查:

    require(msg.sender == zombieToOwner[_zombieId]);
    这和我们 attack 函数将要用到的检查逻辑是相同的。 正因我们要多次调用这个检查逻辑,让我们把它移到它自己的 modifier 中来清理代码并避免重复编码。

    实战演习

    我们回到了zombiefeeding.sol, 因为这是我们第一次调用检查逻辑的地方。让我们把它重构进它自己的 modifier

    1.创建一个 modifier, 命名为 ownerOf。它将传入一个参数, _zombieId (一个 uint)。

    2.它的函数体应该 require msg.sender`` 等于 zombieToOwner[_zombieId], 然后继续这个函数剩下的内容。 如果你忘记了修饰符的写法,可以参考 zombiehelper.sol。

    3.将这个函数的 feedAndMultiply 定义修改为其使用修饰符 ownerOf。

    现在我们使用 modifier了,你可以删除这行了: require(msg.sender == zombieToOwner[_zombieId]);

    答案:

    pragma solidity ^0.4.19;
    
    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;
    
      // 1. 在这里创建 modifier
      modifier ownerOf(uint _zombieId) {
        require(msg.sender == zombieToOwner[_zombieId]);
        _;
      }
      function setKittyContractAddress(address _address) external onlyOwner {
        kittyContract = KittyInterface(_address);
      }
    
      function _triggerCooldown(Zombie storage _zombie) internal {
        _zombie.readyTime = uint32(now + cooldownTime);
      }
    
      function _isReady(Zombie storage _zombie) internal view returns (bool) {
          return (_zombie.readyTime <= now);
      }
    
      // 2. 在函数定义时增加 modifier :
      function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal ownerOf(_zombieId) {
        // 3. 移除这一行
        //require(msg.sender == zombieToOwner[_zombieId]);
        Zombie storage myZombie = zombies[_zombieId];
        require(_isReady(myZombie));
        _targetDna = _targetDna % dnaModulus;
        uint newDna = (myZombie.dna + _targetDna) / 2;
        if (keccak256(_species) == keccak256("kitty")) {
          newDna = newDna - newDna % 100 + 99;
        }
        _createZombie("NoName", newDna);
        _triggerCooldown(myZombie);
      }
    
      function feedOnKitty(uint _zombieId, uint _kittyId) public {
        uint kittyDna;
        (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
        feedAndMultiply(_zombieId, kittyDna, "kitty");
      }
    }
    
    

    第7章: 更多重构

    在 zombiehelper.sol里有几处地方,需要我们实现我们新的 modifier—— ownerOf。

    实战演习

    1.修改 changeName() 使其使用 ownerOf

    2.修改 changeDna() 使其使用 ownerOf

    pragma solidity ^0.4.19;
    
    import "./zombiefeeding.sol";
    
    contract ZombieHelper is ZombieFeeding {
    
      uint levelUpFee = 0.001 ether;
    
      modifier aboveLevel(uint _level, uint _zombieId) {
        require(zombies[_zombieId].level >= _level);
        _;
      }
    
      function withdraw() external onlyOwner {
        owner.transfer(this.balance);
      }
    
      function setLevelUpFee(uint _fee) external onlyOwner {
        levelUpFee = _fee;
      }
    
      function levelUp(uint _zombieId) external payable {
        require(msg.value == levelUpFee);
        zombies[_zombieId].level++;
      }
    
      // 1. 使用 `ownerOf` 修改这个函数:
     function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) ownerOf(_zombieId) {
        zombies[_zombieId].name = _newName;
      }
    
      // 2. 对这个函数做同样的事:
      function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) ownerOf(_zombieId) {
        zombies[_zombieId].dna = _newDna;
      }
    
      function getZombiesByOwner(address _owner) external view returns(uint[]) {
        uint[] memory result = new uint[](ownerZombieCount[_owner]);
        uint counter = 0;
        for (uint i = 0; i < zombies.length; i++) {
          if (zombieToOwner[i] == _owner) {
            result[counter] = i;
            counter++;
          }
        }
        return result;
      }
    
    }
    

    第8章: 回到攻击!

    重构完成了,回到 zombieattack.sol。

    继续来完善我们的 attack 函数, 现在我们有了 ownerOf 修饰符来用了。

    实战演习

    1.将 ownerOf 修饰符添加到 attack 来确保调用者拥有_zombieId.

    2.我们的函数所需要做的第一件事就是获得一个双方僵尸的 storage 指针, 这样我们才能很方便和它们交互:

    a. 定义一个 `Zombie storage 命名为 myZombie,使其值等于 zombies[_zombieId]。

    b. 定义一个 Zombie storage命名为 enemyZombie, 使其值等于zombies[_targetId]`。

    3.我们将用一个0到100的随机数来确定我们的战斗结果。 定义一个 uint,命名为 rand, 设定其值等于 randMod 函数的返回值,此函数传入 100作为参数。

    答案:

    pragma solidity ^0.4.19;
    import "./zombiehelper.sol";
    contract ZombieBattle is ZombieHelper {
      uint randNonce = 0;
      uint attackVictoryProbability = 70;
    
      function randMod(uint _modulus) internal returns(uint) {
        randNonce++;
        return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
      }
    
      function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) {
        Zombie storage myZombie = zombies[_zombieId];
        Zombie storage enemyZombie = zombies[_targetId];
        uint rand = randMod(100);
      }
    }
    
    

    第9章: 僵尸的输赢

    对我们的僵尸游戏来说,我们将要追踪我们的僵尸输赢了多少场。有了这个我们可以在游戏里维护一个 “僵尸排行榜”。

    有多种方法在我们的DApp里面保存一个数值 — 作为一个单独的映射,作为一个“排行榜”结构体,或者保存在Zombie 结构体内。

    每个方法都有其优缺点,取决于我们打算如何和这些数据打交道。在这个教程中,简单起见我们将这个状态保存在 Zombie 结构体中,将其命名为 winCountlossCount

    我们跳回 zombiefactory.sol, 将这些属性添加进 Zombie 结构体.

    实战演习

    1.修改 Zombie 结构体,添加两个属性:

    a. winCount, 一个 uint16

    b. lossCount, 也是一个 uint16

    注意: 记住, 因为我们能在结构体中包装uint, 我们打算用适合我们的最小的 uint。 一个 uint8 太小了, 因为 2^8 = 256 —— 如果我们的僵尸每天都作战,不到一年就溢出了。但是 2^16 = 65536 (uint16)—— 除非一个僵尸连续179年每天作战,否则我们就是安全的。

    2.现在我们的 Zombie 结构体有了新的属性, 我们需要修改 _createZombie() 中的函数定义。

    修改僵尸生成定义,让每个新僵尸都有 0 赢和 0 输。

    答案:

    pragma solidity ^0.4.19;
    import "./ownable.sol";
    contract ZombieFactory is Ownable {
    
        event NewZombie(uint zombieId, string name, uint dna);
    
        uint dnaDigits = 16;
        uint dnaModulus = 10 ** dnaDigits;
        uint cooldownTime = 1 days;
    
        struct Zombie {
          string name;
          uint dna;
          uint32 level;
          uint32 readyTime;
    	  //
          uint16 winCount;
          uint16 lossCount;
        }
    
        Zombie[] public zombies;
    
        mapping (uint => address) public zombieToOwner;
        mapping (address => uint) ownerZombieCount;
    
        function _createZombie(string _name, uint _dna) internal {
            uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime), 0, 0)) - 1;
            zombieToOwner[id] = msg.sender;
            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);
            uint randDna = _generateRandomDna(_name);
            randDna = randDna - randDna % 100;
            _createZombie(_name, randDna);
        }
    
    }
    

    第10章: 僵尸胜利了 ?

    有了 winCountlossCount,我们可以根据僵尸哪个僵尸赢了战斗来更新它们了。

    在第六章我们计算出来一个0到100的随机数。现在让我们用那个数来决定那谁赢了战斗,并以此更新我们的状态。

    实战演习

    1.创建一个 if 语句来检查rand 是不是 小于或者等于 attackVictoryProbability

    2.如果以上条件为 true, 我们的僵尸就赢了!所以:

    a. 增加 myZombiewinCount

    b. 增加 myZombielevel。 (升级了啦!!!)

    c. 增加 enemyZombielossCount. (输家!!! ? ? ?)

    d. 运行feedAndMultiply 函数。 在 zombiefeeding.sol 里查看调用它的语句。 对于第三个参数 (_species),传入字符串 “zombie”. (现在它实际上什么都不做,不过在稍后, 如果我们愿意,可以添加额外的方法,用来制造僵尸变的僵尸)。

    答案:

      function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) {
        Zombie storage myZombie = zombies[_zombieId];
        Zombie storage enemyZombie = zombies[_targetId];
        uint rand = randMod(100);
        // 在这里开始
         if (rand <= attackVictoryProbability) {
          myZombie.winCount++;
          myZombie.level++;
          enemyZombie.lossCount++;
          feedAndMultiply(_zombieId, enemyZombie.dna, "zombie");
        }
      }
    

    第11章: 僵尸失败 ?

    我们已经编写了你的僵尸赢了之后会发生什么, 该看看 输了 的时候要怎么做了。

    在我们的游戏中,僵尸输了后并不会降级 —— 只是简单地给lossCount 加一,并触发冷却,等待一天后才能再次参战。

    要实现这个逻辑,我们需要一个 else 语句。

    else 语句和 JavaScript 以及很多其他语言的 else 语句一样。

    if (zombieCoins[msg.sender] > 100000000) {
      // 你好有钱!!!
    } else {
      // 我们需要更多的僵尸币...
    }
    

    实战演习

    1.添加一个 else 语句。 若我们的僵尸输了:

    a. 增加 myZombielossCount

    b. 增加 enemyZombiewinCount

    2.在 else 最后, 对 myZombie 运行_triggerCooldown 方法。这让每个僵尸每天只能参战一次。

    答案:

    pragma solidity ^0.4.19;
    
    import "./zombiehelper.sol";
    
    contract ZombieBattle is ZombieHelper {
      uint randNonce = 0;
      uint attackVictoryProbability = 70;
    
      function randMod(uint _modulus) internal returns(uint) {
        randNonce++;
        return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
      }
    
      function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) {
        Zombie storage myZombie = zombies[_zombieId];
        Zombie storage enemyZombie = zombies[_targetId];
        uint rand = randMod(100);
        if (rand <= attackVictoryProbability) {
          myZombie.winCount++;
          myZombie.level++;
          enemyZombie.lossCount++;
          feedAndMultiply(_zombieId, enemyZombie.dna, "zombie");
        } // 在这里开始
        else{
          myZombie.lossCount++;
          enemyZombie.winCount++;
           _triggerCooldown(myZombie);
        }
      }
    }
    
    

    第12章: 放在一起

    恭喜你啊,又完成了第四课。

    认领你的战利品
    在赢了战斗之后:

    1.你的僵尸将会升级

    2.你僵尸的 winCount 将会增加

    3.你将为你的僵尸大军获得一个新的僵尸

    继续测试战斗,玩够了以后点击下一章来完成本课。

    展开全文
  • 看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。 如果你想了解更多有关于机器学习、深度学习、区块链、计算机视觉等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们...
  • 看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。 如果你想了解更多有关于机器学习、深度学习、区块链、计算机视觉等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们...
  • solidity教程(一)搭建僵尸工厂

    千次阅读 2018-10-19 20:36:19
    title: solidity教程(一)基础 tags: solidity,区块链 第1章: 课程概述 第一课你将创造一个&amp;quot;僵尸工厂&amp;quot;, 用它建立一支僵尸部队。 我们的工厂会把我们部队中所有的僵尸保存到数据库中 ...

    title: solidity教程(一)基础
    tags: solidity,eth

    声明:本系列教程是整理cryptozombies

    https://cryptozombies.io/zh而来。


    人类,欢迎你
    你认为你可以当一个合格的 CryptoZombie, 嗯?

    这个教程会教你如何搭建一个以太网的游戏。

    此课程为 Solidity 初学者设计,需要你对其他的程序语言有所了解(如 JavaScript)。

    少年,准备开始了吗?

    第1章: 课程概述

    第一课你将创造一个"僵尸工厂", 用它建立一支僵尸部队。

    • 我们的工厂会把我们部队中所有的僵尸保存到数据库中
    • 工厂会有一个函数能产生新的僵尸
    • 每个僵尸会有一个随机的独一无二的面孔
      在后面的课程里,我们会增加功能。比如,让僵尸能攻击人类或其它僵尸! 但是在实现这些好玩的功能之前,我们先要实现创建僵尸这样的基本功能。

    僵尸DNA如何运作
    僵尸的面孔取决于它的DNA。它的DNA很简单,由一个16位的整数组成:

    8356281049284737

    如同真正的DNA, 这个数字的不同部分会对应不同的特点。 前2位代表头型,紧接着的2位代表眼睛,等等。

    注: 本教程我们尽量简化。我们的僵尸只有7种头型(虽然2位数字允许100种可能性)。以后我们会加入更多的头型, 如果我们想让僵尸有更多造型。

    例如,前两位数字是 83, 计算僵尸的头型,我们做83 % 7 + 1 = 7 运算, 此僵尸将被赋予第七类头型。

    第2章: 合约

    从最基本的开始入手:

    Solidity 的代码都包裹在合约里面. 一份合约就是以太应币应用的基本模块, 所有的变量和函数都属于一份合约, 它是你所有应用的起点.

    一份名为 HelloWorld 的空合约如下:

    contract HelloWorld {
    
    }
    
    

    版本指令

    所有的 Solidity 源码都必须冠以 “version pragma” — 标明 Solidity 编译器的版本. 以避免将来新的编译器可能破坏你的代码。

    例如: pragma solidity ^0.4.19; (当前 Solidity 的最新版本是 0.4.19).

    综上所述, 下面就是一个最基本的合约 — 每次建立一个新的项目时的第一段代码:

    pragma solidity ^0.4.19;
    
    contract HelloWorld {
    
    }
    
    

    实战演习

    为了建立我们的僵尸部队, 让我们先建立一个基础合约,称为 ZombieFactory

    1. 在右边的输入框里输入 0.4.19,我们的合约基于这个版本的编译器。

    2. 建立一个空合约 ZombieFactory

    一切完毕,点击下面 “答案” . 如果没效果,点击 “提示”。

    答案:

    pragma solidity ^0.4.19;
    
    contract ZombieFactory {
    }
    

    第3章: 状态变量

    我们已经实现了一个空合约, 下面学习 Solidity 中如何使用变量。
    状态变量是被永久地保存在合约中。也就是说它们被写入以太币区块链中. 想象成写入一个数据库。

    Solidity的变量类型:

    • 值类型 :这类变量在赋值或传参时,总是进行值拷贝。
      • 布尔类型(Booleans)
      • 整型(Integers)
      • 定长浮点型(Fixed Point Numbers)
      • 定长字节数组(Fixed-size byte arrays)
      • 有理数和整型常量(Rational and Integer Literals)
      • 字符串常量(String literals)
      • 十六进制常量(Hexadecimal literals)
      • 枚举(Enums)
      • 函数类型(Function Types)
      • 地址类型(Address)
      • 地址常量(Address Literals)
      • 函数类型及地址类型(Address)
    • 引用类型 :这里变量在赋值或传参时,传递的是地址。
      • 数组(Arrays)
      • 结构体(Structs)

    虽然列举了很多,但是本文涉及计部分简单类型。所以有不懂的小伙伴们不要害怕,以后会慢慢深入讲解的。

    例子:

    contract Example {
      // 这个无符号整数将会永久的被保存在区块链中
      string name ="Jay";
      uint  age = 25;
     
    }
    

    在上面的例子中,定义name为string类型,并赋值为“Jay”和age 为 uint 类型,并赋值25。

    字符串类型:string
    string字符串用于保存任意长度的 UTF-8 编码数据。 如: string greeting = “Hello world!”。
    无符号整数: uint
    uint 无符号数据类型, 指其值不能是负数,对于有符号的整数存在名为 int 的数据类型。

    注: Solidity中, uint 实际上是 uint256代名词, 一个256位的无符号整数。你也可以定义位数少的uints — uint8, uint16, uint32 等,但一般来讲我们都使用简单的 uint。如果使用到其他的我后面会讲。
    保存字符我们也可以选择使用bytes类型,这个类型我们以后会讲。

    实战演习

    我们的僵尸DNA将由一个十六位数字组成。

    • 定义 dnaDigits 为 uint 数据类型, 并赋值 16。

    答案:

    pragma solidity ^0.4.19;
    
    contract ZombieFactory {
    	//插入代码
       uint dnaDigits = 16;
    }
    
    

    注意: 变量直接声明在合约中,是不可直接变更的。

    第4章: 数学运算

    在 Solidity 中,数学运算很直观明了,与其它程序设计语言相同:

    • 加法: x + y
    • 减法: x - y,
    • 乘法: x * y
    • 除法: x / y
    • 取模 / 求余: x % y (例如, 13 % 5 余 3, 因为13除以5,余3)
    • 次方:x ** y (x 的 y次方 // 例如: 5 ** 2 = 25)

    uint x = 5 ** 2; // equal to 5^2 = 25

    实战演习

    为了保证我们的僵尸的DNA只含有16个字符,我们先造一个uint数据,让它等于10^16。这样一来以后我们可以用模运算符 % 把一个整数变成16位。

    • 建立一个uint类型的变量,名字叫dnaModulus, 令其等于 10 的 dnaDigits 次方.

    答案:
    pragma solidity ^0.4.19;

    contract ZombieFactory {

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

    }

    第5章: 结构体

    有时你需要更复杂的数据类型,Solidity 提供了 结构体:

    struct Person {
      uint age;
      string name;
    }
    

    结构体允许你生成一个更复杂的数据类型,它有多个属性。

    注:我们刚刚引进了一个新类型, string。 字符串用于保存任意长度的 UTF-8 编码数据。 如: string greeting = "Hello world!"

    实战演习

    在我们的程序中,我们将创建一些僵尸!每个僵尸将拥有多个属性,所以这是一个展示结构体的完美例子。

    1. 建立一个 struct 命名为 Zombie.

    2. 我们的 Zombie 结构体有两个属性: name (类型为 string), 和 dna (类型为 uint)。

    pragma solidity ^0.4.19;
    
    contract ZombieFactory {
    
        uint dnaDigits = 16;
        uint dnaModulus = 10 ** dnaDigits;
    	//插入结构体
        struct Zombie {
            string name;
            uint dna;
        }
    
    }
    
    

    第6章: 数组

    如果你想建立一个集合,可以用 数组 这样的数据类型. Solidity 支持两种数组: 静态 数组和 动态 数组:

    // 固定长度为2的静态数组:
    uint[2] fixedArray;
    // 固定长度为5的string类型的静态数组:
    string[5] stringArray;
    // 动态数组,长度不固定,可以动态添加元素:
    uint[] dynamicArray;
    

    你也可以建立一个 结构体 类型的数组 例如,上一章提到的 Person:

    Person[] people; // dynamic Array, we can keep adding to it
    

    记住:状态变量被永久保存在区块链中。所以在你的合约中创建动态数组来保存成结构的数据是非常有意义的。

    公共数组

    你可以定义 public 数组, Solidity 会自动创建 getter 方法. 语法如下:

            Person [] public people;
    

    其它的合约可以从这个数组读取数据(但不能写入数据),所以这在合约中是一个有用的保存公共数据的模式。

    实战演习

    为了把一个僵尸部队保存在我们的APP里,并且能够让其它APP看到这些僵尸,我们需要一个公共数组。

    1. 创建一个数据类型为 Zombie 的结构体数组,用 public 修饰,命名为: zombies.

    答案:
    注意: 为了文章的简洁性,我用“…” 代表前面出现过的代码。

    pragma solidity ^0.4.19;
    
    contract ZombieFactory {
    
        ...
        struct Zombie {
            string name;
            uint dna;
        }
    	//这里开始
        Zombie[] public zombies;
    }
    

    第7章: 定义函数

    我们每天需要吃饭、睡觉等等一系列活动,而这一系列的活动在程序中叫做方法,或者函数,下面我们定一个吃汉堡的方法(函数)。
    在 Solidity 中函数定义的句法如下:
    function eatHamburgers(string _name, uint _amount) {

    }
    这是一个名为 eatHamburgers 的函数,它接受两个参数:一个 string类型的 和 一个 uint类型的。_name 代表着汉堡的品种,_amount代表我们吃的数量。现在函数内部还是空的。

    注:: 习惯上函数里的变量都是以“_”开头 (但不是硬性规定) 以区别全局变量。我们整个教程都会沿用这个习惯。

    我们的函数是用如下:
    eatHamburgers(“vitalik”, 100);

    实战演习

    在我们的应用里,我们要能创建一些僵尸,让我们写一个函数做这件事吧!

    建立一个函数 createZombie。 它有两个参数: _name (类型为string), 和 _dna (类型为uint)。
    暂时让函数空着——我们在后面会增加内容。

    pragma solidity ^0.4.19;
    
    contract ZombieFactory {
    
         ...
        Zombie[] public zombies;
        // 这里开始
        function createZombie(string _name, uint _dna) {
    
        }
    }
    

    第8章: 使用结构体和数组

    创建新的结构体

    还记得上个例子中的 Person 结构吗?

    struct Person {
      uint age;
      string name;
    }
    
    Person[] public people;
    

    现在我们学习创建新的 Person 结构,然后把它加入到名为 people 的数组中.

    // 创建一个新的Person:
    Person satoshi = Person(172, "Satoshi");
    
    // 将新创建的satoshi添加进people数组:
    people.push(satoshi);
    

    你也可以两步并一步,用一行代码更简洁:

    people.push(Person(16, "Vitalik"));

    注: array.push() 在数组的 尾部 加入新元素 ,所以元素在数组中的顺序就是我们添加的顺序, 如:

    uint[] numbers;
    numbers.push(5);
    numbers.push(10);
    numbers.push(15);
    // numbers is now equal to [5, 10, 15]
    

    实战演习

    让我们创建名为createZombie的函数来做点儿什么吧。

    1. 在函数体里新创建一个 Zombie, 然后把它加入 zombies 数组中。 新创建的僵尸的 namedna,来自于函数的参数。
    2. 让我们用一行代码简洁地完成它。

    我要看答案! 交卷

    答案:

    pragma solidity ^0.4.19;
    
    contract ZombieFactory {
    	...
        Zombie[] public zombies;
        function createZombie(string _name, uint _dna) {
            // 这里开始
            zombies.push(Zombie(_name, _dna));
        }
    }
    

    第9章: 私有 / 公共函数

    Solidity 定义的函数的属性默认为公共(public)。 这就意味着任何一方 (或其它合约) 都可以调用你合约里的函数。
    显然,不是什么时候都需要这样,而且这样的合约易于受到攻击。 所以将自己的函数定义为私有(private)是一个好的编程习惯,只有当你需要外部世界调用它时才将它设置为公共。

    如何定义一个私有的函数呢?

    uint[] numbers;
    
    function _addToArray(uint _number) private {
      numbers.push(_number);
    }
    

    这意味着只有我们合约中的其它函数才能够调用这个函数,给 numbers 数组添加新成员。

    可以看到,在函数名字后面使用关键字 private 即可。和函数的参数类似,私有函数的名字用(_)起始。

    实战演习

    我们合约的函数 createZombie 的默认属性是公共的,这意味着任何一方都可以调用它去创建一个僵尸。 咱们来把它变成私有吧!

    • 变 createZombie 为私有函数,不要忘记遵守命名的规矩哦!

    答案:

    pragma solidity ^0.4.19;
    
    contract ZombieFactory {
    	...
        function _createZombie(string _name, uint _dna) private {
            zombies.push(Zombie(_name, _dna));
        }
    
    }
    

    第10章: 函数的更多属性

    本章中我们将学习函数的返回值和修饰符。

    返回值 returns
    要想函数返回一个数值,按如下定义:

    string greeting = "What's up dog";
    
    function sayHello() public returns (string) {
     	 return greeting;
    }
    

    本例 执行sayHello函数 将返回"What’s up dog" 。
    Solidity中,函数的定义,可包含返回值的数据类型(如本例中 string)。

    函数的修饰符 view、pure
    上面的函数实际上没有改变 Solidity 里的状态,即,它没有改变任何值或者写任何东西。

    这种情况下我们可以把函数定义为 view, 意味着它只能读取数据不能更改数据:

    function sayHello() public view returns (string) {
    Solidity 还支持 pure 函数, 表明这个函数甚至都不访问应用里的数据,例如:

    function _multiply(uint a, uint b) private pure returns (uint) {
      return a * b;
    }
    

    这个函数甚至都不读取应用里的状态 — 它的返回值完全取决于它的输入参数,在这种情况下我们把函数定义为 pure.

    注:可能很难记住何时把函数标记为 pure/view。 幸运的是, Solidity 编辑器会给出提示,提醒你使用这些修饰符。

    实战演习

    我们想建立一个帮助函数,它根据一个字符串随机生成一个DNA数据。

    • 创建一个 private 函数,命名为 _generateRandomDna。它只接收一个输入变量 _str (类型 string), 返回一个 uint 类型的数值。

    • 此函数只读取我们合约中的一些变量,所以标记为view。

    • 函数内部暂时留空,以后我们再添加代码。

    答案:

    pragma solidity ^0.4.19;
    
    contract ZombieFactory {
     
    	...
    
        function _createZombie(string _name, uint _dna) private {
            zombies.push(Zombie(_name, _dna));
        }
    
        // 这里开始
        function _generateRandomDna(string _str) private view returns (uint) 	{
    
       }
    }
    

    第11章: Keccak256 和 类型转换

    如何让 _generateRandomDna 函数返回一个随机的 uint?

    Ethereum 内部有一个散列函数keccak256,它用了SHA3版本。一个散列函数基本上就是把一个字符串转换为一个256位的16进制数字。字符串的一个微小变化会引起散列数据极大变化。

    这在 Ethereum 中有很多应用,但是现在我们只是用它造一个伪随机数。

    例子:

    //6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
    keccak256(“aaaab”);
    //b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
    keccak256(“aaaac”);
    显而易见,输入字符串只改变了一个字母,输出就已经天壤之别了。

    注: 在区块链中安全地产生一个随机数是一个很难的问题, 本例的方法不安全,但是在我们的Zombie DNA算法里不是那么重要,已经很好地满足我们的需要了。

    类型转换
    有时你需要变换数据类型。例如:

    uint8 a = 5;
    uint b = 6;
    // 将会抛出错误,因为 a * b 返回 uint, 而不是 uint8:
    uint8 c = a * b;
    // 我们需要将 b 转换为 uint8:
    uint8 c = a * uint8(b);
    

    上面, a * b 返回类型是 uint, 但是当我们尝试用 uint8 类型接收时, 就会造成潜在的错误。如果把它的数据类型转换为 uint8, 就可以了,编译器也不会出错。

    实战演习

    给 _generateRandomDna 函数添加代码! 它应该完成如下功能:

    • 第一行代码取 _str 的 keccak256 散列值生成一个伪随机十六进制数,类型转换为 uint, 最后保存在类型为 uint 名为 rand 的变量中。

    • 我们只想让我们的DNA的长度为16位 (还记得 dnaModulus?)。所以第二行代码应该 return 上面计算的数值对 dnaModulus 求余数(%)。
      答案:

    pragma solidity ^0.4.19;
    
    contract ZombieFactory {
    
        ...
    
        function _generateRandomDna(string _str) private view returns (uint) 	{
            uint rand = uint(keccak256(_str));
            return rand % dnaModulus;
        }
    }
    

    第12章: 放在一起

    我们就快完成我们的随机僵尸制造器了,来写一个公共的函数把所有的部件连接起来。

    写一个公共函数,它有一个参数,用来接收僵尸的名字,之后用它生成僵尸的DNA。

    实战演习

    • 创建一个 public 函数,命名为createRandomZombie. 它将被传入一个变量 _name (数据类型是 string)。 (注: 定义公共函数 public 和定义一个私有 private 函数的做法一样)。

    • 函数的第一行应该调用 _generateRandomDna 函数,传入 _name 参数, 结果保存在一个类型为 uint 的变量里,命名为 randDna。

    • 第二行调用 _createZombie 函数, 传入参数: _name 和 randDna。

    • 整个函数应该是4行代码 (包括函数的结束符号 } )。

    pragma solidity ^0.4.19;
    
    contract ZombieFactory {
    
    	...
    	
        function _generateRandomDna(string _str) private view returns (uint) 	{
            uint rand = uint(keccak256(_str));
            return rand % dnaModulus;
        }
    
        // 事件
            function createRandomZombie(string _name) public {
            uint randDna = _generateRandomDna(_name);
            _createZombie(_name, randDna);
        }
    
    }
    

    第13章: 事件

    我们的合约几乎就要完成了!让我们加上一个事件.

    事件 :是合约和区块链通讯的一种机制。你的前端应用“监听”某些事件,并做出反应。

    例子:

    // 这里建立事件

    event IntegersAdded(uint x, uint y, uint result);
    
    function add(uint _x, uint _y) public {
      uint result = _x + _y;
      //触发事件,通知app
      IntegersAdded(_x, _y, result);
      return result;
    }
    你的 app 前端可以监听这个事件。JavaScript 实现如下:
    
    YourContract.IntegersAdded(function(error, result) { 
      // 干些事
    }
    

    实战演习

    我们想每当一个僵尸创造出来时,我们的前端都能监听到这个事件,并将它显示出来。

    1. 定义一个 事件 叫做 NewZombie。 它有3个参数: zombieId (uint), name (string), 和 dna (uint)。

    2.修改 _createZombie 函数使得当新僵尸造出来并加入zombies数组后,生成事件NewZombie。

    3.需要定义僵尸id。 array.push() 返回数组的长度类型是uint - 因为数组的第一个元素的索引是 0, array.push() - 1 将是我们加入的僵尸的索引。 zombies.push() - 1 就是 id,数据类型是 uint。在下一行中你可以把它用到 NewZombie 事件中。
    答案:

    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;
    
        function _createZombie(string _name, uint _dna) private {
            //
            uint id = zombies.push(Zombie(_name, _dna)) - 1;
            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 {
            uint randDna = _generateRandomDna(_name);
            _createZombie(_name, randDna);
        }
    
    }
    

    第14章: Web3.js

    我们的 Solidity 合约完工了! 现在我们要写一段 JavaScript 前端代码来调用这个合约。

    以太坊有一个 JavaScript 库,名为Web3.js。

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

    如果下面的代码你不能全都理解,不用担心。

    // 下面是调用合约的方式:
    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
    }
    

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

    展开全文
  • 加密僵尸 团结教程
  • 看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。 如果你想了解更多有关于机器学习、深度学习、区块链、计算机视觉等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们...
  • Solidity教程:初学Solidity

    千次阅读 2018-09-28 14:07:13
    这篇关于Solidity教程的博客展示了很多Solidity特性。本教程假定你对以太坊虚拟机和编程有一定的了解。 以太坊,“世界计算机”提供了一个非常强大的全球共享基础设施,使用名为Solidity的编程语言构建去中心化应用...

    这篇关于Solidity教程的博客展示了很多Solidity特性。本教程假定你对以太坊虚拟机和编程有一定的了解。

    以太坊,“世界计算机”提供了一个非常强大的全球共享基础设施,使用名为Solidity的编程语言构建去中心化应用程序。

    让我们开始我们的Solidity教程,介绍Solidity。

    什么是Solidity?

    以太坊Solidity是一种面向智能合约的高级语言,其语法与JavaScript类似。solidity是用于生成在EVM上执行的机器级代码的工具。solidity编译器获取高级代码并将其分解为更简单的指令。Solidity代码封装在Contracts中。

    以太坊合约中的solidity

    合约是以太坊去中心化应用程序的基本构建模块。所有变量和函数都是合约的一部分,这是所有项目的起点。一个名为MyFirst的空合约看起来像这样:

    version pragma ^0.4.19;
    contract MyFirst{
    }
    

    盯紧你的屏幕因为接下来在我们的Solidity教程中我们将开始编码…

    Solidity文件的布局

    源文件可以包含任意数量的合约定义,包括指令和pragma指令。

    Version Pragma

    Version Pragma是定义代码使用的Solidity编译器版本的声明。

    version pragma ^0.4.00;
    

    注意:上面显示的源文件不会使用早于版本0.4.0的编译器进行编译,也不能在从版本0.5.0开始的编译器上运行。

    导入其他源文件

    Ethereum Solidity支持与JavaScript中可用的导入语句非常相似的导入语句,尽管Solidity不知道default export的概念。

    在全局级别,可以使用以下形式的import语句:

    import "filename";
    

    上述语句将所有全局符号从filename导入当前全局范围。

    import * as symbolName from "filename";
    

    注释

    就像任何其他语言一样,Solidity可以使用单行和多行注释。

    // This is a single-line comment.
    /*
    This is a
    multi-line comment
    */
    

    现在,在我们进一步深入了解Solidity教程之前,应该知道以太坊有三个可以存储项目的区域。

    • 存储Storage:所有合约状态变量所在的位置。每个合约都有自己的存储,并且在函数调用之间是持久的。
    • 内存Memory:保存临时值并在(外部)函数调用之间擦除,并且使用起来更便宜。
    • 堆栈Stack:保存小的局部变量并且几乎可以免费使用,但只能保存有限数量的值。

    对于几乎所有类型,都无法指定它们应存储的位置,因为它们在每次使用时都会被复制。

    好了,既然你已经知道以太坊Solidity中的存储位置,那么让我告诉你一般的值类型。

    solidity中的值类型

    以下类型也称为值类型,因为这些类型的变量将始终按值传递。

    布尔

    关键词:bool

    值是常数,即true或false。

    整型

    关键字:int/uint(uint8到uint256,步长为8(无符号,最多为256位),int8为int256)

    各种大小的有符号和无符号整数。

    例:

    contract MySample{
    uint UnsignedInt =50;
    }
    

    在上面的语句中,我们创建了一个名为InsignedInt的uint并将其设置为50。

    地址

    关键字:address

    保存一个20字节的值(以太坊地址的大小)。地址类型也有members,并作为所有合约的基础。

    地址成员:balance与transfer

    可以使用属性balance查询地址的余额,并使用transfer函数将以太网发送到地址。

    address x = 0x123;
    address myAddress = this;
    if  (x.balance < 10 && myAddress.balance > = 10)
    x.transfer(10);
    

    字符串

    String:字符串文字用双引号或单引号如“foo”或’bar’编写。

    用于任意长度的UTF数据。

    string language ="Solidity";
    

    这些值类型可以在包含运算符的表达式中相互交互。接下来,在我们的Solidity教程中,让我告诉你各种运算符。

    运算符

    solidity的运算符与JavaScript相同。Solidity有四种类型的运算符:

    算术运算符

    Solidity具有非常简单的数学运算。以下与大多数编程语言类似:

    • 增加:x + y
    • 减法:x - y
    • 乘法:x * y
    • 除法:x / y
    • 取整/求余:x%y

    Solidity还提供了使用指数运算符的选项,具体如下:

    uint x = 10 **  3; // equal to 10^3 = 1000
    

    增量运算符

    增量运算符的稳定性:a++,a- ,++a,-a,a+=1,a=a+1

    适用于其他编程语言的规则也是类似的。

    按位运算符

    以下是运算符:(按位OR)’|’,(按位异或),(按位求反)’~’,(按位右移)’>>’,(按位左移)’<<’

    逻辑运算符

    Solidity中的逻辑运算符:!(逻辑否定),&&(逻辑和),||(逻辑或),==(相等),!=(不相等)

    例:

    contract operators {
    // Arithmetic Operators
    // +,-,*,/, %, **
    // Incremental Operators
    // a++, a--, a+=1, a=a+1,++a,--a;
    a=10;
    a= a++; //here, output will be 10, because the value is first returned and then then increment is done
    a=++a;
    //Logical Operators
    !, &&, ||, ==, !=
    isOwner = true && false;
    var orValue= 0x02 | 0x01; // output would be 0x03
    //Bitwise Operators~,>>, <<;
    function Operators() {
    // Initialize state variables here}}
    

    现在有时需要更复杂的数据类型。为此,Solidity提供结构。

    solidity数据结构

    Solidity提供三种类型的数据结构:

    结构Structs

    Solidity提供了一种以Structs形式定义新类型的方法。Structs是自定义类型,可以对多个变量进行分组。

    pragma solidity ^0.4.0;
    contract Ballot {
    struct Voter { // Struct
    uint weight1, weight2, weight3;
    bool voted;
    address delegate1, delegate2, delegate3, delegate4;
    string name;
    uint vote1, vote2, vote3, vote4, vote5;
    uint height1, height2, height3   } }
    

    注意:结构只能有16个成员,超过该成员可能会发生以下错误:Stack too Deep 堆栈太深。

    结构允许创建具有多个属性的更复杂的数据类型。

    现在,如果你需要一些集合,比如说地址,那该怎么办?好吧,就像大多数语言一样,Solidity也有数组。

    数组Arrays

    Solidity中的数组可以具有编译时固定大小,也可以是动态的。

    uint[3] fixed;  //array of fixed length 3
    uint[] dynamic; //a dynamic array has no fixed size, it can keep growing
    

    还可以创建一个结构数组。使用以前创建的Voter结构:

    Voter[] voting;
    

    注意:将数组声明为public将自动为其创建getter方法。

    Voter[] public voting;
    

    映射mappings

    映射可以看作是哈希表,它们被虚拟地初始化,使得每个可能的键都存在并被映射到其字节表示全为零的值:类型的默认值。

    映射声明为:

    Mapping(_Keytype => _ValueType )
    

    注意:_Keytype几乎可以是任何类型,除了动态大小的数组,合约,枚举和结构。

    例:

    contract MappingExample {
    mapping(address => uint) public balances;
    function update(uint newBalance) {
    balances[msg.sender] = newBalance;  }}
    contract MappingUser {
    function f() returns (uint) {
    MappingExample m = new MappingExample();
    m.update(100);
    return m.balances(this);
    }}
    

    控制结构

    除了switch和goto之外,JavaScript中的大多数控制结构都在Solidity中可用。

    所以有:if,else,while,do,for,break,continue,return,? :,使用从C或JavaScript中已知的通常语义。

    注意:没有像C和JavaScript那样从非布尔类型到布尔类型的类型转换。

    现在让我们看看这些控制结构如何在Solidity中使用。

    contract ControlStructure {
    address public a;
    function ControlStructure>){
    // if-else can be used like this
    if(input1==2)
    a=1;
    else
    a=0;
    // while can be used like this
    while(input1>=0){
    if(input1==5)
    continue;
    input1=input1-1;
    a++;}
    // for loop can be used like this
    for(uint i=0;i<=50;i++) { a++; if(a==4) break; } //do while can be used like this do { a--; } (while a>0);
    // Conditional Operator can be used like this
    bool IsTrue = (a == 1)?true: false;
    /*will show an error because
    there is no type conversion from non-boolean to boolean
    */
    if(1)
    {
    }
    

    继续我们的Solidity教程博客,让我们谈谈合约中可执行的代码单元。这些被称为函数。

    函数

    以下是在Solidity中声明函数的方式。

    function sampleFunc(string name, uint amount) {
    }
    

    上面声明的是一个空体函数,它有两个参数:一个字符串和一个uint。

    可以这样调用此函数:

    sampleFunc("Shashank", 10000);
    

    谈到函数,Solidity还提供函数修饰符。

    函数修饰符

    它用于轻松更改函数的行为。甚至在进行函数调用之前也可以检查这些条件,因为它们已在智能合约的函数定义中声明。

    示例:如果要仅通过函数的所有者或创建者调用kill contract函数。

    contract FunctionModifiers{
    address public creator;
    function FunctionModifiers() {
    creator = msg.sender;}
    Modifier onlyCreator() {
    if(msg.sender!=creator){
    throw; }
    _; //resumes the function wherever the access modifier is used
    }
    function killContract() onlyCreator{ //function will not execute if an exception occurs
    self-destruct(creator); }}
    

    继承

    Solidity通过复制包含多态的代码来支持多重继承。

    contract Owned {
    address Owner ;
    function owned() {
    owner = msg.sender;
    }}
    contract Mortal is Owned {  // 'is' keyword is used for inheritance
    function kill(){
    self-destruct(owner);   }}
    contract User is Owned, Mortal //Multiple inheritance
    {
    string public UserName;
    function User(string _name){
    UserName = _name;
    }}
    

    好吧,我觉得上面讨论的概念足以让你开始使用Solidity编程。

    去写代码吧!!

    有了这个,我结束了这个Solidity Tutorial博客。我希望你喜欢阅读这篇博客并发现它内容丰富。到目前为止,必须对Solidity Programming Language的理解有所了解。现在去练习吧。

    如果希望快速进行以太坊开发,那请看我们精心打造的教程:

    以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。

    其他区块链教程如下:

    • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
    • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
    • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
    • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和事件等内容。
    • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和事件等。
    • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
    • EOS入门教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。

    汇智网原创翻译,转载请标明出处。这里是原文

    展开全文
  • Solidity 教程

    2019-11-19 10:22:31
    章节Solidity – 介绍 Solidity – 环境搭建 Solidity – 基础语法 Solidity – 第一个程序 Solidity – 代码注释 Solidity – 数据类型 Solidity – 变量 Solidity – 变量作用域 Solidity – 运算符 Solidity –...
  • 看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。 如果你想了解更多有关于机器学习、深度学习、区块链、计算机视觉等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们...
  • 看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。 如果你想了解更多有关于机器学习、深度学习、区块链、计算机视觉等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们...
  • solidity教程(五)ERC721 标准和加密

    千次阅读 2018-10-23 19:53:20
    title: solidity教程(五)ERC721 标准和加密 tags: solidity,eth 声明:本系列教程是整理cryptozombies https://cryptozombies.io/zh而来。 Lesson 5: ERC721 标准和加密收藏品 呼,游戏变得越来越刺激啦… 在这...
  • Solidity教程系列第4篇 - Solidity数据位置分析。写在前面Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约有所了解, 如果你还不了解,建议你先看以太坊是什么这部分的内容官方英文文档...
  • 现在的Solidity中文文档,要么翻译的太烂,要么太旧,决定重新翻译下。 尤其点名批评极客学院名为《Solidity官方文档中文版》的翻译,机器翻译的都比它好,大家还是别看了。 写在前面 Solidity是以太坊智能...
  • zCryptoZombies_r8 Solidity教程“(•◡•)/”╚(•⌂•)╝第九轮
  • solidity教程-简介

    2018-10-19 20:16:03
    title: solidity教程-简介 tags: solidity,eth Solidity是一种语法类似JavaScript的高级语言。它被设计成以编译的方式生成以太坊虚拟机代码。在后续内容中你将会发现,使用它很容易创建用于投票、众筹、封闭拍卖、...
  • 这是Solidity教程系列文章第7篇介绍以太单位及时间单位,系列带你全面深入理解Solidity语言。 写在前面 Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约有所了解, 如果你还不...
  • 这是Solidity教程系列文章第8篇介绍Solidity API,它们主要表现为内置的特殊的变量及函数,存在于全局命名空间里。 写在前面 Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约有所了解, ...
  • Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约有所了解,  如果你还不了解,建议你先看以太坊是什么 本文前半部分是参考Solidity官方文档(当前最新版本:0.4.20)进行翻译,后半...
  • solidity编程指南.pdf

    2019-07-05 07:10:20
    solidity编程指南,对语言和工具有详细说明,是基于以太坊开发Dapp的必备参考书。
  • solidity教程(三)高级 Solidity 理论

    千次阅读 2018-10-23 16:20:39
    title: solidity教程(三)高级 Solidity 理论 tags: solidity,eth 声明:本系列教程是整理cryptozombies 而来 https://cryptozombies.io/zh 诶呦喂……少年!你比我想象的厉害。你的 Solidity 技能好棒。 既然你...
  • 这是Solidity教程系列文章第9篇介绍Solidity 错误处理。Solidity系列完整的文章列表请查看分类-Solidity。写在前面Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约有所了解,如果你还不...
  • 这是Solidity教程系列文章第10篇,带大家完全理解Solidity的函数修改器。 Solidity系列完整的文章列表请查看分类-Solidity。 写在前面 Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约...
  • Solidity 是一种语法类似 JavaScript 的高级语言。它被设计成以编译的方式生成以太坊虚拟机代码。在后续内容中你将会发现,使用它很容易创建用于投票、众筹、封闭拍卖、多重签名钱包等等的合约。
  • Solidity教程序列1 - 类型介绍

    千次阅读 2017-12-07 09:32:56
    现在的Solidity中文文档,要么翻译的太烂,要么太旧,决定重新翻译下。写在前面Solidity是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约有所了解。 Solidity语法的介绍会是一系列文章,本文是第一...
  • solidity 0.5.7简明教程

    2019-04-29 15:55:00
    以太坊不仅是一种加密数字货币,它更是功能完备的智能合约平台,solidity就是用来开发以太坊上的智能合约的原生开发语言。solidity最早发布于2015年,它是第一种图灵完备的智能合约专用开发语言。目前除了以太坊之外...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,964
精华内容 1,185
关键字:

solidity教程