Solidity之合约讲解 - 写一个"僵尸"智能合约(小朋友请与家长一起编程)

智能合约

以太坊上的智能合约主要用Solidity编程,智能合约就是运行在以太坊上的一段公开的代码,它有自己的账户地址(就像一个普通的用户地址一样) 有自己的function 可以向别的合约账户或者用户账户转账。

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

contract HelloWorld {

}

版本指令

所有的 Solidity 源码都必须冠以 "version pragma" — 标明 Solidity 编译器的版本. 以避免将来新的编译器可能编译错误。
例如: pragma solidity ^0.4.19; (当前 Solidity 的最新版本是 0.4.19).
综上所述, 下面就是一个最基本的合约 — 每次建立一个新的项目时的第一段代码:

pragma solidity ^0.4.19;
contract HelloWorld {
}

状态变量和整数

contract Example {
  // 这个无符号整数将会永久的被保存在区块链中
  uint myUnsignedInteger = 100;
}

在上面的例子中,定义 myUnsignedInteger 为 uint 类型,并赋值100。
无符号整数: uint
uint 无符号数据类型, 指其值不能是负数,对于有符号的整数存在名 int 的数据类型。
注: Solidity中, uint 实际上是 uint256代名词, 一个256位的无符号整数。你也可以定义位数少的 — uint8, uint16, uint32, 等…… 但一般来讲使用 uint较为简单直接, 除非在某些特殊情况下,这我们后面会讲。

这里我们僵尸合约中每个僵尸的DNA数值为16位的数字,定义 dnaDigitsuint 数据类型, 并赋值 16

数学运算

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

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

Solidity 还支持 乘方操作 (如:x 的 y次方) // 例如: 5 ** 2 = 25

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

结构体

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

struct Zombie {
    string name;
    uint dna;
}

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

1.上面我们建立一个struct 命名为 Zombie.
2.我们的 Zombie 结构体有两个属性: name (类型为 string), 和 dna (类型为 uint)。

数组

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

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

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

Zombie[] zombies; // dynamic Array, we can keep adding to it

记住:状态变量被永久保存在区块链中。所以在你的合约中创建动态数组来保存成结构的数据是非常usefuf的,有点像数据库(但是存的没有数据库多吧啊喂)。

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

Zombie[] public zombies;

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

使用结构体和数组

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

// 创建一个新的Person:
Zombie satoshi = Zombie("Satoshi", dna_number);
// 将新创建的satoshi添加进people数组:
zombies.push(satoshi);
也可以两步并一步,用一行代码更简洁:
zombies.push(Person("Vitalik", dna_Number));

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

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

定义函数

在 Solidity 中函数定义的句法如下:

function createZombie(string _name, uint _dna) {

}

这是一个名为 createZombie 的函数,它接受两个参数:一个 string类型的 和 一个 uint类型的。现在函数内部还是空的。
注:习惯上函数里的变量都是以(_)开头 (但不是硬性规定) 以区别全局变量。我们整个教程都会沿用这个习惯。

我们的函数定义如下:

createZombie(string _name, uint _dna)

私有private/公共public function

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

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

uint[] numbers;

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

这意味着只有 此合约中 的其它函数才能够调用这个函数,给 numbers 数组添加新成员。可以看到,在函数名字后面使用关键字 private 即可。通常来说 private函数的名字用 _ 起始。

View 和 pure等 function修饰符 和 返回值

在function作用域标识符public private等后面加上的returns(type)指明了返回值的类型

string greeting = "What's up dog";

function sayHello() public returns (string) { // returns(类型) 指明返回的类型
  return greeting;
}

如果方法没有改变任何值或者写入任何东西,可以加上 view

function sayHello() public **view** returns (string) {

Solidity 还支持 pure 函数, 表明这个函数甚至都不访问应用里的数据,例如:

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

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

//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256("aaaab");
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256("aaaac");

类型转换-形式是uint(需要转化的数)

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

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, 就可以了,编译器也不会出错。

事件

终于说到事件了,事件算是Solidity里非常重要的一个功能了
事件 是合约和区块链通讯的一种机制。你的前端应用“监听”某些事件,并做出反应。

例子

// 这里建立事件
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) { 
  // 干些事
}
//写到这里整体大概就是这个样子
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);
    }
}

上述代码中在function里面添加事件监听部分...居然...前面的.push(...)可以返回数组的大小

//这样是不行的 
NewZombie(zombies.push(), _name, _dna);

我以为这样也可以,结果push()不带参数是不对的好吗?并不知道这个secret.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,258评论 0 10
  • Scala与Java的关系 Scala与Java的关系是非常紧密的!! 因为Scala是基于Java虚拟机,也就是...
    灯火gg阅读 3,399评论 1 24
  • 邻居杨丽是个漂亮姑娘,从小就很有主见,她有个小2岁的妹妹,姐妹俩一直是巷口亮丽的风景。我们是邻居,也是校友,只是我...
    梓萱迩阅读 473评论 0 3
  • 这几天非常的忙碌,本想拿之前写的东西应付007的作业,想想与其应付不如将这几天的感受的心得写出来,也算是对自己的总...
    浮云狒阅读 383评论 0 0