Solidity語言學習(7) —— 單位和全域性變數
以太幣單位
以太幣單位之間的換算就是在數字後邊加上 wei、finney、 szabo、或ether來實現的,如果後面沒有單位,預設為Wei。例如 2ether == 200finney 的邏輯判斷為true。
時間單位
秒是預設時間單位,在時間單位之間,數字後面帶有 seconds、minutes、hours、days、weeks和years的可以進行換算。
由於閏秒造成的每年不都是365天,每天不都是24小時,所以如果要使用這些單位計算日期和事件,需要注意這個問題。因為閏秒是無法預測的,所以需要藉助外部的語言及(oracle,是一種鏈外資料服務)來對一個精確的日期庫進行更新。
note:
基於上述原因 years 字首已經不再推薦使用了。
這些字尾不能直接用在變數後邊。如果想用時間單位(例如 days)來將輸入變數換算為時間,你可以用如下方式來完成:
function f(uint start, uint daysAfter) public {
if (now >= start + daysAfter * 1 days) {
//...
}
}
特殊變數和函式
在全域性名稱空間中已經存在了(預設了)一些特殊的變數和函式,他們主要用來提供關於區塊鏈的資訊。
區塊和交易屬性
block.blockhash(uint blockNumber) returns (butes32)
block.coinbase (address)
: 挖出當前區塊的礦工地址(括號裡的不是引數而是該值的型別,下同)block.difficulty (uint)
:當前區塊難度block.gaslimit (uint)
: 當前區塊的gas限額block.number (uint)
:當前區塊號block.timestamp (uint)
: 自 unix epoch起始當前區塊以秒記的時間戳gasleft() returns(uint256)
:剩餘的gasmsg.data (bytes)
msg.gas (uint)
:剩餘的gasmsg.sender (address)
:訊息傳送者(當前呼叫)msg.sig (uint)
: calldata的前四位元組(也就是函式識別符號)msg.value (uint)
:隨訊息傳送的 wei 數量now (uint)
:目前區塊時間戳(block.timestamp
)tx.gasprice (uint)
: 交易的 gas 價格tx.origin (address)
: 交易發起者(完全的呼叫鏈)
注意:
1.對於每一個 外部函式 呼叫,包括msg.sender
和msg.value
在內所有 msg 成員都會變化。這裡包括對庫函式的呼叫。
2.不要依賴 block.timestamp、now 和 block.blockhash(uint blockNumber) 產生隨機數,除非你知道自己在做什麼。
時間戳和區塊雜湊在一定程度上都可能收到挖礦礦工的影響。比如說,挖礦社群中的惡意礦工可以用某個給定的雜湊來執行賭場合約中的payout函式,而如果他們沒收到錢,還可以用一個不同的雜湊重新嘗試。
當前區塊的時間戳必須嚴格大於最後一個區塊的時間戳,但這裡唯一能確保的只是它會是在權威鏈上的兩個連續區塊的時間戳之間的數值
3.基於可擴充套件的因素,區塊雜湊不是對所有區塊都有效。你僅僅可以訪問最近的256個區塊的雜湊,其餘的雜湊均為0
ABI編碼函式
abi.encode(...) returns (bytes)
:返回給定引數的ABI編碼abi.encodePacked(...) returns (bytes)
: 對給定引數進行打包的編碼abi.encodeWithSelector(bytes4 selector,...) returns (bytes)
:對從第二個引數開始的給定引數進行ABI編碼,並以第一個引數作為返回結果的前4位元組——即用第一個引數作為函式選擇器(function selector),僅對其餘引數進行ABI 編碼。abi.encodeWithSignature(string signature,..) returns (bytes)
: 作用等同於abi.encodeWithSelector(bytes4 selector,...) returns (bytes)
note:
以上的編碼函式均可以用於基於函式呼叫的資料產生ABI編碼,而不會實際呼叫一個函式。此外,keccak256(abi.encodePacked(a, b))
是計算keccak256(a, b)
的更明確的方式,後者在未來的版本中不再推薦使用。
後面還會涉及到關於ABI編碼的部分
錯誤處理
asser(bool condition)
:如果條件不滿足則是交易不產生實際效果 —— 用於內部錯誤。require(bool condition)
: 如果條件不滿足則恢復(revert) —— 用於輸入或者外部元件引起的錯誤,同時提供一個錯誤訊息。require(bool condition,string message)
: 如果條件不滿足則恢復(revert) —— 用於輸入或者外部元件引起的錯誤,同時提供一個錯誤資訊。revert()
:終止執行並恢復(revert)狀態(state) 變動revert(string reason)
:終止執行並恢復狀態變動,並提供一個字元資訊來解釋原因。
數學和密碼學函式
addmod(uint x ,uint y,uint k) returns (uint)
:計算 (x+y)% k,加法會在任意精度下執行,並且加法的結果即使超過 2**256 也不會擷取。從 0.5.0 版本的編譯器開始會加入對 k != 0 的校驗。mulmod(uint x,uint y,uint k) returns (uin)
:計算 (x * y) %k,乘法會在任意精度下執行,並且乘法的結果即使超過 2**256 也不會被擷取。keccak256(...) returns (bytes32)
:計算“緊打包”引數(參加後續的“應用二進位制編碼說明”一章)的Ethereum-SHA-3 (keccak-256)雜湊。sha256(...) returns (bytes32)
:計算“緊打包”引數的SHA-256雜湊。sha3(...) returns (bytes32)
:等價於 keccak256.ripemd160(...) returns (bytes20)
:計算“緊打包”引數的RIPEMD-160雜湊。ecrecover(bytes32 hash, uint8 v, bytes32 r,bytew32 s) returns (address)
:利用橢圓曲線簽名恢復與公鑰相關的地址,錯誤返回0值。
上文中的緊打包(tightly packed) “是指不會把引數值進行padding處理(就是說所有引數值的位元組碼是連續存放的,中間不保留為把長度補充為一個”字“,即32位元組,而追加的若干0值位元組資料)這意味著下面的這些呼叫是等價的:
keccak256("ab", "c")
keccak256("abc")
keccak256(0x616263)
keccak256(6382179)
keccak256(97, 98, 99)
如果需要 padding,可以使用顯示型別轉換:keccak256(”\x00\x12")
和 keccak256(uint6(0x12))
是一樣的.
請注意,常量值會使用儲存他們所需要的最少位元組數進行打包.比如:keccak256(0) == keccak256(uint8(0))
,keccak256(0x12345678) == keccak256(uint32(0x12345678))
.
在一個私鏈上,你很有可能碰到由於 sha256, ripemd160 或者 ecrecover 引起的gas用盡問題.這個原因就是他們被當做所謂的預編譯合約而執行,並且第一次收到訊息後這些合約才真正存在(儘管合約程式碼是硬編碼).傳送到不存在的合約的訊息非常昂貴,所以實際的執行會導致out - of - gas錯誤.在你的合約中實際使用他們之前,給每個合約傳送一點兒以太幣,比如 1 wei.這在官方網路或測試網路上 都不是問題.(讀不懂)
地址相關
<address>.balance (uint256)
: 以Wei為單位的某個地址(address)的餘額.<address>.transfer(uint256 amount)
:向某個地址(address)傳送數量為amount的Wei,失敗時丟擲異常,且將額外發送2300 gas的曠工費,不可調整.<address>.send(uint256 amount) returns (bool)
:想某個地址(address)傳送數量為amount的Wei,失敗時返回false,且將額外發送2300gas的礦工費用,不可調整.<address>.call(...) returns (bool)
: 執行低階函式CALL ,失敗時返回 false,會發送所有可用的 gas,不可調整.<address>.callcode(...) returns (bool)
:執行低階函式 CALLCODE,失敗時返回 false,會發送所有可用的gas,不可調整.<address>.delegatecall(...) returns (bool)
:執行低階函式 DELEGATECALL, 失敗是返回 false,會發送所有可用的gas,不可調整.
warning:
使用send會有很多危險:如果呼叫棧深度已經達到1024(這總是可以由呼叫者所強制指定),轉賬會失敗;並且如果接受者用光了gas,轉賬同樣會失敗.為了保證以太幣轉賬安全,總是檢查 send 的返回值,利用 transfer 或者後文中更好的方式:使用一種由接受者取回資金的模式.note:
如果通過一個低階函式 delegatecall 來訪問一個storage 變數,兩個合約儲存中的資料佈局(位置)必須一致,以保證可以在呼叫的合約中通過變數名正確地訪問到呼叫合約中的儲存變數.這當然不是在高階的庫中通過函式引數傳遞儲存指標那種情況.
不鼓勵使用callcode,並且將來他會被移除.
合約相關
this
(current contract’s type):當前合約,可以明確轉換為某個地址(address).selfdestruct(address recipient)
:銷燬合約,並把餘額傳送到指定的地址(address)suicide(address recipient)
:等價於 selfdestruct.
此外,當前合約內地所有函式都可以被直接呼叫,包括當前函式.