本文默認讀者已掌握至少一種面向?qū)ο缶幊陶Z言,所以文中一些概念會借助其他語言進行類比。
在滕州等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站設(shè)計、成都網(wǎng)站制作、外貿(mào)網(wǎng)站建設(shè) 網(wǎng)站設(shè)計制作定制制作,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),網(wǎng)絡(luò)營銷推廣,成都外貿(mào)網(wǎng)站制作,滕州網(wǎng)站建設(shè)費用合理。solidity是用于實現(xiàn)智能合約的一種面向合約的高級編程語言,solidity受到C++、Python和JavaScript的影響,被設(shè)計為可運行在以太坊虛擬機(EVM)上,所以用戶無需擔(dān)心代碼的可移植性和跨平臺等問題。solidity是一種靜態(tài)類型的語言,支持繼承、庫引用等特性,并且用戶可自定義復(fù)雜的結(jié)構(gòu)類型。
目前嘗試 Solidity 編程的最好的方式是使用 Remix (由于是網(wǎng)頁IDE可能加載起來需要一定的時間)。Remix 是一個基于 Web 的 IDE,它可以讓你編寫 Solidity 智能合約,然后部署并運行該智能合約,它看起來是這樣子的:
也可以使用sublime或vs code等編輯器編寫 Solidity 代碼,然后復(fù)制粘貼到Remix上部署運行。
solidity官網(wǎng)地址如下:
https://solidity.readthedocs.io/en/latest/index.html
本小節(jié)我們來說說合約文件,眾所周知任何語言所編寫的代碼都需要存儲在一個文件里,并且該文件都有一個特定的后綴名,我們一般將這種文件稱之為代碼文件。
solidity代碼文件的后綴名為.sol
,但我們通常會把使用solidity編寫的文件稱之為合約文件,一個合約文件通常會包含四個部分,其實與我們平時所編寫其他語言的代碼文件是類似的,如下圖所示:
版本聲明的代碼需寫在合約文件的開頭,接著可以根據(jù)實際情況導(dǎo)入一些合約,所謂導(dǎo)入合約也就類似于其他面向?qū)ο蟮恼Z言導(dǎo)入某個類的概念。然后就是聲明一個合約,在合約里編寫具體的代碼,其實這里的合約與我們所熟悉的類的概念基本上是一樣的,可以暫時將它們當做同一個東西。
我們先來對一個較為完整的合約代碼進行一個預(yù)覽,在之后會對代碼中的每個部分進行逐一介紹:
// 版本聲明
pragma solidity ^0.4.0;
// 導(dǎo)入一個合約
import "solidity_for_import.sol";
// 定義一個合約
contract ContractTest {
// 定義一個無符號整型變量
uint a;
// 定義一個事件
event Set_A(uint a);
// 定義一個函數(shù)
function setA(uint x) public {
a = x;
// 觸發(fā)一個事件
emit Set_A(x);
}
// 定義一個具有返回值的函數(shù)
function getA() public returns (uint) {
return a;
}
// 自定義一個結(jié)構(gòu)類型
struct Pos {
// 定義一個有符號整型變量
int lat;
int lng;
}
// 定義一個地址類型,每個合約都運行在一個特定的地址上
address public addr;
// 定義一個函數(shù)修改器
modifier owner () {
require(msg.sender == addr);
_;
}
// 讓函數(shù)使用函數(shù)修改器
function mine() public owner {
a += 1;
}
}
這里對函數(shù)修改器做一個簡單的說明:
函數(shù)修改器的概念類似于python中的裝飾器,其核心目的都是給函數(shù)增加函數(shù)內(nèi)沒有定義的功能,也就是對函數(shù)進行增強
從以上代碼中,可以看到owner
函數(shù)修改器里定義了一句條件代碼,其意義為:
當
msg.sender
等于addr
地址變量時,才繼續(xù)往下執(zhí)行,因為這個require函數(shù)是solidity校驗條件用的,若不符合條件就會拋出異常
mine函數(shù)使用了owner函數(shù)修改器后,那么mine函數(shù)在執(zhí)行之前,會先執(zhí)行owner函數(shù)修改器里的條件代碼,也就是說當msg.sender
等于addr
成立的話,才會執(zhí)行mine函數(shù)里a += 1;
的代碼,否則就不會執(zhí)行。從中也可以看出函數(shù)修改器里的_;
語句,其實表示的就是mine函數(shù)里的代碼,如此一來在不修改mine函數(shù)的前提下,給mine函數(shù)增加了額外的功能。
Solidity是一種靜態(tài)類型語言,意味著每個變量(本地或狀態(tài)變量)需要在編譯時指定變量的類型(或至少可以推導(dǎo)出類型),Solidity提供了一些基本類型可以用來組合成復(fù)雜類型。
Solidity和大多數(shù)語言一樣,有兩種類型:
solidity所包含的值類型如下:
注:其中標紅的是最常用的類型
官網(wǎng)關(guān)于solidity類型的文檔地址如下:
https://solidity.readthedocs.io/en/latest/types.html
1.布爾類型取值范圍是true和false,使用bool關(guān)鍵字進行聲明,聲明方式如下:
// 版本聲明
pragma solidity ^0.4.0;
// 定義一個合約
contract ContractTest {
bool b1 = true;
bool b2 = false;
}
2.solidity中有兩種整型的定義方式,一種是無符號整型,另一種則是有符號整型。并且支持關(guān)鍵字uint8 到 uint256 (以8步進),uint 和 int 默認對應(yīng)的是 uint256 和 int256。如下示例:
// 版本聲明
pragma solidity ^0.4.0;
// 定義一個合約
contract ContractTest {
// 定義一個無符號的整型變量
uint a;
// 定義一個有符號的整型變量
int i;
}
在solidity里使用constant關(guān)鍵字來聲明常量,但并非所有的類型都支持常量,當前支持的僅有值類型和字符串:
pragma solidity ^0.4.0;
contract C {
uint constant x = 32**22 + 8;
string constant text = "abc";
bytes32 constant myHash = keccak256("abc");
}
在solidity中還可以將函數(shù)聲明為常量,該函數(shù)的返回值就是常量值,這類函數(shù)將承諾自己不修改區(qū)塊鏈上任何狀態(tài):
// 定義有理數(shù)常量
function testLiterals() public constant returns (int) {
return 1;
}
// 定義字符串常量
function testStringLiterals() public constant returns (string) {
return "string";
}
// 定義16進制常量,以關(guān)鍵字hex打頭,后面緊跟用單或雙引號包裹的字符串,內(nèi)容是十六進制字符串
function testHexLiterals() public constant returns (bytes2) {
return hex"abcd";
}
有理數(shù)常量函數(shù)里的運算可以是任意精度的,不會有溢出的問題:
// 定義有理數(shù)常量
function testLiterals() public constant returns (int) {
return 1859874861811128585416.0 + 123.0;
}
科學(xué)符號也支持,基數(shù)可以是小數(shù),但指數(shù)必須是整數(shù),如下:
// 定義有理數(shù)常量
function testLiterals() public constant returns (int) {
return 2e10;
}
solidity中使用address關(guān)鍵字聲明地址類型變量,該類型屬于值類型,地址類型主要用于表示一個賬戶地址,一個以太坊地址的長度為20字節(jié)的16進制數(shù),地址類型也有成員,地址是所有合約的基礎(chǔ)。
地址類型的主要成員:
代碼示例如下:
pragma solidity ^0.4.7;
contract AddrTest {
// payable關(guān)鍵字定義一個可接受以太幣的函數(shù)
function deposit() public payable {
}
// 查詢賬戶余額
function getBalance() public constant returns (uint) {
return this.balance;
}
// 轉(zhuǎn)移以太幣
function transferEther(address towho) public {
towho.transfer(10);
}
}
然后我們將這段代碼復(fù)制粘貼到remix中編譯運行看看,首先需要在Compile選項卡中將代碼進行編譯:
編譯成功后,到Run選項卡中,部署該合約:
部署成功后,可以查看到合約中的各個函數(shù),并且只需要點擊就可以運行指定的函數(shù):
此時我們來點擊執(zhí)行一下getBalance函數(shù):
可以看到,此時該合約的賬戶余額為0,現(xiàn)在我們來存儲10個wei的以太幣到合約中:
此時再執(zhí)行g(shù)etBalance函數(shù),合約余額為10個wei:
然后我們再來看看轉(zhuǎn)移/發(fā)送以太幣的transferEther函數(shù),此時我們這個合約地址的余額為10個wei,當我將這10個wei的以太轉(zhuǎn)移到另一個地址后,當前合約的余額為0:
在solidity中一個能通過地址合法性檢查(address checksum test)的十六進制常量就會被認為是地址,如:
0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF
而不能通過地址合法性檢查的39到41位長的十六進制常量,會提示一個警告,被視為普通的有理數(shù)常量。
關(guān)于賬戶地址的合法性檢查定義參考如下提案:
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
在上文中我們提到Solidity 類型分為值類型和引用類型,以上小節(jié)介紹了常見的值類型,接下來會介紹一下引用類型。
引用類型是一個復(fù)雜類型,占用的空間通常超過256位, 拷貝時開銷很大,因此我們需要考慮將它們存儲在什么位置,是存儲在memory(內(nèi)存,數(shù)據(jù)不是永久存在)中還是存儲在storage(永久存儲在區(qū)塊鏈)中。
所有的復(fù)雜類型如數(shù)組和結(jié)構(gòu)體都有一個額外的屬性:數(shù)據(jù)的存儲位置(data location),可為memory和storage。根據(jù)上下文的不同,大多數(shù)時候數(shù)據(jù)存儲的位置有默認值,也可以通過指定關(guān)鍵字storage和memory修改它。
函數(shù)參數(shù)(包含返回的參數(shù))默認是memory。而局部復(fù)雜類型變量(local variables)和狀態(tài)變量(state variables) 默認是storage。局部變量即部作用域(越過作用域即不可被訪問,等待被回收)的變量,如函數(shù)內(nèi)的變量,狀態(tài)變量則是合約內(nèi)聲明的公有變量。
除此之外,還有一個存儲位置是:calldata,用來存儲函數(shù)參數(shù),是只讀的,不會永久存儲的一個數(shù)據(jù)位置。外部函數(shù)的參數(shù)(不包括返回參數(shù))被強制指定為calldata。效果與memory差不多。還有一個存儲位置是:calldata,用來存儲函數(shù)參數(shù),是只讀的,不會永久存儲的一個數(shù)據(jù)位置。外部函數(shù)的參數(shù)(不包括返回參數(shù))被強制指定為calldata。效果與memory差不多。
數(shù)組是一種典型的引用類型,在solidity中數(shù)組的定義方式如下:
數(shù)組類型有兩個主要成員:
具體的示例代碼如下:
pragma solidity ^0.4.7;
contract ArrayTest {
// 定義一個無符號整型的變長數(shù)組
uint[] public numbers = [1, 2, 3];
// 定義一個字符串
string str = "abcdefg";
function getNumbersLength() public returns (uint) {
// 往數(shù)組中添加一個元素
numbers.push(4);
// 返回數(shù)組的長度
return numbers.length;
}
function getStrLength() public constant returns (uint) {
// 將字符串轉(zhuǎn)換為bytes并返回長度
return bytes(str).length;
}
function getFirst() public constant returns (byte) {
// 將字符串轉(zhuǎn)換為bytes后,通過下標訪問元素
return bytes(str)[0];
}
function newMemory(uint len) public constant returns (uint) {
// 定義一個定長數(shù)組并通過memory指定數(shù)組的存儲位置
uint[] memory memoryArr = new uint[] (len);
return memoryArr.length;
}
function changeFirst(uint[3] _data) public constant returns (uint[3]) {
// 通過索引操作元素
_data[0] = 0;
return _data;
}
}
Solidity提供struct關(guān)鍵字來定義自定義類型也就是結(jié)構(gòu)體,自定義的類型屬于引用類型,如果學(xué)習(xí)過go語言的話應(yīng)該對其不會陌生。如下示例:
// 版本聲明
pragma solidity ^0.4.7;
// 定義一個合約
contract ContractTest {
// 聲明一個結(jié)構(gòu)體
struct Funder {
address addr;
uint amount;
}
// 將自定義的結(jié)構(gòu)體聲明為狀態(tài)變量
Funder funder;
// 使用結(jié)構(gòu)體
function newFunder() public {
funder = Funder({addr: msg.sender, amount: 10});
}
}
solidity擁有映射類型,映射類型是一種鍵值對的映射關(guān)系存儲結(jié)構(gòu),有點類似于python語言中的字典。定義方式為mapping(_KeyType => _KeyValue)
。鍵類型允許除映射、變長數(shù)組、合約、枚舉、結(jié)構(gòu)體外的幾乎所有類型值類型沒有任何限制,可以為任何類型包括映射類型。
映射可以被視作為一個哈希表,所有可能的鍵會被虛擬化的創(chuàng)建,映射到一個類型的默認值(二進制的全零表示)。在映射表中,并不存儲鍵的數(shù)據(jù),僅僅存儲它的keccak256哈希值,這個哈希值在查找值時需要用到。正因為此,映射是沒有長度的,也沒有鍵集合或值集合的概念。
映射類型有一點比較特殊,它僅能用來作為狀態(tài)變量,或在內(nèi)部函數(shù)中作為storage類型的引用。
可以通過將映射標記為public,來讓Solidity創(chuàng)建一個訪問器。通過提供一個鍵值做為參數(shù)來訪問它,將返回對應(yīng)的值。映射的值類型也可以是映射,使用訪問器訪問時,要提供這個映射值所對應(yīng)的鍵,不斷重復(fù)這個過程。
示例代碼如下:
// 版本聲明
pragma solidity ^0.4.7;
// 定義一個合約
contract ContractTest {
// 定義一個映射類型,key類型為address,value類型為uint
mapping(address => uint) public balances;
function updateBalance(uint newBalance) public {
// msg.sender作為鍵,newBalance作為值,將這對鍵值添加到該映射中
balances[msg.sender] = newBalance;
}
}
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。