智能合約文檔

solidity文檔審閱javascript

特色

  1. 基於底層帳戶而非utxo 因此有一個特殊的地址 定位於用戶 定位於合約 合約自己是一個帳戶
  2. 語言內嵌框架支持支付 只須要一個關鍵詞payable能夠在語言層面支持支付 而且超級簡單
  3. 存儲是使用網絡上的區塊鏈 數據的每個狀態均可以永久存儲 因此須要肯定變量使用內存仍是區塊鏈 使用內存不是永久存儲
  4. 運行環境是在p2p網絡,會比較強調合約或者函數執行的方式,函數的調用由原來的簡單函數變成了一個區塊鏈上的代碼執行
  5. 一旦出現異常全部的交易都能被回撤 這保證了合約執行的原子性 避免了中間狀態的不一致。

第一個例子:

`pragma solidity ^0.4.0;

contract HelloWorld{
uint balance;
function update(uint amount) returns (address, uint){
balance += amount;
return (msg.sender, balance);
}
}`java

讀取函數輸出的新值,累加到合約變量中 返回發送人的地址 和累餘額。python

remix編輯器的使用

部署函數 create 能在瀏覽器函數創造能調用的函數按鈕。 在update輸入參數,點擊按鈕就能執行函數而且打印結果。git

出錯的話 等待瀏覽器加載資源完成再操做es6

文件結構

1.版本申明
pragma solidity ^0.4.0;
2.全局引入
import 「filename」;
3.自定義命名空間
import * as symbolName from 「filename」
非es6兼容的簡寫語法
import 「filename」 as symbolName
等同於import*as symbolName from "filename"github

注意事項:非.打頭的會被認爲是絕對路徑 所以要引用同級目錄 必須如下寫法 import 「./x」 as x 錯誤方式 import 「x」 as x;(這樣會在全局目錄下 等同與網絡地址)web

編譯器解析文件機制

  1. 能夠將一個域名下的文件映射到本地,從而從本地的某個文件中讀取
    2.提供對同一實現的不一樣版本的支持(可能某版本的實現先後不兼容,須要區分)
    3.若是前綴相同,取最長,
    4.有一個」fallback-remapping」機制,空串會映射到「/usr/local/include/solidify」

solc編譯器

命令行編譯器 經過如下命名空間提供支持算法

context:prefix=target

在contex目錄下的以prefix開頭的會被替換爲target
舉例說:若是你將github.com/ethereum/dapp-bin拷到本地的/usr/local/dapp-bin,並使用下述方式使用文件
import 「github.com/ethereum/dapp-bin/library/iterable_mapping.sol」 as it_mapping;
要編譯只須要
solc github.com/ethereum/dapp-bin=/usr/local/dapp-bin source.solapi

舊版本的寫法
solc module1:github.com/ethereum/dapp-bin=/usr/local/dapp-bin modeule2:github.com/ethereum/dapp-bin=/usr/local/dapp-bin_old source.sol數組

solc僅僅容許包含實際存在的文件 他必存在於你映射後的目錄裏 若是你想包含絕對路徑那麼能夠將命名空間映射爲= 注意:若是多重映射指向同一個文件 那麼取最長的文件

browser-solidity編譯器

會自動映射到github上 而後自動檢索文件 所以能夠經過如下方式進行引入一個迭代包
import 「github.com/ethereum/dapp-bin/library/iterable_mapping.sol」 as it_mapping
備註:將來可能會支持其餘的源碼方式

代碼註釋:

//註釋
多行註釋/.../
// this is a single-line comment

文檔註釋:

///或者/../ 也可使用Doxygen語法 能夠生成對文檔的說明 參數的註解或者用戶在調用函數,彈出來的確認內容。

pragma solidity ^0.4.0;
/** @title Shape calculator.*/
contract shapeCalculator{
/**
*@dev calculate a rectangle's suface and perimeter

*@param w width of the rectangles

*@param h height of the rectangles

*@return s surface of the rectangles

*@return p perimeter of the rectangles

*/

function rectangles(uint w, uint h) returns (uint s, uint p){

    s = w * h;

    p = 2 * ( w + h) ;

}

}

基本要素

狀態變量
pragma solidity ^0.4.0;

// simple store example

contract simpleStorage{

uint valueStore; //state variable

}
函數
函數分爲內部和外部調用 同時對於其餘合約不一樣級別的可見性和訪問控制
函數修飾符
事件
事件是evm提供的一個便利接口 用於獲取當前實踐
例如
pragma solidity ^0.4.0;

contract SimpleAuction {
event aNewHigherBid(address bidder, uint amount);

function  bid(uint bidValue) external {
    aNewHigherBid(msg.sender, msg.value);
}

}
結構類型
結構體
pragma solidity ^0.4.0;

contract Company {
//user defined Employee struct type
//group with serveral variables
struct employee{
string name;
uint age;
uint salary;
}

//User defined `manager` struct type
//group with serveral variables
struct manager{
    employee employ;
    string title;
}

}
枚舉類型
用於列舉全部的狀況
pragma solidity ^0.4.0;

contract Home {
enum Switch{On,Off}
}

庫的概念

庫就是合約 他的目的在於僅僅部署一次來複用代碼 經過的是DELEGATECALL這個方法 。 這意味着庫函數調用的 他的代碼是在上下文中執行

使用庫合約的 能夠稱爲隱藏的父合約,他們不會顯示出如今繼承關係上
對兩個庫文件使用的說明
pragma solidity ^0.4.0;

library Set {
// We define a new struct datatype that will be used to
// hold its data in the calling contract.
struct Data { mapping(uint => bool) flags; }

// Note that the first parameter is of type "storage
// reference" and thus only its storage address and not
// its contents is passed as part of the call. This is a
// special feature of library functions. It is idiomatic
// to call the first parameter 'self', if the function can
// be seen as a method of that object.
function insert(Data storage self, uint value)
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}

function remove(Data storage self, uint value)
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}

function contains(Data storage self, uint value)
returns (bool)
{
return self.flags[value];
}
}

contract C {
Set.Data knownValues;

function register(uint value) {
    // The library functions can be called without a
    // specific instance of the library, since the
    // "instance" will be the current contract.
    if (!Set.insert(knownValues, value))
        throw;
}
// In this contract, we can also directly access knownValues.flags, if we want.

}

1.library定義了一個結構體 用來在調用合約時候使用 若是函數須要操做數據 這個數據通常是經過庫函數的第一個參數傳入 按慣例會把參數定爲self
2.self的類型是storage 意味着是一個函數引用 修改他會影響全部的地方
3.庫函數的使用不須要實例化 c.register 能夠看出使用set.insert 但實際上當前的合約自己就是一個實例
3.c能夠直接訪問 kownValues這個值能夠被庫函數調用

另外一種調用方法:
`pragma solidity ^0.4.0;

library BigInt {
struct bigint {
uint[] limbs;
}

function fromUint(uint x) internal returns (bigint r) {
    r.limbs = new uint[](1);
    r.limbs[0] = x;
}

function add(bigint _a, bigint _b) internal returns (bigint r) {
    r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length));
    uint carry = 0;
    for (uint i = 0; i < r.limbs.length; ++i) {
        uint a = limb(_a, i);
        uint b = limb(_b, i);
        r.limbs[i] = a + b + carry;
        if (a + b < a || (a + b == uint(-1) && carry > 0))
            carry = 1;
        else
            carry = 0;
    }
    if (carry > 0) {
        // too bad, we have to add a limb
        uint[] memory newLimbs = new uint[](r.limbs.length + 1);
        for (i = 0; i < r.limbs.length; ++i)
            newLimbs[i] = r.limbs[i];
        newLimbs[i] = carry;
        r.limbs = newLimbs;
    }
}

function limb(bigint _a, uint _limb) internal returns (uint) {
    return _limb < _a.limbs.length ? _a.limbs[_limb] : 0;
}

function max(uint a, uint b) private returns (uint) {
    return a > b ? a : b;
}

}

contract C {
using BigInt for BigInt.bigint;

function f() {
    var x = BigInt.fromUint(7);
    var y = BigInt.fromUint(uint(-1));
    var z = x.add(y);
}

}`

編譯器並不知道庫的最終地址 這些地址必須linker填進的最終字節碼鍾 若是地址沒有參數的方式正確編譯器 編譯後的字節碼仍會包含一個這樣的字符 能夠手動替換爲十六進制地址。

當前庫的限制

  1. 無狀態變量
  2. 不能被繼承
  3. 不能接收ether

附着庫

using A for *的效果是,庫A中的函數被附着在作任意的類型上。
using A for B;指令僅在當前的做用域有效,且暫時僅僅支持當前的合約這個做用域,後續也很是有可能解除這個限制,容許做用到全局範圍。若是能做用到全局範圍,經過引入一些模塊(module),數據類型將能經過庫函數擴展功能,而不須要每一個地方都得寫一遍相似的代碼了。

內存變量的佈局

solidity預留了3個32字節的大小的槽位
0-64:哈希方法的暫存空間(scratch space)
64-96:當前已分配內存大小(也稱空閒內存指針(free memory pointer))

有一些在Solidity中的操做須要超過64字節的臨時空間,這樣就會超過預留的暫存空間。他們就將會分配到空閒內存指針所在的地方,但因爲他們自身的特色,生命週期相對較短,且指針自己不能更新,內存也許會,也許不會被清零(zerod out)。所以,你們不該該認爲空閒的內存必定已是清零(zeroed out)的。
空間的空間不必定會被清零。

調用合約

調用合約的帳戶 輸入的數據須要符合abi的標準

狀態變量的存儲模型

特色:
1.第一項是按照低位對其存儲
2.基本理性存儲使用實際字節
3.基本類型不能存放 放入下一個槽位
4.結構體和數組老是佔滿槽位

建議

若是操做少於32 應當使用實際的字節
編譯器會將多個元素進行打包到一個槽位
最後爲了方便evm進行優化 嘗試有意識的storage變量和結構體成員 好比應當使用uint128 uint128 uint256 而不是使用uint128 uint256 uint128
原理:編譯器會將多個元素打包一個槽位 進行一次讀寫 當操做momory 編譯器不會優化 上述草操做沒有意義

非固定大小

結構體和數組裏的元素按照他們給定的順序存儲。
因爲他們不可預估大小。
1.evm堆棧
2.映射和動態數組很具規則在p沾滿一個未滿的槽位。 對一個的動態數組,位置p這個槽位。 對於映射,這個槽位沒有填充任何數據。 數組的原始位置是keccak(256) 映射位置k.p
pragma solidity ^0.4.4;

contract C {
struct s { uint a; uint b; }
uint x;
mapping(uint => mapping(uint => s)) data;
}
3.按照實際的代碼來看 結構體從0開始 定義了一個結構體 但並無對應的結構體變量 故不佔用空間。 uint x實際uint256 佔用32個字節 佔用槽位0 因此映射data 將從槽位1開始
data[4][9].b的位置在kecca256(uint256(9)).keccak256(uint256(4).uint256(1))+1;

源文件映射

做爲AST輸出的一部分,編譯器會提供AST某個節點以應的源代碼的範圍。
上述源代碼的映射都使用整數來引用源代碼

特殊機制

使用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;
}
}

能夠對var賦值爲不一樣的函數

內部機制-清理變量

設計思想:在任何潛在的參與數據帶來反作用以前 清理掉這些髒數據。 這些髒數據會帶來意想不到的錯誤。 特別是storage中。
若是不會形成反作用則不會清理。

獨立的彙編語言

1.使用他編寫代碼要可讀
2.從彙編語言轉爲字節碼應該儘量少坑
3.控制流便於優化和驗證
所以solidity設計了下面的東西:

提供高級組件

for
switch
這樣比swap dup jump jumpi 更加可讀
函數形式的語句如mul(add(x,y),7)比7y x add num更加可讀

很是規規則

引入一個絕對階段來實現 該階段只能以很是規則的方式取除較高階別的構造,而且仍容許檢查低級彙編語言。 函數名變量名 這些遵循soldity的規則

爲何使用高層級的構造器。
每個變量在棧的鐘位置是已知的。

示例:
咱們來考慮一下solidity程序的字節碼:
contract C {
function f(uint x) returns (uint y) {
y = 1;
for (uint i = 0; i < x; i++)
y = 2 * y;
}
}

生成的彙編
{
mstore(0x40, 0x60) // store the "free memory pointer"
// function dispatcher
switch div(calldataload(0), exp(2, 226))
case 0xb3de648b {
let (r) = f(calldataload(4))
let ret := $allocate(0x20)
mstore(ret, r)
return(ret, 0x20)
}
default { revert(0, 0) }
// memory allocator
function $allocate(size) -> pos {
pos := mload(0x40)
mstore(0x40, add(pos, size))
}
// the contract function
function f(x) -> y {
y := 1
for { let i := 0 } lt(i, x) { i := add(i, 1) } {
y := mul(2, y)
}
}
}

脫機彙編
{
mstore(0x40, 0x60)
{
let $0 := div(calldataload(0), exp(2, 226))
jumpi($case1, eq($0, 0xb3de648b))
jump($caseDefault)
$case1:
{
// the function call - we put return label and arguments on the stack
$ret1 calldataload(4) jump(f)
// This is unreachable code. Opcodes are added that mirror the
// effect of the function on the stack height: Arguments are
// removed and return values are introduced.
pop pop
let r := 0
$ret1: // the actual return point
$ret2 0x20 jump($allocate)
pop pop let ret := 0
$ret2:
mstore(ret, r)
return(ret, 0x20)
// although it is useless, the jump is automatically inserted,
// since the desugaring process is a purely syntactic operation that
// does not analyze control-flow
jump($endswitch)
}
$caseDefault:
{
revert(0, 0)
jump($endswitch)
}
$endswitch:
}
jump($afterFunction)
allocate:
{
// we jump over the unreachable code that introduces the function arguments
jump($start)
let $retpos := 0 let size := 0
$start:
// output variables live in the same scope as the arguments and is
// actually allocated.
let pos := 0
{
pos := mload(0x40)
mstore(0x40, add(pos, size))
}
// This code replaces the arguments by the return values and jumps back.
swap1 pop swap1 jump
// Again unreachable code that corrects stack height.
0 0
}
f:
{
jump($start)
let $retpos := 0 let x := 0
$start:
let y := 0
{
let i := 0
$for_begin:
jumpi($for_end, iszero(lt(i, x)))
{
y := mul(2, y)
}
$for_continue:
{ i := add(i, 1) }
jump($for_begin)
$for_end:
} // Here, a pop instruction will be inserted for i
swap1 pop swap1 jump
0 0
}
$afterFunction:
stop
}

彙編的幾個階段:
1.解析
2.脫彙編
3.生成指令流
4.生成字節碼
關鍵點:
1>
將字節流轉爲符號流,去掉其中的C++風格的註釋(一種特殊的源代碼引用的註釋,這裏不打算深刻討論)。
將符號流轉爲下述定義的語法結構的AST。
註冊塊中定義的標識符,標註從哪裏開始(根據AST節點的註解),變量能夠被訪問。
2>
一個AST轉換,移除其中的for,switch和函數構建。結果仍由同一個解析器,但它不肯定使用什麼構造。若是添加僅跳轉到而且不繼續的jumpdests,則添加有關堆棧內容的信息,除非沒有局部變量訪問到外部做用域或棧高度與上一條指令相同。
3>
在操做碼流生成期間,咱們在一個計數器中跟蹤當前的棧高,因此經過名稱訪問棧的變量是可能的。棧高在會修改棧的操做碼後或每個標籤後進行棧調整。當每個新局部變量被引入時,它都會用當前的棧高進行註冊。若是要訪問一個變量(或者拷貝其值,或者對其賦值),會根據當前棧高與變量引入時的當時棧高的不一樣來選擇合適的DUP或SWAP指令。

solidity內聯彙編

着手解決各類特性:
容許函數風格
內聯局部變量
可訪問外部變量
標籤
循環
switch語句
函數調用
assembly內聯編譯語言

內有安全機制 指的是一種底層訪問

示例:
使用時間:
在編譯器沒辦法獲得有效率的代碼時候很是有用。

pragma solidity ^0.4.0;

library VectorSum {
// This function is less efficient because the optimizer currently fails to
// remove the bounds checks in array access.
function sumSolidity(uint[] _data) returns (uint o_sum) {
for (uint i = 0; i < _data.length; ++i)
o_sum += _data[i];
}

// We know that we only access the array in bounds, so we can avoid the check.
// 0x20 needs to be added to an array because the first slot contains the
// array length.
function sumAsm(uint[] _data) returns (uint o_sum) {
    for (uint i = 0; i < _data.length; ++i) {
        assembly {
            o_sum := mload(add(add(_data, 0x20), mul(i, 0x20)))
        }
    }
}

}

內聯編譯的語法

字面量 ox123 42 或者abc
操做碼 mload sload dup1 sstore 後面有課支持的指令列表
函數風格 add(1,mlod(0))
標籤 name:
變量定義 let x:=7 let x:=add(y,3)
賦值 jump(name) x3 add
函數風格的賦值
支持塊級局部變量

solidity慣例
固定長度的memory數組沒有一個長度字段 但他們將很快增長這個字段 讓定長和變長數組之間有更好的轉換能力 因此不要依賴這一點

接口

限制

  1. 不能繼承其餘合約或者接口
  2. 不能定義構造器
  3. 不能定義變量
  4. 不能定義結構體
  5. 不能定義枚舉類型

接口僅僅限制abi能夠表示的內容不會有任何信息丟失

接口用本身的關鍵詞表示
interface Token{
function transfer(address recipient,uint amount);
}
合約能夠繼承接口 由於他們能夠繼承其餘合約。

抽象函數

抽象函數是沒有函數體的函數
以下:
pragma solidity ^0.4.0;

contract Feline {
function utterance() returns (bytes32);
}

這樣的合約不能經過編譯 即便合約內容 也包含一些正常函數

pragma solidity ^0.4.0;

contract Feline {
function utterance() returns (bytes32);

function getContractName() returns (string){
    return "Feline";
}

}

contract Cat is Feline {
function utterance() returns (bytes32) { return "miaow"; }
}

若是從一個合約從一個合約裏繼承 但卻沒有實現全部函數 那麼他也是一個抽象合約

繼承

多重繼承 能夠繼承多個合約
原理:父合於會被拷貝過來代碼 造成一個合體

一個特殊的例子:
pragma solidity ^0.4.0;

contract mortal is owned {
function kill() {
if (msg.sender == owner) selfdestruct(owner);
}
}

contract Base1 is mortal {
function kill() { /* do cleanup 1 */ mortal.kill(); }
}

contract Base2 is mortal {
function kill() { /* do cleanup 2 */ mortal.kill(); }
}

contract Final is Base1, Base2 {
}
結果:合約只會執行base2.kill 由於會派生重寫 會跳base1.kill 由於他根本不知道有base1

一個變通的方法是使用super

基類構造器

pragma solidity ^0.4.0;

contract Base {
uint x;
function Base(uint _x) { x = _x; }
}

contract Derived is Base(7) {
function Derived(uint _y) Base(_y * _y) {
}
}
一種方式是頭部聲名 還有一種是合約頭生聲名 第二鍾方式優先

多繼承和線性化

須要解決的是鑽石問題 鑽石問題 解決方法是參考python

pragma solidity ^0.4.0;

contract X {}
contract A is X {}
contract C is A, X {}
錯誤Linearization of inheritance graph impossible
緣由是c會請求x來重寫a a又是重寫x 所以這是一個不可能解決的矛盾

繼承相同名字的不一樣類型的成員

當繼承最後致使一個合約同時才能在多個名字相同的修改器時 他會被視爲一個錯誤 同新的若是事件與修改器重名
或爲一個例外 getter能夠覆蓋一個public函數

事件

事件是使用EVM日誌內置功能的方便工具,在DAPP的接口中,它能夠反過來調用Javascript的監聽事件的回調。

原理:事件能夠被繼承 被調用時候 出發的參數將存儲到交易日誌中 這些日誌與合約地址關聯 併合併到區塊鏈中 區塊存在就能夠一直訪問。

日至不能再合約被直接訪問

如何獲取日誌的例子
var abi = /* abi as generated by the compiler /;
var ClientReceipt = web3.eth.contract(abi);
var clientReceipt = ClientReceipt.at(0x123 /
address */);

var event = clientReceipt.Deposit();

// watch for changes
event.watch(function(error, result){
// result will contain various information
// including the argumets given to the Deposit
// call.
if (!error)
console.log(result);
});

// Or pass a callback to start watching immediately
var event = clientReceipt.Deposit(function(error, result) {
if (!error)
console.log(result);
});

步驟:
1.使用web3調用abi 2.拿到合約地址contract.at 實例化合約 3.調用watch方法 獲取result
4.回調函數

底層日誌接口

經過函數能夠訪問 logN表明當問參數的數目 默認從0開始
第一個參數被做爲日誌的一部分 其餘的會做爲主題
log3{
msg.value,
0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20,
msg.sender,
_id
}

回退函數

沒有匹配任何值會默認調用這個函數
支付eth時也會調用這個函數。
若是下述函數寫在回退裏面 那麼花費將會不少
1.寫入合約
2.建立合約
3.執行一個外部函數
4.發送ether

注意 要保證你的函數花費在2300gas之內

一個沒有定義一個回退函數的合約 若是接收ether 會觸發異常 返還ether(0.4.0開始) 合約要接收ether 必須事先回退函數

常量

能夠被定義爲constant
賦值的表達式不容許:1)訪問storage;2)區塊鏈數據,如now,this.balance,block.number;3)合約執行的中間數據,如msg.gas;4)向外部合約發起調用。
當前的支持的僅有值類型和字符串。

常函數

函數也能夠被聲明爲常量 這類函數承諾本身不修改狀態
當前不會強制使用 可是建議對於不修改的數據標記爲constant

函數修改器

修改器(Modifiers)能夠用來輕易的改變一個函數的行爲。好比用於在函數執行前檢查某種前置條件。修改器是一種合約屬性,可被繼承,同時還可被派生的合約重寫(override)。

做用範圍和聲名

一個變量在聲明後都有初始值爲字節表示的全0值。也就是全部類型的默認值是典型的零態(zero-state)。舉例來講,默認的bool的值爲false,uint和int的默認值爲0。
全部的類型默認零態。 舉例 bool默false uint 和int默認值爲0
對於變長的數組bytes和string,默認值則爲空數組和空字符串。

另外 一個變量被聲明瞭 那麼他會在開始前被初始化位默認值
pragma solidity ^0.4.0;

contract C{
function foo() returns (uint) {
// baz is implicitly initialized as 0
uint bar = 5;
if (true) {
bar += baz;
} else {
uint baz = 10;// never executes
}
return bar;// returns 5
}
}

賦值

內置結果返回多個值

數組和自定義結構體的複雜性

數組類型 賦值語法有一些複雜
賦值給一個狀態變量老是建立一個徹底無關的拷貝
賦值給一個局部變量 僅僅對支持的類型 如那些32字節之內的靜態類型 建立一份徹底無關的拷貝
。。。。麻煩
若是是數據結構和數組 bytes 和string 由狀態變量賦值爲一個局部變量,局部變量只是持有原始狀態變量的一個引用。

執行順序

表達式的求值順序並非肯定的,更正式的說法是,表達式樹一個節點的某個節點在求值時的順序是不肯定的,可是它確定會比節點自己先執行。

建立合約示例

不能限制gas使用 由於無足夠的餘額 會拋出一個異常

函數調用

直接調用 或者遞歸調用

contract C {
function g(uint a) returns (uint ret) { return f(); }
function f() returns (uint ret) { return g(7) + f(); }
}

外部函數調用

在合約的構造器中,不能使用this調用函數,由於當前合約尚未建立完成。

發生異常的幾種狀況
若是被調用的合約不存在,或者是不包代碼的賬戶,或調用的合約產生了異常,或者gas不足,均會形成函數調用發生異常。

寫一個函數,只有當狀態變量(state variables)的值有對應的改變時,才調用外部函數,這樣你的合約就不會有可重入性漏洞。
可重入漏洞 外部函數修改內部的值 合約之間交互存在潛在危險

命名參數調用和匿名參數調用

函數調用的參數,能夠經過指定名字的方式調用,但能夠以任意的順序,使用方式是{}包含。但參數的類型和數量要與定義一致。

省略函數名稱

沒有使用的參數名能夠省略(通常常見於返回值)。這些名字在棧(stack)上存在,但不可訪問。
沒有使用的能夠省略 這樣的在站上存在 可是不能夠訪問

控制結構

持if,else,while,do,for,break,continue,return,?:。
好比if(1){...}在Solidity中是無效的。由於不存在自動轉換。

入參和出參

同javascript同樣,函數有輸入參數,但與之不一樣的是,函數可能有任意數量的返回參數。

返回多個

當返回多個參數時,使用return (v0, v1, ..., vn)。返回結果的數量須要與定義的一致。

地址相關方法

.balance (uint256):

Address的餘額,以wei爲單位。

.transfer(uint256 amount):

發送給定數量的ether,以wei爲單位,到某個地址。失敗時拋出異常。

.send(uint256 amount) returns (bool):

發送給定數量的ether,以wei爲單位,到某個地址。失敗時返回false。

.call(...) returns (bool):

發起底層的call調用。失敗時返回false。

.callcode(...) returns (bool):

發起底層的callcode調用,失敗時返回false。

.delegatecall(...) returns (bool):

發起底層的delegatecall調用,失敗時返回false。

數學和加密函數

asser(bool condition):

若是條件不知足,拋出異常。

addmod(uint x, uint y, uint k) returns (uint):

計算(x + y) % k。加法支持任意的精度。但不超過(wrap around?)2**256。

keccak256(...) returns (bytes32):

使用以太坊的(Keccak-256)計算HASH值。緊密打包。

sha3(...) returns (bytes32):

等同於keccak256()。緊密打包。

sha256(...) returns (bytes32):

使用SHA-256計算HASH值。緊密打包。

ripemd160(...) returns (bytes20):

使用RIPEMD-160計算HASH值。緊密打包。

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):

經過簽名信息恢復非對稱加密算法公匙地址。若是出錯會返回0,附錄提供了一個例子1.

revert():

取消執行,並回撤狀態變化。

  1. 在私鏈上 一個可能存在問題是:
    向一個不存在的合約發送消息,很是昂貴,因此纔會致使Out-Of-Gas的問題。

2.須要注意的是字面量會用,儘量小的空間來存儲它們。好比,keccak256(0) == keccak256(uint8(0)),keccak256(0x12345678) == keccak256(uint32(0x12345678))

3.若是須要補位,須要明確的類型轉換,如keccak256("\x00\x12")等同於keccak256(uint16(0x12))

特殊變量和函數

一些變量村子當前上下文中 用來獲取一些區塊信息

block.blockhash(uint blockNumber) returns (bytes32),給定區塊號的哈希值,只支持最近256個區塊,且不包含當前區塊。
block.coinbase (address) 當前塊礦工的地址。
block.difficulty (uint)當前塊的難度。
block.gaslimit (uint)當前塊的gaslimit。
block.number (uint)當前區塊的塊號。
block.timestamp (uint)當前塊的時間戳。
msg.data (bytes)完整的調用數據(calldata)。
msg.gas (uint)當前還剩的gas。
msg.sender (address)當前調用發起人的地址。
msg.sig (bytes4)調用數據的前四個字節(函數標識符)。
msg.value (uint)這個消息所附帶的貨幣量,單位爲wei。
now (uint)當前塊的時間戳,等同於block.timestamp
tx.gasprice (uint) 交易的gas價格。
tx.origin (address)交易的發送者(完整的調用鏈)

時間單位

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

若是須要進行單位轉換 要特別當心 不是天天都有24小時 不是每一年都是365天
解決方案: 必須有一個外部的orcale來跟新獲得一個準確的日曆(內部消耗gas)

示範:
pragma solidity ^0.4.0;

contract DeleteExample{

function nowInSeconds() returns (uint256){
    return now;
}

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

}

貨幣單位

一個字面量的數字,可使用後綴wei,finney,szabo或ether來在不一樣面額中轉換。不含任何後綴的默認單位是wei。如2 ether == 2000 finney的結果是true。

類型推斷

函數的參數,包括返回參數,不可使用var這種不指定類型的方式。
代碼for (var i = 0; i < 2000; i++) {}將是一個無限循環,由於一個uint8的i的將小於2000。
代碼不能編譯經過

編譯器會根據第一個值進行推斷

基本類型間的轉換

隱士轉換和顯示轉換。
Uint8 能夠轉換成uint256 可是int8不能轉換成uint256
由於uint256不能表示-1

顯示轉換:
pragma solidity ^0.4.0;

contract DeleteExample{
uint a;

function f() returns (uint){
  int8 y = -3;
  uint x = uint(y);
  return x;
}

}

uint32 a = 0x12345678;
uint16 b = uint16(a); // b will be 0x5678 now

左值的相關運算符

運算有
-= += *= %= |= &= ^= ++ --

不能對一個storage的類型賦值

映射 字典

pragma solidity ^0.4.0;

//file indeed for compile
//may store in somewhere and import
contract MappingExample{
mapping(address => uint) public balances;

function update(uint amount) returns (address addr){
    balances[msg.sender] = amount;
    return msg.sender;
}

}

contract MappingUser{

address conAddr;
address userAddr;

function f() returns (uint amount){
//address not resolved!
//tringing
    conAddr = hex"0xf2bd5de8b57ebfc45dcee97524a7a08fccc80aef";
    userAddr = hex"0xca35b7d915458ef540ade6068dfe2f44e8fa733c";
    
    return MappingExample(conAddr).balances(userAddr);
}

}

結構體

solidity 和結構體自定義類型
不能聲明一個struct同時將這個struct做爲這個struct的一個成員。這個限制是基於結構體的大小必須是有限的。

雖然數據結構能做爲一個mapping的值,但數據類型不能包含它自身類型的成員,由於數據結構的大小必須是有限的。

須要注意的是在函數中,將一個struct賦值給一個局部變量(默認是storage類型),實際是拷貝的引用,因此修改局部變量值時,會影響到原變量。

固然,你也能夠直接經過訪問成員修改值,而不用必定賦值給一個局部變量,如campaigns[comapingnId].amount = 0

數組

storage來講 元素的類型是任意的 類型能夠是數組映射或者數據結構

可使用new新建momory數組 可是不能更改 storag能夠修改。 Error: Expression has to be an lvalue.

內聯數組

數組字面量,是指以表達式方式隱式聲明一個數組,並做爲一個數組變量使用的方式。下面是一個簡單的例子:
pragma solidity ^0.4.0;

contract C {
function f() {
g([uint(1), 2, 3]);
}
function g(uint[3] _data) {
// ...
}
}

abi不能支持數組 之後也許會解除限制

數組的方法 和屬性

length屬性
不能訪問超出數組長度的方式

push方法
storage的變長數組都有一個push

限制的狀況處理:
web3.js能返回數據 可是不能返回數據 一種方法是使用一個很是大的靜態數組

數據位置

calldata
存儲的是函數 而且是隻讀的 不會被永久存儲 外部函數的參數被強制指定爲calldata 效果與momoey差很少

強制的數據位置(Forced data location)

外部函數(External function)的參數(不包括返回參數)強制爲:calldata
狀態變量(State variables)強制爲: storage

默認數據位置(Default data location)

函數參數括返回參數:memory
全部其它的局部變量:storage

引用類型

複雜類型
不一樣於以前的類型 複雜類型佔據更大的空間 超256 拷貝佔據空間更多的空間

函數

1.賦值
2.參數傳遞
3.函數調用中返回

外部函數
內部函數
函數的完整定義
function ( ) {internal(默認)|external} [constant] [payable] [returns ( )]
默認是內部調用external 沒有返回必須值省略結果returns

f和this.f的區別
前者是內部調用 後者是外部調用
這裏的this和大多數語言向違背

library知識總結

library是什麼呢。
library引入時爲何使用using,這和文件引入的import有何區別。
library內的函數全是internal的。
library內的函數,他的參數函數爲何是internal的,不該該是external的?
uint[]是什麼類型,不能寫作[]uint
memory又是什麼呢,爲何map函數明明是兩個參數,但只須要傳一個呢。

枚舉

用戶自定義類型 應當至少一名成員

十六進制字面量

因爲一個字節是8位,因此一個hex是由兩個[0-9a-z]字符組成的。因此var b = hex"A";不是成雙的字符串是會報錯的。

轉換

pragma solidity ^0.4.0;

contract HexLiteralBytes{
function test() returns (bytes4, bytes1, bytes1, bytes1, bytes1){
bytes4 a = hex"001122FF";

return (a, a[0], a[1], a[2], a[3]);

}
}

Result: "0x001122ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000000"
Transaction cost: 21857 gas.
Execution cost: 585 gas.
Decoded:
bytes4: 0x001122ff
bytes1: 0x00
bytes1: 0x11
bytes1: 0x22
bytes1: 0xff

字符串

字符串字面量是指由單引號,或雙引號引發來的字符串。字符串並不像C語言,包含結束符,foo這個字符串大小僅爲三個字節。

定長字節數組

字符串的長度也是能夠邊唱的 能夠轉化爲字節

pragma solidity ^0.4.0;

contract StringConvert{
function test() returns (bytes3){
bytes3 a = "123";

//bytes3 b = "1234";
  //Error: Type literal_string "1234" is not implicitly convertible to expected type bytes3.

  return a;

}
}

轉義字

字符串字面量支持轉義字符,好比\n,\xNN,\uNNNN。其中\xNN表式16進制值,最終錄入合適的字節。而\uNNNN表示Unicode碼點值,最終會轉換爲UTF8的序列。

小說

定點數
小數字面量
支持的運算符
數字字面量
大多數含小數的十進制,均不可被二進制準確表達,好比5.3743的類型多是ufixed8*248。若是你想使用這樣的值,須要明確指定精度x + ufixed(5.3743),不然會報類型轉換錯誤。
字面量截斷
字面量轉換
pragma solidity ^0.4.0;

contract IntegerLiteralConvert{
function literalTest(){
uint128 a = 1;
//uint128 b = 2.5 + a + 0.5;
//Error: Operator + not compatible with types rational_const 5/2 and uint128
}
}

字節數組

定長字節數組
bytes1, ... ,bytes32,容許值以步長1遞增。byte默認表示byte1。
運算符
<=,<,==,!=,>=,>,返回值爲bool類型。
&,|,^(異或),~非
成員變量
.length表示這個字節數組的長度(只讀)。
動態大小的字節數組
bytes: 動態長度的字節數組,參見數組(Arrays)。非值類型1。

string: 動態長度的UTF-8編碼的字符類型,參見數組(Arrays)。非值類型[valueType]。

習慣說: bytes用來存儲任意長度字節數據 若是長度確認 儘可能使用byte1到byte32之間的一個

地址

以太坊地址的長度,大小20個字節,160位,因此能夠用一個uint160編碼。
地址是全部合約的基礎。 全部的合約都會繼承地址對象

運算符

地址類型成員
屬性:balance
函數:send(),call(),delegatecall(),callcode()。
字面量

39-41位長的沒有經過合法檢查 會提出警告
balance

核心點是添加一個雙引號

this
返回當前合約

send
像某個地址發錢 單位是WEI

充值合約
pragma solidity ^0.4.0;

//請注意這個僅是Demo,請不要用到正式環境
contract PayTest {
//獲得當前合約的餘額
function getBalance() returns (uint) {
return this.balance;//0
}

//向當前合約存款
function deposit() payable returns(address addr, uint amount, bool success){
    //msg.sender 全局變量,調用合約的發起方
    //msg.value 全局變量,調用合約的發起方轉發的貨幣量,以wei爲單位。
    //send() 執行的結果
    return (msg.sender, msg.value, this.send(msg.value));
}

}

這個合約實現的是充值。this.send(msg.value)意指向合約自身發送msg.value量的以太幣。msg.value是合約調用方附帶的以太幣。

深度不超過1024 若是gas不夠執行失敗 使用這個方法檢查成功與否 執行失敗 全部交易回撤
call(),delegatecall(),callcode()都是底層的消息傳遞調用,最好僅在萬不得已才進行使用,由於他們破壞了Solidity的類型安全。
他們的區別在於call能夠調用支持的abi協議 call方法返回一個bool值 delegatecall僅僅執行另外一個工具箱 callcode訪問權限收到限制

布爾

!邏輯非

&& 邏輯與

|| 邏輯或

== 等於

!= 不等於

值引用和引用類型

Solidity是一個靜態類型的語言,因此編譯時需明確指定變量的類型. solidity提供了一些基本類型來組合複雜類型

值類型(Value Type)
值類型包含

布爾(Booleans)
整型(Integer)
地址(Address)
定長字節數組(fixed byte arrays)
有理數和整型(Rational and Integer Literals,String literals)
枚舉類型(Enums)
函數(Function Types)

不定長字節數組(bytes)
字符串(string)
數組(Array)
結構體(Struts)

智能合約源文件概述

合約相似面嚮對象語言中的類。
支持繼承

狀態變量(State Variables),函數(Functions),函數修飾符(Function Modifiers),事件(Events),結構類型(Structs Types)和枚舉類型(Enum Types)。

相關文章
相關標籤/搜索