摘要:狀態(tài)變量是被永久地保存在合約中。如何定義一個私有的函數(shù)呢這意味著只有我們合約中的其它函數(shù)才能夠調用這個函數(shù),給數(shù)組添加新成員。事件事件事件我們的合約幾乎就要完成了讓我們加上一個事件事件是合約和區(qū)塊鏈通訊的一種機制。
大愛loom!大愛loom!內容整理自loom僵尸小游戲課程
一、合約所有的 Solidity 源碼都必須冠以 "version pragma" — 標明 Solidity 編譯器的版本. 以避免將來新的編譯器可能破壞你的代碼。
例如: pragma solidity ^0.4.19; (當前 Solidity 的最新版本是 0.4.19).
綜上所述, 下面就是一個最基本的合約 — 每次建立一個新的項目時的第一段代碼:
pragma solidity ^0.4.19; contract HelloWorld { }
為了建立我們的僵尸部隊, 讓我們先建立一個基礎合約,稱為?ZombieFactory。
pragma solidity ^0.4.19; contract ZombieFactory { }
!我們已經為我們的合約做了一個外殼, 下面學習 Solidity 中如何使用變量。
狀態(tài)變量是被永久地保存在合約中。也就是說它們被寫入以太幣區(qū)塊鏈中. 想象成寫入一個數(shù)據(jù)庫。
無符號整數(shù): uint
uint 無符號數(shù)據(jù)類型, 指其值不能是負數(shù),對于有符號的整數(shù)存在名為 int的數(shù)據(jù)類型。
注: Solidity中, uint 實際上是 uint256代名詞, 一個256位的無符號整數(shù)。你也可以定義位數(shù)少的uints — uint8, uint16, uint32, 等
我們的僵尸DNA將由一個十六位數(shù)字組成。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; }1.1 數(shù)值運算
在 Solidity 中,數(shù)學運算很直觀明了,與其它程序設計語言相同:
加法: 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
為了保證我們的僵尸的DNA只含有16個字符,我們先造一個uint數(shù)據(jù),讓它等于10^16。這樣一來以后我們可以用模運算符?%?把一個整數(shù)變成16位。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; }1.2 結構體
有時你需要更復雜的數(shù)據(jù)類型,Solidity 提供了 結構體:
struct Person { uint age; string name; }
結構體允許你生成一個更復雜的數(shù)據(jù)類型,它有多個屬性。
注:我們剛剛引進了一個新類型, string。 字符串用于保存任意長度的 UTF-8 編碼數(shù)據(jù)。 如: string greeting = "Hello world!"。
在我們的程序中,我們將創(chuàng)建一些僵尸!每個僵尸將擁有多個屬性,所以這是一個展示結構體的完美例子。 solidity中的結構體要聲明在contract內部
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; // 這里開始 struct Zombie { string name; uint dna; } }1.3 數(shù)組
如果你想建立一個集合,可以用 數(shù)組這樣的數(shù)據(jù)類型. Solidity 支持兩種數(shù)組: 靜態(tài) 數(shù)組和動態(tài) 數(shù)組:
// 固定長度為2的靜態(tài)數(shù)組: uint[2] fixedArray; // 固定長度為5的string類型的靜態(tài)數(shù)組: string[5] stringArray; // 動態(tài)數(shù)組,長度不固定,可以動態(tài)添加元素: uint[] dynamicArray;
你也可以建立一個 結構體類型的數(shù)組 例如,上一章提到的 Person:
Person[] people; // dynamic Array, we can keep adding to it
記住:狀態(tài)變量被永久保存在區(qū)塊鏈中。所以在你的合約中創(chuàng)建動態(tài)數(shù)組來保存成結構的數(shù)據(jù)是非常有意義的。
公共數(shù)組
你可以定義 public 數(shù)組, Solidity 會自動創(chuàng)建 getter 方法. 語法如下:
Person[] public people;
其它的合約可以從這個數(shù)組讀取數(shù)據(jù)(但不能寫入數(shù)據(jù)),所以這在合約中是一個有用的保存公共數(shù)據(jù)的模式。
為了把一個僵尸部隊保存在我們的APP里,并且能夠讓其它APP看到這些僵尸,我們需要一個公共數(shù)組。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; }1.4 定義函數(shù)
在 Solidity 中函數(shù)定義的句法如下:
function eatHamburgers(string _name, uint _amount) { }
這是一個名為 eatHamburgers 的函數(shù),它接受兩個參數(shù):一個 string類型的 和 一個 uint類型的。現(xiàn)在函數(shù)內部還是空的。
注:: 習慣上函數(shù)里的變量都是以(_)開頭 (但不是硬性規(guī)定) 以區(qū)別全局變量。我們整個教程都會沿用這個習慣。
我們的函數(shù)定義如下:
eatHamburgers("vitalik", 100);
在我們的應用里,我們要能創(chuàng)建一些僵尸,讓我們寫一個函數(shù)做這件事吧!
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { uint dna; string name; } Zombie[] public zombies; function createZombie(string _name, uint _dna) { } }1.5 創(chuàng)建新的結構體
還記得上個例子中的 Person 結構嗎?
struct Person { uint age; string name; } Person[] public people;
現(xiàn)在我們學習創(chuàng)建新的 Person 結構,然后把它加入到名為 people 的數(shù)組中.
// 創(chuàng)建一個新的Person: Person satoshi = Person(172, "Satoshi"); // 將新創(chuàng)建的satoshi添加進people數(shù)組: people.push(satoshi);
你也可以兩步并一步,用一行代碼更簡潔:
people.push(Person(16, "Vitalik"));
注:array.push() 在數(shù)組的 尾部 加入新元素 ,所以元素在數(shù)組中的順序就是我們添加的順序, 如:
uint[] numbers; numbers.push(5); numbers.push(10); numbers.push(15); // numbers is now equal to [5, 10, 15]
讓我們創(chuàng)建名為createZombie的函數(shù)來做點兒什么吧。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function createZombie(string _name, uint _dna) { // 這里開始 Zombie z = Zombie(_name, _dna); zombies.push(z); } }1.6 私有 / 公共函數(shù)
Solidity 定義的函數(shù)的屬性默認為公共。 這就意味著任何一方 (或其它合約) 都可以調用你合約里的函數(shù)。
顯然,不是什么時候都需要這樣,而且這樣的合約易于受到攻擊。 所以將自己的函數(shù)定義為私有是一個好的編程習慣,只有當你需要外部世界調用它時才將它設置為公共。
如何定義一個私有的函數(shù)呢?
uint[] numbers; function _addToArray(uint _number) private { numbers.push(_number); }
這意味著只有我們合約中的其它函數(shù)才能夠調用這個函數(shù),給 numbers 數(shù)組添加新成員。
可以看到,在函數(shù)名字后面使用關鍵字 private 即可。和函數(shù)的參數(shù)類似,私有函數(shù)的名字用(_)起始。
我們合約的函數(shù)?createZombie?的默認屬性是公共的,這意味著任何一方都可以調用它去創(chuàng)建一個僵尸。 咱們來把它變成私有吧!
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function _createZombie(string _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); } }1.7 函數(shù)的更多屬性
本章中我們將學習函數(shù)的返回值和修飾符。
返回值要想函數(shù)返回一個數(shù)值,按如下定義:
string greeting = "What"s up dog"; function sayHello() public returns (string) { return greeting; }
Solidity 里,函數(shù)的定義里可包含返回值的數(shù)據(jù)類型(如本例中 string)。
函數(shù)的修飾符上面的函數(shù)實際上沒有改變 Solidity 里的狀態(tài),即,它沒有改變任何值或者寫任何東西。
這種情況下我們可以把函數(shù)定義為 view, 意味著它只能讀取數(shù)據(jù)不能更改數(shù)據(jù):
function sayHello() public view returns (string) {
Solidity 還支持 pure 函數(shù), 表明這個函數(shù)甚至都不訪問應用里的數(shù)據(jù),例如:
function _multiply(uint a, uint b) private pure returns (uint) { return a * b; }
這個函數(shù)甚至都不讀取應用里的狀態(tài) — 它的返回值完全取決于它的輸入?yún)?shù),在這種情況下我們把函數(shù)定義為 pure.
注:可能很難記住何時把函數(shù)標記為 pure/view。 幸運的是, Solidity 編輯器會給出提示,提醒你使用這些修飾符。
我們想建立一個幫助函數(shù),它根據(jù)一個字符串隨機生成一個DNA數(shù)據(jù)。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function _createZombie(string _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); } function _generateRandomDna(string _str) private view returns (uint) { } }1.8 Keccak256 和 類型轉換
如何讓 _generateRandomDna 函數(shù)返回一個全(半) 隨機的 uint?
Ethereum 內部有一個散列函數(shù)keccak256,它用了SHA3版本。一個散列函數(shù)基本上就是把一個字符串轉換為一個256位的16進制數(shù)字。字符串的一個微小變化會引起散列數(shù)據(jù)極大變化。
這在 Ethereum 中有很多應用,但是現(xiàn)在我們只是用它造一個偽隨機數(shù)。
例子:
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5 keccak256("aaaab"); //b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9 keccak256("aaaac");
顯而易見,輸入字符串只改變了一個字母,輸出就已經天壤之別了。
注: 在區(qū)塊鏈中安全地產生一個隨機數(shù)是一個很難的問題, 本例的方法不安全,但是在我們的Zombie DNA算法里不是那么重要,已經很好地滿足我們的需要了。類型轉換
有時你需要變換數(shù)據(jù)類型。例如:
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 類型接收時, 就會造成潛在的錯誤。如果把它的數(shù)據(jù)類型轉換為 uint8, 就可以了,編譯器也不會出錯。
給 _generateRandomDna 函數(shù)添加代碼! 它應該完成如下功能:
第一行代碼取 _str 的 keccak256 散列值生成一個偽隨機十六進制數(shù),類型轉換為 uint, 最后保存在類型為 uint 名為 rand 的變量中。
我們只想讓我們的DNA的長度為16位 (還記得 dnaModulus?)。所以第二行代碼應該 return 上面計算的數(shù)值對 dnaModulus 求余數(shù)(%)。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function _createZombie(string _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); } function _generateRandomDna(string _str) private view returns (uint) { uint rand = uint(keccak256(_str)); return rand % dnaModulus; } }1.9 放在一起
我們就快完成我們的隨機僵尸制造器了,來寫一個公共的函數(shù)把所有的部件連接起來。
寫一個公共函數(shù),它有一個參數(shù),用來接收僵尸的名字,之后用它生成僵尸的DNA。
創(chuàng)建一個 public 函數(shù),命名為createRandomZombie. 它將被傳入一個變量 _name (數(shù)據(jù)類型是 string)。 (注: 定義公共函數(shù) public 和定義一個私有 private 函數(shù)的做法一樣)。
函數(shù)的第一行應該調用 _generateRandomDna 函數(shù),傳入 _name 參數(shù), 結果保存在一個類型為 uint 的變量里,命名為 randDna。
第二行調用 _createZombie 函數(shù), 傳入?yún)?shù): _name 和 randDna。
整個函數(shù)應該是4行代碼 (包括函數(shù)的結束符號 } )。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function _createZombie(string _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); } function _generateRandomDna(string _str) private view returns (uint) { uint rand = uint(keccak256(_str)); return rand % dnaModulus; } // 事件 pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function _createZombie(string _name, uint _dna) private { zombies.push(Zombie(_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); } }1.10 : 事件
我們的合約幾乎就要完成了!讓我們加上一個事件.
事件 是合約和區(qū)塊鏈通訊的一種機制。你的前端應用“監(jiān)聽”某些事件,并做出反應。
例子:
// 這里建立事件 event IntegersAdded(uint x, uint y, uint result); function add(uint _x, uint _y) public { uint result = _x + _y; //觸發(fā)事件,通知app IntegersAdded(_x, _y, result); return result; }
你的 app 前端可以監(jiān)聽這個事件。JavaScript 實現(xiàn)如下:
YourContract.IntegersAdded(function(error, result) { // 干些事 }
我們想每當一個僵尸創(chuàng)造出來時,我們的前端都能監(jiān)聽到這個事件,并將它顯示出來。
1。 定義一個 事件 叫做 NewZombie。 它有3個參數(shù): zombieId (uint), name (string), 和 dna (uint)。
2。 修改 _createZombie 函數(shù)使得當新僵尸造出來并加入zombies數(shù)組后,生成事件NewZombie。
3。 需要定義僵尸id。 array.push() 返回數(shù)組的長度類型是uint - 因為數(shù)組的第一個元素的索引是 0, array.push() - 1 將是我們加入的僵尸的索引。 zombies.push() - 1 就是 id,數(shù)據(jù)類型是 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; // 這里觸發(fā)事件 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); } }二、支持多用戶啦! 2.1 映射(Mapping)和地址(Address)
我們通過給數(shù)據(jù)庫中的僵尸指定“主人”, 來支持“多玩家”模式。
如此一來,我們需要引入2個新的數(shù)據(jù)類型:mapping(映射) 和 address(地址)。
Addresses (地址)以太坊區(qū)塊鏈由 account (賬戶)組成,你可以把它想象成銀行賬戶。一個帳戶的余額是 以太 (在以太坊區(qū)塊鏈上使用的幣種),你可以和其他帳戶之間支付和接受以太幣,就像你的銀行帳戶可以電匯資金到其他銀行帳戶一樣。
每個帳戶都有一個“地址”,你可以把它想象成銀行賬號。這是賬戶唯一的標識符,它看起來長這樣:
0x21110f92fd4e5b09d1237e6d30a2a8d733cd154b
(如果你喜歡本課程的話,請打賞我一些以太幣!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://www.ezyhdfw.cn/yun/24059.html
摘要:本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接一步步教你開發(fā)部署第一個去中心化應用寵物商店原文已更新,請讀者前往原文閱讀今天我們來編寫一個完整的去中心化區(qū)塊鏈應用本文可以和編寫智能合約結合起來看。 本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接:一步步教你開發(fā)、部署第一個去中心化應用(Dapp) - 寵物商店原文已更新,請讀者前往原文閱讀 今天我們來編寫一個完整的去中心化(區(qū)塊鏈)應用(Dapps), 本...
摘要:利用以太坊的智能合約可以輕松編寫出屬于自己的代幣,代幣可以代表任何可以交易的東西,如積分財產證書等等。要求我們在實現(xiàn)代幣的時候必須要遵守的協(xié)議,如指定代幣名稱總量實現(xiàn)代幣交易函數(shù)等,只有支持了協(xié)議才能被以太坊錢包支持。 本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接:創(chuàng)建自己的數(shù)字貨幣(ERC20 代幣)進行 ICO原文已更新,請讀者前往原文閱讀 本文從技術角度詳細介紹如何基于以太坊ERC20創(chuàng)...
摘要:和比特幣協(xié)議有所不同的是,以太坊的設計十分靈活,極具適應性。超級賬本區(qū)塊鏈的商業(yè)應用超級賬本超級賬本是基金會下的眾多項目中的一個。證書頒發(fā)機構負責簽發(fā)撤 showImg(https://segmentfault.com/img/bV2ge9?w=900&h=385); 從比特幣開始 一個故事告訴你比特幣的原理及運作機制 這篇文章的定位會比較科普,盡量用類比的方法將比特幣的基本原理講出來...
摘要:引言給迷失在如何學習區(qū)塊鏈技術的同學一個指引,區(qū)塊鏈技術是隨比特幣誕生,因此要搞明白區(qū)塊鏈技術,應該先了解下比特幣。但區(qū)塊鏈技術不單應用于比特幣,還有非常多的現(xiàn)實應用場景,想做區(qū)塊鏈應用開發(fā),可進一步閱讀以太坊系列。 本文始發(fā)于深入淺出區(qū)塊鏈社區(qū), 原文:區(qū)塊鏈技術學習指引 原文已更新,請讀者前往原文閱讀 本章的文章越來越多,本文是一個索引帖,方便找到自己感興趣的文章,你也可以使用左側...
摘要:在區(qū)塊鏈上,虛擬機就是智能合約的運行環(huán)境,是一個可以完全對外隔離的完整計算機體系。區(qū)塊鏈通過虛擬機來調用和執(zhí)行智能合約,并要求所有節(jié)點都達成一致。 秘猿科技使命是用技術創(chuàng)造信任,為價值網(wǎng)絡提供基礎設施和服務。為了實現(xiàn)這個使命,我們三年來堅持初心,步步為營打造加密經濟網(wǎng)絡。我們想要讓互聯(lián)網(wǎng)回歸到本源,用區(qū)塊鏈技術,去構造更美好的社會,因此我們設計了 CKB 底層公鏈。我們自己造輪子,開創(chuàng)...
閱讀 899·2021-10-13 09:39
閱讀 3781·2021-10-12 10:12
閱讀 1858·2021-08-13 15:07
閱讀 1068·2019-08-29 15:31
閱讀 2939·2019-08-26 13:25
閱讀 1838·2019-08-23 18:38
閱讀 1951·2019-08-23 18:25
閱讀 1903·2019-08-23 17:20