Solidity文檔(中文版)連載六:?jiǎn)挝缓腿肿兞?/h1>

序言
本文是 Solidity 文檔(以太坊官方 Solidity 開(kāi)發(fā)手冊(cè))中文版連載的第六部分。這個(gè)連載的前五部分是智能合約概述安裝 Solidity 編譯器結(jié)合實(shí)例學(xué)習(xí) Solidity源文件結(jié)構(gòu)類(lèi)型扣猫。
這份文檔的英文原文可以在以太坊官網(wǎng)的最下方 Solidity 鏈接中找到。官方的英文版本文檔中有中譯版鏈接睁蕾,即是本連載內(nèi)容的出處。這個(gè)連載將按照英文文檔的先后順序進(jìn)行债朵。
Solidity 是以太坊官方的智能合約開(kāi)發(fā)高級(jí)語(yǔ)言子眶。這份中文譯本是由 Hiblock 社區(qū)組織貢獻(xiàn)的,官方 Github:https://github.com/etherchina/solidity-doc-cn序芦。
我本人于 3 月初加入本項(xiàng)目臭杰,目前作為管理員、貢獻(xiàn)者和校訂人利用業(yè)余時(shí)間參與日常工作谚中;截至到 5 月底渴杆,翻譯工作已接近完成。有興趣的朋友請(qǐng)直接在以太坊官網(wǎng)的鏈接中查看最新中文版本狀態(tài)宪塔,或者關(guān)注上述中文譯本的 Github repository磁奖。
出于單獨(dú)閱讀的需要,我在連載中會(huì)刪除原文里的 rst 控制標(biāo)簽某筐、部分外部鏈接和文內(nèi)鏈接比搭。
本文是對(duì) Solidity 中的單位和全局變量的完整介紹。

單位和全局變量

以太幣單位

以太幣單位之間的換算就是在數(shù)字后邊加上 wei南誊、 finney身诺、 szaboether 來(lái)實(shí)現(xiàn)的,如果后面沒(méi)有單位抄囚,缺省為 Wei霉赡。例如 2 ether == 2000 finney 的邏輯判斷值為 true

時(shí)間單位

秒是缺省時(shí)間單位幔托,在時(shí)間單位之間穴亏,數(shù)字后面帶有 secondsminutes重挑、 hours迫肖、 daysweeksyears 的可以進(jìn)行換算攒驰,基本換算關(guān)系如下:

  • 1 == 1 seconds
  • 1 minutes == 60 seconds
  • 1 hours == 60 minutes
  • 1 days == 24 hours
  • 1 weeks == 7 days
  • 1 years == 365 days

由于閏秒造成的每年不都是 365 天蟆湖、每天不都是 24 小時(shí),所以如果你要使用這些單位計(jì)算日期和時(shí)間玻粪,請(qǐng)注意這個(gè)問(wèn)題隅津。因?yàn)殚c秒是無(wú)法預(yù)測(cè)的诬垂,所以需要借助外部的預(yù)言機(jī)(oracle,是一種鏈外數(shù)據(jù)服務(wù)伦仍,譯者注)來(lái)對(duì)一個(gè)精確的日期庫(kù)進(jìn)行更新结窘。

注意:
基于上述原因 years 前綴已經(jīng)不推薦使用了。

這些后綴不能直接用在變量后邊充蓝。如果想用時(shí)間單位(例如 days)來(lái)將輸入變量換算為時(shí)間隧枫,你可以用如下方式來(lái)完成:

function f(uint start, uint daysAfter) public {
    if (now >= start + daysAfter * 1 days) {
        // ...
    }
}

特殊變量和函數(shù)

在全局命名空間中已經(jīng)存在了(預(yù)設(shè)了)一些特殊的變量和函數(shù),他們主要用來(lái)提供關(guān)于區(qū)塊鏈的信息谓苟。

區(qū)塊和交易屬性

  • block.blockhash(uint blockNumber) returns (bytes32): 給定區(qū)塊的哈瞎倥В—僅對(duì)最近的 256 個(gè)區(qū)塊有效而不包括當(dāng)前區(qū)塊
  • block.coinbase (address): 挖出當(dāng)前區(qū)塊的礦工地址
  • block.difficulty (uint): 當(dāng)前區(qū)塊難度
  • block.gaslimit (uint): 當(dāng)前區(qū)塊 gas 限額
  • block.number (uint): 當(dāng)前區(qū)塊號(hào)
  • block.timestamp (uint): 自 unix epoch 起始當(dāng)前區(qū)塊以秒計(jì)的時(shí)間戳
  • gasleft() returns (uint256):剩余的 gas
  • msg.data (bytes): 完整的 calldata
  • msg.gas (uint): 剩余 gas
  • msg.sender (address): 消息發(fā)送者(當(dāng)前調(diào)用)
  • msg.sig (bytes4): calldata 的前 4 字節(jié)(也就是函數(shù)標(biāo)識(shí)符)
  • msg.value (uint): 隨消息發(fā)送的 wei 的數(shù)量
  • now (uint): 目前區(qū)塊時(shí)間戳(block.timestamp
  • tx.gasprice (uint): 交易的 gas 價(jià)格
  • tx.origin (address): 交易發(fā)起者(完全的調(diào)用鏈)

注意:
對(duì)于每一個(gè)外部函數(shù)調(diào)用,包括 msg.sendermsg.value 在內(nèi)所有 msg 成員的值都會(huì)變化涝焙。這里包括對(duì)庫(kù)函數(shù)的調(diào)用卑笨。

注意:
不要依賴(lài) block.timestampnowblock.blockhash 產(chǎn)生隨機(jī)數(shù)仑撞,除非你知道自己在做什么赤兴。

時(shí)間戳和區(qū)塊哈希在一定程度上都可能受到挖礦礦工影響。例如隧哮,挖礦社區(qū)中的惡意礦工可以用某個(gè)給定的哈希來(lái)運(yùn)行賭場(chǎng)合約的 payout 函數(shù)桶良,而如果他們沒(méi)收到錢(qián),還可以用一個(gè)不同的哈希重新嘗試沮翔。

當(dāng)前區(qū)塊的時(shí)間戳必須嚴(yán)格大于最后一個(gè)區(qū)塊的時(shí)間戳艺普,但這里唯一能確保的只是它會(huì)是在權(quán)威鏈上的兩個(gè)連續(xù)區(qū)塊的時(shí)間戳之間的數(shù)值。

注意:
基于可擴(kuò)展因素鉴竭,區(qū)塊哈希不是對(duì)所有區(qū)塊都有效歧譬。你僅僅可以訪問(wèn)最近 256 個(gè)區(qū)塊的哈希,其余的哈希均為零搏存。

ABI 編碼函數(shù)

  • abi.encode(...) returns (bytes):返回給定參數(shù)的 ABI 編碼
  • abi.encodePacked(...) returns (bytes):對(duì)給定參數(shù)進(jìn)行打包的編碼
  • abi.encodeWithSelector(bytes4 selector, ...) returns (bytes):對(duì)從第二個(gè)參數(shù)開(kāi)始的給定參數(shù)進(jìn)行 ABI 編碼瑰步,并以第一個(gè)參數(shù)作為返回結(jié)果的前 4 字節(jié)——即用第一個(gè)參數(shù)作為函數(shù)選擇器(function selector),僅對(duì)其余參數(shù)進(jìn)行 ABI 編碼
  • abi.encodeWithSignature(string signature, ...) returns (bytes):等價(jià)于 ``abi.encodeWithSelector(bytes4(keccak256(signature), ...)```

注意:
這些編碼函數(shù)可以用來(lái)基于函數(shù)調(diào)用的數(shù)據(jù)來(lái)產(chǎn)生 ABI 編碼璧眠,而不會(huì)實(shí)際調(diào)用一個(gè)函數(shù)缩焦。此外,keccak256(abi.encodePacked(a, b)) 是計(jì)算 keccak256(a, b) 的更明確的方式责静,后者在未來(lái)的版本中不再推薦使用袁滥。

關(guān)于 ABI 編碼的詳情請(qǐng)參考后續(xù)的“應(yīng)用二進(jìn)制編碼(ABI)說(shuō)明”一章。

錯(cuò)誤處理

  • assert(bool condition):如果條件不滿(mǎn)足則是交易不產(chǎn)生實(shí)際效果——用于內(nèi)部錯(cuò)誤灾螃。
  • require(bool condition):如果條件不滿(mǎn)足則恢復(fù)(revert)——用于輸入或者外部組件引起的錯(cuò)誤题翻。
  • require(bool condition, string message):如果條件不滿(mǎn)足則恢復(fù)(revert)——用于輸入或者外部組件引起的錯(cuò)誤,同時(shí)提供一個(gè)錯(cuò)誤消息腰鬼。
  • revert():終止運(yùn)行并恢復(fù)(revert)狀態(tài)(state)變動(dòng)嵌赠。
  • revert(string reason):終止運(yùn)行并恢復(fù)(revert)狀態(tài)(state)變動(dòng)塑荒,并提供一個(gè)字符串信息來(lái)解釋原因。

數(shù)學(xué)和密碼學(xué)函數(shù)

  • addmod(uint x, uint y, uint k) returns (uint):計(jì)算 (x + y) % k姜挺,加法會(huì)在任意精度下執(zhí)行齿税,并且加法的結(jié)果即使超過(guò) 2**256 也不會(huì)被截取。從 0.5.0 版本的編譯器開(kāi)始會(huì)加入對(duì) k != 0 的校驗(yàn)(assert)炊豪。
  • mulmod(uint x, uint y, uint k) returns (uint):計(jì)算 (x * y) % k凌箕,乘法會(huì)在任意精度下執(zhí)行,并且乘法的結(jié)果即使超過(guò) 2**256 也不會(huì)被截取词渤。從 0.5.0 版本的編譯器開(kāi)始會(huì)加入對(duì) k != 0 的校驗(yàn)(assert)牵舱。
  • keccak256(...) returns (bytes32):計(jì)算“緊打包”參數(shù)(需要參考后續(xù)的“應(yīng)用二進(jìn)制編碼說(shuō)明”一章)的 Ethereum-SHA-3 (Keccak-256)哈希。
  • sha256(...) returns (bytes32):計(jì)算“緊打包”參數(shù)(需要參考后續(xù)的“應(yīng)用二進(jìn)制編碼說(shuō)明”一章)的 SHA-256 哈希掖肋。
  • sha3(...) returns (bytes32):等價(jià)于 keccak256仆葡。
  • ripemd160(...) returns (bytes20):計(jì)算“緊打包”參數(shù)(需要參考后續(xù)的“應(yīng)用二進(jìn)制編碼說(shuō)明”一章)的 RIPEMD-160 哈希赏参。
  • ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):利用橢圓曲線(xiàn)簽名恢復(fù)與公鑰相關(guān)的地址志笼,錯(cuò)誤返回零值。參考示例把篓。

上文中的“緊打包(tightly packed)”是指不會(huì)對(duì)參數(shù)值進(jìn)行 padding 處理(就是說(shuō)所有參數(shù)值的字節(jié)碼是連續(xù)存放的纫溃,中間不保留為把長(zhǎng)度補(bǔ)充為一個(gè)“字”,即 32 字節(jié)韧掩,而追加的若干 0 值字節(jié)數(shù)據(jù)紊浩,譯者注),這意味著下邊這些調(diào)用都是等價(jià)的:

keccak256("ab", "c")
keccak256("abc")
keccak256(0x616263)
keccak256(6382179)
keccak256(97, 98, 99)

如果需要 padding疗锐,可以使用顯式類(lèi)型轉(zhuǎn)換:keccak256("\x00\x12")keccak256(uint16(0x12)) 是一樣的坊谁。

請(qǐng)注意,常量值會(huì)使用存儲(chǔ)它們所需要的最少字節(jié)數(shù)進(jìn)行打包滑臊。例如:keccak256(0) == keccak256(uint8(0))口芍,keccak256(0x12345678) == keccak256(uint32(0x12345678))

在一個(gè)私鏈上雇卷,你很有可能碰到由于 sha256鬓椭、ripemd160 或者 ecrecover 引起的 gas 用盡(Out-of-Gas)問(wèn)題。這個(gè)原因就是他們被當(dāng)做所謂的預(yù)編譯合約而執(zhí)行关划,并且在第一次收到消息后這些合約才真正存在(盡管合約代碼是硬編碼)小染。發(fā)送到不存在的合約的消息非常昂貴,所以實(shí)際的執(zhí)行會(huì)導(dǎo)致 Out-of-Gas 錯(cuò)誤贮折。在你的合約中實(shí)際使用它們之前裤翩,給每個(gè)合約發(fā)送一點(diǎn)兒以太幣,比如 1 Wei调榄。這在官方網(wǎng)絡(luò)或測(cè)試網(wǎng)絡(luò)上都不是問(wèn)題岛都。

地址相關(guān)

  • <address>.balance (uint256):以 Wei 為單位的某個(gè)地址(address)的余額律姨。
  • <address>.transfer(uint256 amount):向某個(gè)地址(address)發(fā)送數(shù)量為 amount 的 Wei,失敗時(shí)拋出異常臼疫,且將額外發(fā)送 2300 gas 的礦工費(fèi)择份,不可調(diào)整。
  • <address>.send(uint256 amount) returns (bool):向某個(gè)地址(address)發(fā)送數(shù)量為 amount 的 Wei烫堤,失敗時(shí)返回 false荣赶,且將額外發(fā)送 2300 gas 的礦工費(fèi)用,不可調(diào)整鸽斟。
  • <address>.call(...) returns (bool):執(zhí)行低級(jí)函數(shù) CALL拔创,失敗時(shí)返回 false,會(huì)發(fā)送所有可用 gas富蓄,不可調(diào)整剩燥。
  • <address>.callcode(...) returns (bool):執(zhí)行低級(jí)函數(shù) CALLCODE,失敗時(shí)返回 false立倍,會(huì)發(fā)送所有可用 gas灭红,不可調(diào)整。
  • <address>.delegatecall(...) returns (bool):執(zhí)行低級(jí)函數(shù) DELEGATECALL口注,失敗時(shí)返回 false变擒,會(huì)發(fā)送所有可用 gas,不可調(diào)整寝志。

更多信息娇斑,請(qǐng)參考上一章中介紹的“地址”部分。

警告:
使用 send 有很多危險(xiǎn):如果調(diào)用棧深度已經(jīng)達(dá)到 1024(這總是可以由調(diào)用者所強(qiáng)制指定)材部,轉(zhuǎn)賬會(huì)失敽晾隆;并且如果接收者用光了 gas乐导,轉(zhuǎn)賬同樣會(huì)失敗苦丁。為了保證以太幣轉(zhuǎn)賬安全,總是檢查 send 的返回值兽叮,利用 transfer 或者后文中的更好的方式:
使用一種由接收者取回資金的模式芬骄。

注意:
如果通過(guò)一個(gè)低級(jí)函數(shù) delegatecall 來(lái)訪問(wèn)一個(gè)存儲(chǔ)(storage)變量,兩個(gè)合約存儲(chǔ)中的數(shù)據(jù)布局(位置)必須一致鹦聪,以保證可以在被調(diào)用的合約中通過(guò)變量名正確地訪問(wèn)到調(diào)用合約中的存儲(chǔ)變量账阻。這當(dāng)然不是在高級(jí)的庫(kù)中通過(guò)函數(shù)參數(shù)傳遞存儲(chǔ)指針的那種情況。

注意:不鼓勵(lì)使用 callcode泽本,并且將來(lái)它會(huì)被移除淘太。

合約相關(guān)

  • this (current contract's type):當(dāng)前合約,可以明確轉(zhuǎn)換為某個(gè)地址(address)。
  • selfdestruct(address recipient):銷(xiāo)毀合約蒲牧,并把余額發(fā)送到指定的地址(address)撇贺。
  • suicide(address recipient):等價(jià)于 selfdestruct。

此外冰抢,當(dāng)前合約內(nèi)的所有函數(shù)都可以被直接調(diào)用松嘶,包括當(dāng)前函數(shù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末挎扰,一起剝皮案震驚了整個(gè)濱河市翠订,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌遵倦,老刑警劉巖尽超,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異梧躺,居然都是意外死亡似谁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)掠哥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)巩踏,“玉大人,你說(shuō)我怎么就攤上這事龙致≈欤” “怎么了顷链?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵目代,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我嗤练,道長(zhǎng)榛了,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任煞抬,我火速辦了婚禮霜大,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘革答。我一直安慰自己战坤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布残拐。 她就那樣靜靜地躺著途茫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪溪食。 梳的紋絲不亂的頭發(fā)上囊卜,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼栅组。 笑死雀瓢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的玉掸。 我是一名探鬼主播刃麸,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼司浪!你這毒婦竟也來(lái)了嫌蚤?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤断傲,失蹤者是張志新(化名)和其女友劉穎脱吱,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體认罩,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡箱蝠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了垦垂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宦搬。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖劫拗,靈堂內(nèi)的尸體忽然破棺而出间校,到底是詐尸還是另有隱情,我是刑警寧澤页慷,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布憔足,位于F島的核電站,受9級(jí)特大地震影響酒繁,放射性物質(zhì)發(fā)生泄漏滓彰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一州袒、第九天 我趴在偏房一處隱蔽的房頂上張望揭绑。 院中可真熱鬧,春花似錦郎哭、人聲如沸他匪。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)邦蜜。三九已至,卻和暖如春陈惰,著一層夾襖步出監(jiān)牢的瞬間畦徘,已是汗流浹背毕籽。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留井辆,地道東北人关筒。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像杯缺,于是被迫代替她去往敵國(guó)和親蒸播。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容