solidity雜項

雜項

存儲中狀態變量的佈局

靜態尺寸大小的變量(除了映射和動態尺寸大小的數組類型(的其餘類型變量))在存儲中,是從位置0連續存儲。若是可能的話,不足32個字節的多個條目被緊湊排列在一個單一的存儲塊,參見如下規則:git

  • 在存儲塊中的第一項是存儲低階對齊的。github

  • 基本類型只使用了正好存儲它們的字節數。數組

  • 若是一個基本類型不適合存儲塊的剩餘部分,則移動到下一個存儲塊中。app

  • 結構和數組的數據老是開始一個新的塊而且佔整個塊(根據這些規則,結構或數組項都是緊湊排列的)。函數

結構和數組元素是一個接着一個存儲排列的,就如當初它們被聲明的次序。佈局

因爲沒法預知的分配的大小,映射和動態尺寸大小的數組類型(這兩種類型)是使用sha3 計算來找到新的起始位置,來存放值或者數組數據。這些起始位置老是滿棧塊。優化

根據上述規則,映射或動態數組自己存放在(沒有填滿)的存儲塊位置p(或從映射到映射或數組遞歸應用此規則)。對於一個動態數組,存儲塊存儲了數組元素的數目(字節數組和字符串是一個例外,見下文)。對於映射,存儲塊是未使用的(但它是須要的,所以,先後相鄰的兩個相同的映射,將使用一個不一樣的hash分佈)。數組數據位於sha3(p), 對應於一個映射key值k位於 sha3(k . p)  (這裏 . 是鏈接符)。若是該值又是一個非基本類型,位置的偏移量是sha3(k . p)。ui

若是bytes 和 string是短類型的,它們將和其長度存儲在同一個存儲塊裏。特別是:若是數據最長31字節,它被存儲在高階字節(左對齊), 低字節存儲length* 2。 若是是長類型,主存儲塊存儲 length* 2 + 1,  數據存儲在sha3(shot)。this

所以,本合約片斷以下:編碼

contract c {

  struct S { uint a; uint b; }

  uint x;

  mapping(uint => mapping(uint => S)) data;

}

深奧的特色

在Solidity的類型系統中,有一些在語法中沒有對應的類型。其中就有函數的類型。但若使用 var (這個關鍵字),該函數就被認爲是這個類型的局部變量:

contract FunctionSelector {

  function select(bool useB, uint x) returns (uint z) {

    var f = a;

    if (useB) f = b;

    return f(x);

  }

  function a(uint x) returns (uint z) {

    return x * x;

  }

  function b(uint x) returns (uint z) {

    return 2 * x;

  }

}

(在上面的程序片斷中)

若調用select(false, x),  就會計算 x * x 。若調用select(true, x))就會計算 2 * x。

內部-優化器

Solidity 優化器是在彙編級別上的操做,因此它也能夠同時被其餘語言所使用。它將指令的(執行)次序,在JUMP 和 JUMPDEST上分紅基本的塊。在這些塊中,指令被解析 。 堆棧、內存或存儲上的每一次修改,都將做爲表達式被記錄。該表達式包括一條指令以及指向其餘表達式的一系列參數的一個指針。如今的主要意思是要找到相等的表達式(在每次輸入),作成了表達式的類。優化器首先在已知的表達式列表裏找,若找不到的話,就根據constant + constant = sum_of_constants  或 X * 1 = X  來簡化。 由於這樣作是遞歸的,若是第二個因子是一個更復雜的表達式,咱們也能夠應用latter規則來計算。存儲和內存位置的修改,是不知道存儲和內存的位置的區別。若是咱們先寫到的位置x,再寫到位置y , x,y均是輸入變量。第二個能夠覆寫第一個,因此咱們不知道x是存放在y以後的。另外一方面,若是一個簡化的表達式 x-y 能計算出一個非零常數,咱們就知道x存放的內容。

在這個過程結束時,咱們知道,表達式必須在堆棧中結尾,並有一系列對內存和存儲的修改。這些信息存儲在block上,並連接這些block。此外,有關堆棧,存儲和內存配置的信息會轉發到下一個block。若是咱們知道全部的 JUMP 和 JUMPI 指令,咱們能夠創建一個完整的程序控制流圖。若是咱們不知道目標塊(原則上,跳轉目標是從輸入裏獲得的),咱們必須清除全部輸入狀態的存儲塊上的信息,(由於它的目標塊未知)。若是條件計算的結果爲一個常量,它轉化爲一個無條件jump。

做爲最後一步,在每一個塊中的代碼徹底能夠從新生成。從堆棧裏block的結尾表達式開始,建立一個依賴關係圖。每一個不是這個圖上的操做將捨棄。如今能按照原來代碼的順序,生成對存儲和內存修改的代碼(捨棄沒必要要的修改)。最後,在正確位置的堆棧上,生成全部的值。

這些步驟適用於每個基本塊, 若是它是較小的,用新生成的代碼來替換。若是一個基本塊在JUMPI上進行分割,在分析過程當中,條件表達式的結果計算爲一個常數,JUMP就用常量值進行替換。代碼以下

var x = 7;

data[7] = 9;

if (data[x] != x + 2)

  return 2;

else

  return 1;

簡化成下面能夠編譯的形式

data[7] = 9;

return 1;

即便在開始處包含有jump指令

使用命令行編譯器

一個 Solidity 庫構建的目標是solc, Solidity命令行編譯器。使用solc –help  爲您提供全部選項的解釋。編譯器能夠產生不一樣的輸出,從簡單的二進制文件,程序集的抽象語法樹(解析樹)到gas使用的估量。若是你只想編譯一個文件,你運行solc –bin sourceFile.sol , 將會打印出二進制。你部署你的合約以前,使用solc –optimize –bin sourceFile.sol 來激活優化器。若是你想得到一些solc更進一步的輸出變量,可使用solc -o outputDirectory –bin –ast –asm sourceFile.sol,(這條命令)將通知編譯器輸出結果到單獨的文件中。

命令行編譯器會自動從文件系統中讀取輸入文件,但也能夠以下列方法,提供重定向路徑prefix=path :

solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ =/usr/local/lib/fallback file.sol

該命令告訴編譯器在/usr/local/lib/dapp-bin目錄下,尋找以github.com/ethereum/dapp-bin/  開頭的文件,若是找不到的話,到usr/local/lib/fallback目錄下找(空前綴老是匹配)。

solc不會從remapping目標的外部,或者顯式定義的源文件的外部文件系統讀取文件,因此要寫成 import 「/etc/passwd」;     只有增長 =/ 做爲remapping,程序才能工做。

若是remapping裏找到了多個匹配,則選擇有共同的前綴最長的那個匹配。

若是你的合約使用了  libraries ,你會注意到字節碼中包含了 form LibraryName 這樣的子字符串。你能夠在這些地方使用solc 做爲連接器,來插入庫地址 :

Either add –libraries 「Math:0x12345678901234567890 Heap:0xabcdef0123456」    提供每一個庫的地址, 或者在文件中存放字符串(每行一個庫)

而後運行solc,後面寫 –libraries fileName.

若是solc後面接着 –link 選項,全部輸入文件將被解釋爲未連接的二進制文件(十六進制編碼), LibraryName形式如前所述, 庫此時被連接(從stdin讀取輸入,從stdout輸出)。在這種狀況下,除了–libraries,其餘全部的選項都將被忽略(包括 -o)

提示和技巧

  • 在數組中使用delete,就是刪除數組中的全部元素。

  • 使用較短的類型和結構元素,短類型分組在一塊兒進行排序。sstore操做可能合併成一個單一的sstore,這能夠下降gas的成本(sstore消耗5000或20000 gas,因此這是你必須優化的緣由)。使用天gas的價格估算功能(優化器 enable)進行檢查!

  • 讓你的狀態變量公開,編譯器會免費建立 getters 。

  • 若是你結束了輸入或狀態的檢查條件,請嘗試使用函數修飾符。

  • 若是你的合約有一個功能send, 但你想使用內置的send功能,請使用 address(contractVariable).send(amount)。

  • 若是你不想你的合約經過send接收ether,您能夠添加一個拋出回退函數 function() { throw; }.。

  • 用單條賦值語句初始化存儲結構:x = MyStruct({a: 1, b: 2});

陷阱

不幸的是,還有一些編譯器微妙的狀況尚未告訴你。

  • 在for (var i = 0; i < arrayName.length; i++) { ... },  i的類型是uint8,由於這是存放值0最小的類型。若是數組元素超過255個,則循環將不會終止。

列表

全局變量

  • block.coinbase (address):當前塊的礦場的地址

  • block.difficulty (uint):當前塊的難度

  • block.gaslimit (uint):當前塊的gaslimit

  • block.number (uint):當前塊的數量

  • block.blockhash (function(uint) returns (bytes32)):給定的塊的hash值, 只有最近工做的256個塊的hash值

  • block.timestamp (uint):當前塊的時間戳

  • msg.data (bytes):完整的calldata

  • msg.gas (uint): 剩餘gas

  • msg.sender (address):消息的發送者(當前調用)

  • msg.value (uint):和消息一塊兒發送的wei的數量

  • now (uint):當前塊的時間戳(block.timestamp的別名)

  • tx.gasprice (uint):交易的gas價格

  • tx.origin (address):交易的發送者(全調用鏈)

  • sha3(...) returns (bytes32):計算(緊湊排列的)參數的 Ethereum-SHA3  hash值 

  • sha256(...) returns (bytes32)計算(緊湊排列的)參數的SHA256 hash值 

  • ripemd160(...) returns (bytes20):計算 256個(緊湊排列的)參數的RIPEMD

  • ecrecover(bytes32, uint8, bytes32, bytes32) returns (address):橢圓曲線簽名公鑰恢復

  • addmod(uint x, uint y, uint k) returns (uint):計算(x + y)K,加法爲任意精度,不以2 ** 256取餘

  • mulmod(uint x, uint y, uint k) returns (uint):計算(XY)K,乘法爲任意精度,不以2 * 256取餘

  • this (current contract’s type): 當前合約,在地址上顯式轉換

  • super:在層次關係上一層的合約

  • selfdestruct(address):銷燬當前的合同,將其資金髮送到指定address地址

  • <address>.balance:address地址中的帳戶餘額(以wei爲單位)

  • <address>.send(uint256) returns (bool):將必定量wei發送給address地址,若失敗返回false。

函數可見性定義符

function myFunction() <visibility specifier> returns (bool) {

    return true;

}
  • public:在外部和內部都可見(建立存儲/狀態變量的訪問者函數)

  • private:僅在當前合約中可見

  • external: 只有外部可見(僅對函數)- 僅僅在消息調用中(經過this.fun)

  • internal: 只有內部可見

Modifiers

  • constant for state variables: Disallows assignment (except initialisation), does not occupy storage slot.

  • constant for functions: Disallows modification of state - this is not enforced yet.

  • anonymous for events: Does not store event signature as topic.

  • indexed for event parameters: Stores the parameter as topic.

修飾符

  • constant for state variables: 不容許賦值(除了初始化),不佔用存儲塊。

  • constant for functions:不容許改變狀態- 這個目前不是強制的。

  • anonymous for events:不能將topic做爲事件指紋進行存儲。

  • indexed for event parameters: 將topic做爲參數存儲。

相關文章
相關標籤/搜索