solidity基础知识
1、solidity是一種語法類似JavaScript的高級語言,它被設(shè)計(jì)成以編譯的方式生成以太坊虛擬機(jī)代碼。在后續(xù)的內(nèi)容中你將會發(fā)現(xiàn),使用它很容易創(chuàng)建用于投票、眾籌、封閉拍賣、多重簽名錢包等等的合約。
solidity在線IDE:https://ethereum.github.io/browser-solidity/#optimize=false&version=soljson-v0.4.23+commit.124ca40d.js
2、智能合約
智能合約(Smart Contract)是一種旨在以信息化方式傳播、驗(yàn)證或執(zhí)行合同的計(jì)算機(jī)協(xié)議。只能合約允許在沒有第三方的情況下進(jìn)行可信交易。這些交易可追蹤不可逆轉(zhuǎn)。智能合約概念于1994年由Nick Azabo首次指出。
智能合約的目的是提供優(yōu)于傳統(tǒng)合同方法的安全,病減少與合同相關(guān)的其他成本交易。
3、以太坊虛擬機(jī)
以太坊虛擬機(jī)(EVM)是以太坊中只能合約的運(yùn)行環(huán)境。它不僅被沙箱封裝起來,事實(shí)上它被完全隔離,也就是說EVM內(nèi)部的代碼不能尋呼機(jī)到網(wǎng)絡(luò)、文件系統(tǒng)或者其他進(jìn)程。甚至只能合約與其它只能合約只有有限的接觸。
3.1、賬戶
以太坊有兩類賬戶。他們共用同一個(gè)地址 空間。外部賬戶,該賬戶被公鑰-私鑰對控制(人類)。合約賬戶,該類賬戶被存儲在賬戶中的代碼控制。
外部賬戶由公鑰決定;合約賬戶在創(chuàng)建合約時(shí)確定(由合約創(chuàng)建者地址+該地址發(fā)出過得交易數(shù)量計(jì)算。地址發(fā)出過的交易數(shù)量也被稱為nonce);合約賬戶存儲代碼。
另外,每個(gè)賬戶都有一個(gè)以太幣余額(單位wei),該賬戶余額可以通過向它發(fā)送帶有以太幣的交易來改變。
3.2、交易
交易是一條消息,從一個(gè)賬戶發(fā)送得到另一個(gè)賬戶(可能是相同的賬戶或者零賬戶)。交易可以包含二進(jìn)制數(shù)據(jù)(payload)和以太幣。
另外,如果目標(biāo)賬戶零賬戶(賬戶地址是零),交易將創(chuàng)建一個(gè)新合約。即創(chuàng)建合約也是一個(gè)交易的過程,只是目標(biāo)賬戶是0.
3.3、Gas
以太坊上的每筆交易都會收取一定數(shù)量的gas,gas的目的是限制執(zhí)行交易所需的工作量,同時(shí)為執(zhí)行支付費(fèi)用。當(dāng)EVM執(zhí)行交易時(shí),gas將按照特定規(guī)則被逐漸消耗。
gas price (以太幣計(jì))是由交易創(chuàng)建者設(shè)計(jì)的,發(fā)送賬戶需要預(yù)付的交易費(fèi)用 = gas price * gas amount。如果執(zhí)行結(jié)束還有剩余,這些gas將被返還給發(fā)送賬戶。
無論執(zhí)行到什么位置,一旦gas被消耗盡,將會觸發(fā)一個(gè)out-of-gas異常。當(dāng)前調(diào)用幀所做的所有狀態(tài)修改都將被回滾。
3.3 存儲、主存和棧
每個(gè)賬戶有一塊持久化區(qū)域被稱為存儲,其形式為key-value,key和value的長度均為256比特。在合約里,不能遍歷賬戶的存儲。相對于另外兩種,存儲的讀操作開銷較大,修改存儲更甚。一個(gè)合約只能對它自己的存儲進(jìn)行讀寫。
第二內(nèi)存區(qū)被稱為主存。合約執(zhí)行每次消息調(diào)用時(shí),都有一塊新的,被清除過得主存。主存可以以字節(jié)粒度尋址,但是讀寫粒度為32字節(jié)(256比特)。操作主存的開銷隨著其增長而變大(平方級別)。
EVM不是基于寄存器,而是基于棧的虛擬機(jī),因此所有的計(jì)算都在一個(gè)被稱為棧的區(qū)域執(zhí)行。棧最大有1024個(gè)元素,每個(gè)元素256比特,對棧的訪問只限于其頂端,方式為:允許拷貝最頂端的16個(gè)元素中的一個(gè)到棧頂,或者交換棧頂元素和下面16個(gè)元素中的一個(gè)。所有其他操作都只能取最頂?shù)膬蓚€(gè)(或者一個(gè)、更多)元素,并把結(jié)果壓在棧頂。當(dāng)然可以把棧上的元素放到存儲或者主存中,但是無法訪問棧上指定深度的那個(gè)元素,在那之前必須要把指定深度之上的所有元素從棧中移除才行
3.4、指令集
EVM的指令集被刻意保持在最小規(guī)模,以盡可能避免可能導(dǎo)致共識問題的錯(cuò)誤實(shí)現(xiàn)。所有的指令都是針對256比特這個(gè)基本的數(shù)據(jù)類型的操作。具備常用的算術(shù),位,邏輯和比較操作。也可以做到條件和無條件跳轉(zhuǎn)。此外,合約可以訪問當(dāng)前區(qū)塊的相關(guān)屬性,比如它的編號和時(shí)間戳。
3.5、消息調(diào)用
合約可以通過消息調(diào)用的方式來調(diào)用其它合約或者發(fā)送以太幣到非合約賬戶。消息調(diào)用和交易非常類似,它們都有一個(gè)源,一個(gè)目標(biāo),數(shù)據(jù)負(fù)載,以太幣,gas和返回?cái)?shù)據(jù)。事實(shí)上每個(gè)交易都可以被認(rèn)為是一個(gè)頂層消息調(diào)用,這個(gè)消息調(diào)用會依次產(chǎn)生更多的消息調(diào)用。
一個(gè)合約可以決定剩余gas的分配。比如內(nèi)部消息調(diào)用時(shí)使用多少gas,或者期望保留多少gas。如果在內(nèi)部消息調(diào)用時(shí)發(fā)生了out-of-gas異常(或者其他異常),合約將會得到通知,一個(gè)錯(cuò)誤碼被壓在棧上。這種情況只是內(nèi)部消息調(diào)用的gas耗盡。在solidity中,這種情況下發(fā)起調(diào)用的合約默認(rèn)會觸發(fā)一個(gè)人工異常。這個(gè)異常會打印出調(diào)用棧。就像之前說過的,被調(diào)用的合約(發(fā)起調(diào)用的合約也一樣)會擁有嶄新的主存并能夠訪問調(diào)用的負(fù)載。調(diào)用負(fù)載被存儲在一個(gè)單獨(dú)的被稱為calldata的區(qū)域。調(diào)用執(zhí)行結(jié)束后,返回?cái)?shù)據(jù)將被存放在調(diào)用方預(yù)先分配好的一塊內(nèi)存中。
調(diào)用層數(shù)被限制為1024,因此對于更加復(fù)雜的操作,我們應(yīng)該使用循環(huán)而不是遞歸。
3.6、代碼調(diào)用和庫
存在一種特殊類型的消息調(diào)用,被稱為callcode。它跟消息調(diào)用幾乎完全一樣,只是加載目標(biāo)地址的代碼將在發(fā)起調(diào)用的合約上下文中運(yùn)行。這意味著一個(gè)合約可以在運(yùn)行時(shí)從另外一個(gè)地址動態(tài)加載代碼。存儲當(dāng)前地址和余額都指向發(fā)起調(diào)用的合約,只有代碼是被調(diào)用地址獲取的。
這使得solidity可以實(shí)現(xiàn)庫。可服用的庫代碼可以應(yīng)用在一個(gè)合約存儲上,可以用來實(shí)現(xiàn)復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。
3.7、日志
在區(qū)塊層面,可以用一種特殊的可索引的數(shù)據(jù)界都來存儲數(shù)據(jù)。這個(gè)特性被稱為日志,solidity用它來實(shí)現(xiàn)事件。合約創(chuàng)建之后就無法訪問日志數(shù)據(jù),但是這些數(shù)據(jù)可以從區(qū)塊鏈外高效的訪問。因?yàn)椴糠秩罩緮?shù)據(jù)被存儲在布隆過濾器中,我們可以高效并且安全地搜索日志,所以那些沒有下載整個(gè)區(qū)塊鏈的網(wǎng)絡(luò)節(jié)點(diǎn)(輕客戶端)也可以找到這些日志。
3.8、創(chuàng)建
合約甚至可以通過一個(gè)特殊的指令來創(chuàng)建其他合約(不是簡單的向零地址發(fā)起調(diào)用)。創(chuàng)建合約的調(diào)用跟普通的消息調(diào)用的區(qū)別在于,負(fù)載數(shù)據(jù)執(zhí)行的結(jié)果被當(dāng)做代碼。調(diào)用者/創(chuàng)建者在棧上得到新的合約。
3.9、自毀
只有在某個(gè)地址的合約執(zhí)行自毀操作時(shí),合約代碼才會從區(qū)塊鏈上移除。合約地址上剩余的以太幣會發(fā)送給指定目標(biāo),然后其存儲和代碼被移除。
4、語法
4.1、引入源文件
源文件包括任意數(shù)量的合約定義和include指令
solidity支持import語句。
在全局層次上,可以這樣用:
(1)import "filename";將會從“filename”
(2)**import** ****** as symboName from "filename" 創(chuàng)立一個(gè)全局的符號名為sumbolName,其中的成員來自filename的所有符號
(3)import {symbol as alias,symbol2} from "filename";將創(chuàng)立一個(gè)新的全局變量別名:alias和symbol2,它將分別從filename引入symbol1和symbol2
(4)import "filename" as symbolName;//推薦使用 等價(jià)于 import * as symbolName from "filename"
路徑:
(1)在上面,文件名總是用/作為目錄分隔符,.是當(dāng)前的目錄,..是父目錄,路徑名稱不用.開頭的都將視為絕對路徑。
(2)從同一個(gè)目錄下import一個(gè)文件夾x作為當(dāng)前文件,用import "./x" as x;如果使用import "x" as x;是不同的文件引用(在全局中使用“include directory”)
(3)import "x" as X 是不同的文件引用(在全局中使用“include directory”)。
(4)它將依賴于編譯器(見后)來解析路徑。通常,目錄層次不必嚴(yán)格限定映射到你的本地文件系統(tǒng),它也可以映射到ipfs,http或git上的其他資源
4.2、注釋
//單行注釋和多行注釋(/../)
有種特別的注釋叫做“natspec”(文檔以后寫出來),在函數(shù)聲明或定義的右邊用三個(gè)斜杠(///)或者用兩個(gè)型號(/*.../).如果想要調(diào)用一個(gè)函數(shù),可以使用doxygen-style標(biāo)簽里面文檔功能,形式驗(yàn)證,并提供一個(gè)確認(rèn)條件的文本注釋顯示給用戶。
4.3、 類型
solidity是一種靜態(tài)類型語言,意思是每個(gè)變量(聲明和本地)在編譯時(shí)刻都要定義(或者至少要知曉,參看后面的類型導(dǎo)出)。solidity提供幾個(gè)基本類型組合成復(fù)雜類型。
(1)變量類型
以下類型被叫做值類型,因?yàn)檫@些類型的變量總是要被賦值,作為函數(shù)參數(shù)或者在賦值中,總需要拷貝。
布爾類型
布爾:bool 值是true和false
操作符:!(邏輯非)、&&(邏輯與)、||(邏輯或)、==(相等)、!=(不等)。
操作符||和&&可以應(yīng)用常規(guī)短路規(guī)則,||如果一邊為true,另一邊不用計(jì)算,&&如果一邊為false,另一邊不用計(jì)算。
整型:int和uint,有符號和無符號的整數(shù),關(guān)鍵字uint8到uint256步長8(從8到256位的無符號整數(shù))。uint和int分別是uint256和int256的別名。
操作符:比較(<=,<,==,!=,>=,>計(jì)算布爾量),位操作符(&,|,^(位異或),~(位取反)),算術(shù)操作符(+,-,一元-,一元+,*,/,%(取余數(shù)),**(冪次方)).
地址成員:address,可以轉(zhuǎn)賬和查詢余額(balance、send、transfer。
注解:如果x是合約地址,它的代碼將和send調(diào)用一起執(zhí)行(這是EVM的限制,不能修改),如果gas用完或者失敗,ether轉(zhuǎn)移將被回退,這種情況下,send返回false。
調(diào)用(call)和調(diào)用碼(callcode):調(diào)用這兩個(gè)函數(shù)將會返回true或false。callcode的目的是使用庫代碼存儲在另一個(gè)合同。用戶必須要確保存儲在兩個(gè)合約的布局適用于callcode。這兩個(gè)函數(shù)是非常低級的函數(shù),它可以作為打破solidity的類型安全的最后手段。
(2)引用類型
復(fù)雜類型,拷貝復(fù)雜類型可能相當(dāng)耗費(fèi)存儲?時(shí)間,我們必須考慮把他們存儲在內(nèi)存(這不是持久化)或者存儲器(狀態(tài)變量存儲的地方)
函數(shù)返回參數(shù)存儲在內(nèi)存,局部變量默認(rèn)是存儲在存儲器里面。
(3)數(shù)據(jù)存儲位置:內(nèi)存、存儲器、calldata
外部函數(shù)的參數(shù)(無返回):calldata
狀態(tài)變量:存儲器
默認(rèn)數(shù)據(jù)存儲位置
函數(shù)(有返回)的參數(shù):內(nèi)存
其他局部變量:存儲器。
注意存儲在不同位置的變量不能直接賦值。
內(nèi)存與內(nèi)存、存儲器和存儲器之間賦值是引用賦值 ,值會改變,其他是拷貝,不會改變值。
(4)數(shù)組
數(shù)組可以是固定大小的,也可以是動態(tài)的,對于存儲器數(shù)組來說,成員類型可以是任意的(也可以是其他數(shù)組、映射或結(jié)構(gòu))。對于內(nèi)存數(shù)組來說,成員類型不能是一個(gè)映射;如果是公開可見的函數(shù)參數(shù),成員類型是必須是ABI類型的。
動態(tài)數(shù)組:string[] str;
定長數(shù)組:string[3] str;
bytesh和string是特殊類型的數(shù)組。bytes類似于byte[],但它是緊湊排列在calldata里的。string等于bytes,但不允許用長度或索引訪問。同時(shí)如果想要訪問字符串s的某個(gè)字節(jié),要使用bytes(s).length/bytes(s)[7] = "x";記住,你正在訪問的低級utf - 8字節(jié)表示,而不是單個(gè)字符!
成員:
length:數(shù)組數(shù)量。數(shù)組長度一旦確定,如果訪問長度以外的數(shù)據(jù),會報(bào)錯(cuò),如果刪除數(shù)組里面的數(shù)據(jù),要手動調(diào)整長度,和數(shù)據(jù)位置。因?yàn)閯h除只是把值初始化,并沒有刪除。
push:在數(shù)組尾部添加數(shù)據(jù)。
注意:到目前為止還不可以在外部函數(shù)中使用數(shù)組的數(shù)組。由于EVM的局限,合約函數(shù)f contract C { function f() returns (uint[]) { ... } }不可能從外部函數(shù)調(diào)用返回動態(tài)的數(shù)組(不能返回?cái)?shù)組),使用web3.js調(diào)用,將有返回值,但是使用solidity調(diào)用,就沒有返回值。
(5)結(jié)構(gòu)體
struct shape{uint height;uint width;} 函數(shù)賦值 shape(1,2).結(jié)構(gòu)體內(nèi)部可以包含任何類型的數(shù)據(jù),但是不能包含它自己。也就是不能把自己作為自己的成員。
(6)映射(mapping)
一種鍵值對的映射關(guān)系存儲結(jié)構(gòu)。定義方式為mapping(_KeyType => _KeyValue).鍵的類型允許除映射外的所有類型。值的類型無限制。映射可以視為一個(gè)哈希表,其中所有可能的鍵已被虛擬化的創(chuàng)建,被映射到一個(gè)默認(rèn)值(二進(jìn)制表示的零)。但在映射表中,我們并不存儲鍵的數(shù)據(jù),僅僅存儲它的keccak256哈希值,用來查找時(shí)使用。所以映射沒有長度。mapping(address => uint) public balances;添加數(shù)據(jù)balance[msg.sender]=msg.value;
(7)刪除delete(參考:http://baijiahao.baidu.com/s?id=1566265348199485&wfr=spider&for=pc)
delete用于釋放空間,釋放空間將會返還一些gas。
刪除基本類型將他們的值設(shè)置為初始值。刪除枚舉時(shí),會將其值重置為序號0的值。不能刪除函數(shù),刪除結(jié)構(gòu)體會將結(jié)構(gòu)體里面的變量都設(shè)置為初始值,刪除映射會報(bào)錯(cuò),不過可以刪除里面的某一項(xiàng)。刪除十足試講數(shù)組的所有元素設(shè)置為0.
刪除本質(zhì)是對一個(gè)變量賦初值。所以我們刪除storage的引用時(shí)會報(bào)錯(cuò),因?yàn)閟torage的引用并沒有自己已分配的存儲空間,所以不能對storage的引用直接賦初值。
(8)類型轉(zhuǎn)換
隱式轉(zhuǎn)換:如果一個(gè)操作符應(yīng)用于不同類型,編譯器就會試圖隱式把操作數(shù)的類型,從一種類型轉(zhuǎn)換到其他類型。一般來說,一個(gè)隱式的值類型之間的轉(zhuǎn)換時(shí)可能的,如果予以敏感的話,信息不回丟失;int8可轉(zhuǎn)成int16,int256等等,但是int8不能轉(zhuǎn)成uint256,因?yàn)閡int256范圍不包含int8部分內(nèi)容。
顯示轉(zhuǎn)換:int8 a = 2;uint b = uint(a);如果一個(gè)類型是顯式地轉(zhuǎn)換為一個(gè)更小的類型,高階位將被移除.
(9)單位(參考:https://blog.csdn.net/wo541075754/article/details/79049425)
以太單位:wei、finney、szabo、ether,以太幣數(shù)量默認(rèn)缺省單位是wei。
1kwei(babbage) = 1e3wei (e就是乘以10的3次方)
1Mwei(lovelace) = 1e6wei; 1Gwei(shannon) = 1e9; 1microether(szabo) = 1e12; 1milliether(finney) = 1e15wei; 1ether = 1e18wei;
時(shí)間單位:now獲取系統(tǒng)當(dāng)前時(shí)間,類型是int,沒有日期:1 seconds;minutes ,hours ,days,weeks,years等
如果你使用這些單位執(zhí)行日歷計(jì)算,要注意以下問題。 因?yàn)殚c秒,所以每年不總是等于365天,甚至每天也不是都有24小時(shí),。由于無法預(yù)測閏秒,一個(gè)精確的日歷庫必須由外部oracle更新。
(10)特殊的變量和函數(shù)
有特殊的變量和函數(shù)總是存在于全局命名空間,主要用于提供關(guān)于blockchain的信息。
塊和交易屬性:
block.coinbase (address): :當(dāng)前塊的礦工的地址
block.difficulty (uint):當(dāng)前塊的難度系數(shù)
block.gaslimit (uint):當(dāng)前塊汽油限量
block.number (uint):當(dāng)前塊編號
block.blockhash (function(uint) returns (bytes32)):指定塊的哈希值——最新的256個(gè)塊的哈希值
block.timestamp (uint):當(dāng)前塊的時(shí)間戳
msg.data (bytes):完整的calldata
msg.gas (uint):剩余的汽油
msg.sender (address):消息的發(fā)送方(當(dāng)前調(diào)用)
msg.sig (bytes4):calldata的前四個(gè)字節(jié)(即函數(shù)標(biāo)識符)
msg.value (uint):所發(fā)送的消息中wei的數(shù)量
now (uint):當(dāng)前塊時(shí)間戳(block.timestamp的別名)
tx.gasprice (uint):交易的汽油價(jià)格
tx.origin (address):交易發(fā)送方(完整的調(diào)用鏈)
由于所有塊可伸縮性的原因,(所有)塊的hash值就拿不到,你只能訪問最近的256塊的hash值,其他值為零。
(11)數(shù)學(xué)和加密功能
addmod(uint x,uint y,uint z) return (uint) 計(jì)算(X+y)%z
mulmod(uint x,uint y,uint z) return (uint)計(jì)算(X*y)%z
加密數(shù)據(jù)
sha3(……) returns (byte32);
sha256(……) returns (byte32);
(12)自毀
selfdestruct(address);銷毀當(dāng)前合約,其資發(fā)送給指定的地址。此外,當(dāng)前合同的所有函數(shù)可以被直接調(diào)用(包括當(dāng)前函數(shù))
4.4、表達(dá)式和控制結(jié)構(gòu)
(1)控制結(jié)構(gòu):
除了 switch和goto,solidity的絕大多數(shù)控制結(jié)構(gòu)均來自于C / JavaScript,if, else, while, for, break, continue, return, ? :, 的語義均和C / JavaScript一樣。
條件語句中的括號不能省略,但在單條語句前后的花括號可以省略。
(2)函數(shù)調(diào)用
內(nèi)部函數(shù)調(diào)用:直接調(diào)用函數(shù);
外部函數(shù)調(diào)用:外外部調(diào)用函數(shù),參數(shù)必須存儲到內(nèi)存中,特別注意的是合約構(gòu)造函數(shù)中不能用this調(diào)用函數(shù)。因?yàn)楫?dāng)前合約還沒有創(chuàng)建。如果直接調(diào)用合約名,而不實(shí)例化,那么合約并不執(zhí)行構(gòu)造函數(shù),
具名參數(shù)調(diào)用:可以直接調(diào)用,按照順序輸入值,也可以({參數(shù):值,})
函數(shù)返回可以返回多個(gè)類型不一樣的值。支持返回元組類型。
(3)異常
有一些自動拋出異常的情況(見下文)。您可以使用throw 指令手動拋出一個(gè)異常。異常的影響是當(dāng)前執(zhí)行的調(diào)用被停止和恢復(fù)(即所有狀態(tài)和余額的變化均沒有發(fā)生)。另外, 異常也可以通過Solidity 函數(shù) “冒出來”, (一旦“異常”發(fā)生, 就send "exceptions", call和callcode底層函數(shù)就返回false)。
捕獲異常是不可能的。
目前,Solidity異常自動發(fā)生,有三種情況,:如果你訪問數(shù)組超出其長度 (即x[i]wherei >= x.length);如果一個(gè)通過消息調(diào)用的函數(shù)沒有正確的執(zhí)行結(jié)束(即gas用完,或本身拋出異常);如果一個(gè)庫里不存在的函數(shù)被調(diào)用,或Ether被發(fā)送到一個(gè)函數(shù)庫里。
異常還會通過solidity的函數(shù)調(diào)用向上冒泡(bubbled up)傳遞。(send,和底層的函數(shù)調(diào)用call,delegatecall,callcode是一個(gè)例外,當(dāng)異常發(fā)生時(shí),這些函數(shù)返回false)。
通過assert判斷內(nèi)部條件是否達(dá)成,require驗(yàn)證輸入的有效性。這樣的分析工具,可以假設(shè)正確的輸入,減少錯(cuò)誤。這樣無效的操作碼將永遠(yuǎn)不會出現(xiàn)。(返回值是false)
5、合約
合約是面向?qū)ο笳Z言的類,他們持久存放在狀態(tài)變量和函數(shù)中,可以修改這些變量。合約在創(chuàng)建時(shí),構(gòu)造函數(shù)將被調(diào)用。
(1)可見性和修飾符
函數(shù)調(diào)用分為內(nèi)部調(diào)用和外部調(diào)用,內(nèi)部調(diào)用不創(chuàng)建一個(gè)真實(shí)的EVM調(diào)用,也稱為消息調(diào)用,外部調(diào)用需要創(chuàng)建一個(gè)真實(shí)的EVM調(diào)用。
函數(shù)修飾符有external、public、internal、private,缺省是public。對狀態(tài)變量而言,external是不可能的,默認(rèn)是internal。
external:外部函數(shù)是合約接口的一部分,這意味著他們可以從其他合約調(diào)用,也可以通過事務(wù)調(diào)用。如果在本合約調(diào)用external,要像外部合約調(diào)用這個(gè)函數(shù)一樣,不能直接調(diào)用。external和public 修飾函數(shù),外部函數(shù)都可以訪問,但是external更省gas。
public:公共函數(shù)是合約接口的一部分,可以通過內(nèi)部調(diào)用或通過消息調(diào)用。對公共狀態(tài)變量而言,會有的自動訪問修飾符的函數(shù)生成(如果一個(gè)狀態(tài)變量用public修飾,合約將會自動給這個(gè)變量生成get函數(shù),獲取這個(gè)變量,需要調(diào)用get方法)
internal:這些函數(shù)和狀態(tài)變量只能內(nèi)部訪問(即當(dāng)前合約或由它派生的合約),而不使用(關(guān)鍵字)this
private:私有函數(shù)和狀態(tài)變量僅僅在定義該合約中可見,在派生的合約中不可見。
注意:
在外部觀察者中,合約的內(nèi)部的各項(xiàng)均可見。用private僅僅防止其他合約來訪問和修改(該合約中)信息。但它對blockchain之外的整個(gè)世界仍然可見。
可見性說明符是放在狀態(tài)變量的類型之后,(也可以放在)參數(shù)列表和函數(shù)返回的參數(shù)列表之間。
(2)函數(shù)修飾符(modifier)
修飾符可以用來輕松改變函數(shù)的行為。列如,在執(zhí)行的函數(shù)之前自動檢查條件。他們是可繼承合約的屬性,也可被派生合約重寫。(可以封裝一些條件等等,或者封裝重復(fù)使用的代碼,一個(gè)函數(shù)可以使用多個(gè)修飾符,使用空格格開)
(3)常量(constant)
(4)回退函數(shù)(callback)
一個(gè)合約可以有一個(gè)匿名函數(shù)。若沒有其他函數(shù)和給定法人函數(shù)標(biāo)識符一致的話,該函數(shù)將沒有參數(shù),將執(zhí)行一個(gè)合約的調(diào)用(如果沒有提供數(shù)據(jù))。
1)當(dāng)合約接收一個(gè)普通的ether時(shí),函數(shù)將被執(zhí)行(沒有數(shù)據(jù))。在這樣一個(gè)情況下,幾乎沒有g(shù)as用于函數(shù)調(diào)用,所以調(diào)用回退函數(shù)是非常廉價(jià)的,這點(diǎn)非常重要。
2)當(dāng)合約收到ether時(shí)(沒有任何其它數(shù)據(jù)),這個(gè)函數(shù)也會被執(zhí)行。為了接收ether,回退功能必須標(biāo)記為應(yīng)付款。如果不存在此類功能,則合約無法通過正常交易接收ether。
3)一個(gè)沒有定義一個(gè)回退函數(shù)的合約。如果接收ether,會觸發(fā)異常,并返還ether(solidity v0.4.0開始)。所以合約要接收ether,必須實(shí)現(xiàn)回退函數(shù)。在部署合約到網(wǎng)絡(luò)前,保證透徹的測試你的回退函數(shù),來保證函數(shù)執(zhí)行的花費(fèi)控制在2300gas以內(nèi).
(5)事件
事件允許EVM寫日志功能的方便使用,進(jìn)而在dapp的用戶接口中用javascript順序調(diào)用,從而監(jiān)聽這些事件。
事件是合約可繼承的成員。當(dāng)他們調(diào)用時(shí),會在導(dǎo)致一些參數(shù)在事務(wù)日志上的存儲--在blockchain上的一種特殊的數(shù)據(jù)結(jié)構(gòu)。這些日志和合約的地址相關(guān)聯(lián),將納入blockchain中,存儲在block里以便訪問(在Frontier和Homestead里是永久存儲,但在Serenity里有些變化)。在合約內(nèi)部,日志和事件數(shù)據(jù)是不可訪問的(從創(chuàng)建該日志的合約里)
事件的參數(shù)中,如果參數(shù)被設(shè)置為indexed(最多只有三個(gè)參數(shù)可以設(shè)置indexed),來設(shè)置是否索引,設(shè)置索引后,可以允許通過這個(gè)參數(shù)來查找日志,甚至可以按特定的值過濾。
如果參數(shù)增加indexed,存儲到日志中的topic部分,如果事件參數(shù)對應(yīng)的參數(shù)值只是單個(gè)值, 就是正常的存儲(存儲達(dá)到日志的topic部分),如果事件參數(shù)對應(yīng)的參數(shù)值是一個(gè)數(shù)組類型,因?yàn)閿?shù)組類型這種復(fù)雜類型的長度不確定,先轉(zhuǎn)換成了哈希值。
如果參數(shù)不增加indexed,存儲到日志的data部分。
日志中存儲的不同的索引事件就叫不同的主題。比如,事件定義,event transfer(address indexed _from, address indexed _to, uint value)有三個(gè)主題,第一個(gè)主題為默認(rèn)主題,即事件簽名transfer(address,address,uint256),但如果是聲明為anonymous的事件,則沒有這個(gè)主題;另外兩個(gè)indexed的參數(shù)也會分別形成兩個(gè)主題,可以分別通過_from,_to主題來進(jìn)行過濾。如果數(shù)組,包括字符串,字節(jié)數(shù)據(jù)做為索引參數(shù),實(shí)際主題是對應(yīng)值的Keccak-256哈希值。
(6)繼承(is)
solidity支持多重繼承,除非合約時(shí)顯式給出的,所有的函數(shù)調(diào)用都是虛擬的,絕大多數(shù)派生函數(shù)可被調(diào)用。即使合約繼承了多個(gè)其他的。
子類可以訪問public、internal權(quán)限控制的變量和函數(shù)。在子類中可以訪問狀態(tài)變量,因?yàn)闋顟B(tài)變量默是internal的。
1)繼承支持傳參,如果父類構(gòu)造函數(shù)有參數(shù),繼承時(shí) is Base(1)
方法二:
contract Base{
uint a;
function Base(uint _a){
a = _a;
}
}
contract InheritParaModifier is Base{
function InheritParaModifier(uint _a) Base(_a * _a){}
function getBasePara() returns (uint){
return a;
}
}
View Code
如果要傳入到基類的是簡單的常量,第一種方式會更加簡潔。但如果傳入的參數(shù)與子類的輸入?yún)?shù)有關(guān),那么你應(yīng)該使用第二種方式,以獲取參數(shù)值。如果你同時(shí)使用了這兩種方式,后一種方式將最終生效。
2)多繼承會出現(xiàn)幾個(gè)問題,如菱形繼承問題(鉆石問題),所以多繼承要注意繼承的順序。同時(shí)如果繼承的合約中擁有相同名字不同類型的方法或者變量,會視為錯(cuò)誤。所以建議使用單繼承。
(7)抽象(沒有關(guān)鍵字,只是沒有方法體的合約會視為抽象合約)
如果一個(gè)合約從一個(gè)抽象合約里繼承,但卻沒實(shí)現(xiàn)所有函數(shù),那么它也是一個(gè)抽象合約。
(8) 接口(interface)
接口與抽象合約類似,與之不同的是,接口內(nèi)沒有任何函數(shù)是已實(shí)現(xiàn)的,同時(shí)還有如下限制: .不能繼承其它合約,或接口;不能定義構(gòu)造器 ;.不能定義變量 ;不能定義結(jié)構(gòu)體 ;.不能定義枚舉類。
(9)庫(library)
庫與合約類似,但它的目的是在一個(gè)指定的地址,且僅部署一次,然后通過EVM的特性DELEGATECALL來復(fù)用代碼。這意味著庫函數(shù)調(diào)用時(shí),它的代碼是在調(diào)用合約的上下文中執(zhí)行。使用this將會指向到調(diào)用合約,而且可以訪問調(diào)用合約的存儲(storage)。因?yàn)橐粋€(gè)合約是一個(gè)獨(dú)立的代碼塊,它僅可以訪問調(diào)用合約明確提供的狀態(tài)變量(state variables),否則除此之外,沒有任何方法去知道這些狀態(tài)變量。(調(diào)用同目錄下本地庫(./libraryName.sol))
對比普通合約來說,庫的限制: 無狀態(tài)變量(state variables)。 不能繼承或被繼承 不能接收ether。
庫的常見―”坑” msg.sender 的值msg.sender 的值將是調(diào)用庫函數(shù)的合約的值。 例如,如果 A 調(diào)用合約 B,B 內(nèi)部調(diào)用庫 C。在庫 C 庫的函數(shù)調(diào)用里,msg.sender 將是 合約 B 的地址。 表達(dá)式 LibraryName.functionName() 用 CALLCODE 完成外部函數(shù)調(diào)用, 它映射到一 個(gè)真正的EVM調(diào)用,就像otherContract.functionName() 或者 this.functionName()。 這種調(diào)用可以一級一級擴(kuò)展調(diào)用深度(最多 1024 級),把 msg.sender 存儲為當(dāng)前的調(diào) 用者,然后執(zhí)行庫合約的代碼,而不是執(zhí)行當(dāng)前的合約存儲。這種執(zhí)行方式是發(fā)生在一個(gè)完 全嶄新的內(nèi)存環(huán)境中,它的內(nèi)存類型將被復(fù)制,并且不能繞過引用。 轉(zhuǎn)移 Ether原則上使用 LibraryName.functionName.value(x)()來轉(zhuǎn)移 Ether。但若使用 CALLCODE,Ether 會在當(dāng)前合約里用完。
1)指令 using A for B;可用于附加庫函數(shù)(從庫A)到任何類型(B)。這些函數(shù)將收到一個(gè)作為第一個(gè)參數(shù)的對象(像Python中self變量)。
using A for *;,是指函數(shù)從庫A附加到任何類型。
在這兩種情況下,所有的函數(shù)將被附加,(即使那些第一個(gè)參數(shù)的類型與對象的類型不匹配)。該被調(diào)用函數(shù)的入口類型將被檢查,并進(jìn)行函數(shù)重載解析。
6、修飾符
修飾符
constant for state variables:不允許賦值(除了初始化),不占用存儲塊。
constant for functions:不允許改變狀態(tài)- 這個(gè)目前不是強(qiáng)制的。
anonymous for events:不能將topic作為事件指紋進(jìn)行存儲。
函數(shù)可以聲明view,在這種情況下他們保證不修改狀態(tài)。(以下聲明被視為修改狀態(tài): 1).寫入狀態(tài)變量。 2).發(fā)送事件。 3).創(chuàng)建其他合同。 4).使用selfdestruct。 5).通過電話發(fā)送以太。 6).調(diào)用任何未標(biāo)記為view或pure的函數(shù)。 7).使用低級別呼叫。 8).使用包含某些操作碼的內(nèi)聯(lián)匯編。.
)
函數(shù)可以聲明為pure,在這種情況下,它們無權(quán)讀取或者修改狀態(tài)。(除了上面解釋的狀態(tài)修改語句列表之外,以下內(nèi)容被認(rèn)為是從狀態(tài)中讀取的: 1).從狀態(tài)變量讀取 2).訪問this.balance或<address> .balance。 3).訪問塊,tx,msg的任何成員(msg.sig和msg.data除外)。 4).調(diào)用任何未標(biāo)記為pure的函數(shù)。 5).使用包含某些操作碼的內(nèi)聯(lián)匯編。
)
assert(bool condition);//用于判斷內(nèi)部錯(cuò)誤,條件不滿足時(shí)拋出異常
require(bool condition);//用于判斷條件或外部組件錯(cuò)誤,條件不滿足時(shí)拋出異常
revert();終止執(zhí)行并還原改變的狀態(tài)
7、雜項(xiàng)
(1)命名,私有函數(shù)和參數(shù)用_開始來命名。
總結(jié)
以上是生活随笔為你收集整理的solidity基础知识的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。