以太坊智能合約學習筆記(一)

智能合約能夠簡單的理解爲一段可執行的程序片斷,具體的代碼通過 Solidity 編寫以後,發佈到區塊鏈上。而以太坊的智能合約也能夠理解爲一個特殊的交易(包括可執行代碼的),被髮送出去後會被礦工打包記錄在某一個區塊中,當須要調用這個智能合約的方法時只須要向這個智能合約的地址發送一筆交易便可。 由於觸發的條件和打錢地址都已經被編寫在代碼裏,存儲在區塊鏈上,因此能夠最大程度的排除人爲因素的干擾。git

合約的基本結構

程序版本(Version Pragma):Solidity 大多都是開源的程序,在代碼中加上程序版本是爲了方便社區合做。描述程序版本的規則和 npm 的同樣。github

pragma solidity ^0.4.19;

合同(contract)聲明:合同相似於面嚮對象語言中的類(Class)。npm

contract SimpleStorage {

}

狀態變量(State variable)聲明:狀態變量是永久存儲在合同存儲中的值。安全

contract SimpleStorage {
    uint storedData; // State variable
}

函數(function)聲明:函數是合約內代碼的可執行單元。網絡

contract SimpleStorage {
    function simpleFn() {
    }
}
  • 返回一個或多個參數
  • 直接賦值返回的命名參數
// 返回一個值
    function oneParameter() returns(uint){
        return 1;
    }
    
    // 返回多個返回值
    function twoParameters() returns(uint, uint) {
        return (1,2);
    }
    
    // 命名參數直接賦值
    function namedParameter() returns(uint foo, uint bar) {
        foo = 1;
        bar = 2;
    }
    
    // 獲取返回值 
    function get() {
        uint one = oneParameter();
        var (two1, two2) = twoParameters();
        var (named1, named2) = namedParameter();
    }

類型

boolfalse / trueoracle

操做符:! , && , || , == , !=編輯器

uinit/int:無符整型、有符整型函數

操做符:學習

  • 比較:<= , < , == , >= , >
  • 位計算:& , | , ^ , ~
  • 計算:+ , - , * , / , % , **

注意:區塊鏈

  1. 整型除法運行會捨去小數位。
  2. solidity 暫時沒有浮點數。有定點數,但支持性很差。
int x = 7/4 // 報錯。計算出來的值是有理數,但 solidity 根本沒有浮點數等類型來表示有理數,更不能將有理數轉換爲 int 類型。

// 正確方法,需先定義類型
int a =7;
int b = 4;
int c = a/b; 

c == 1.75 // 報錯。根本沒有浮點數。
c == 1 // true。捨去小數位

address:在以太坊中帳戶有兩種類型:普通帳戶智能合約帳戶。普通帳戶只存儲 ETH 的帳戶,智能合約帳戶不只存儲 ETH,同時也有能夠運行的代碼。

以太坊地址是 20 字節的十六進制的值,該值範圍在 2^256 之內。能夠經過isValidAddress檢測是否有效。

address x = 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF

function isValidAddress(address) {
  return /^0x[0-9a-fA-F]{40}$/.test(address)
}

isValidAddress(x)

成員:

  • address.banlance (uint256):地址餘額,單位 Wei。banlance 的值是 readonly 的,調用 payable 函數入帳,調用 address.transfer() 等出帳,以太坊自動計算新的餘額。
  • address.transfer(uint256 value) :給 address 轉帳 value(Wei),且調用異常會拋出。
  • address.send(value):和 transfer 相似,但調用後的異常將不會被返回,只會返回一個 false。
  • address.call, address.callcode, address.delegatecall:智能合約相互調用時使用

注意:在 solidity 源碼中,address 不須要加雙引號。但在 Remix 的對話界面中輸入 address 時,務必加上雙引號,不然會報錯,且報錯的消息很是詭異。

全局變量

ether 變量:1 ether 表明數字 1*10^x18 ,而不是幣的單位。

  • wei == 1
  • szabo == 10^12 wei
  • finney == 10^15 wei
  • ether == 10^18 wei

時間變量:1 seconds 表明數字 1,而不是時間的單位。同理 1 years 表明的是數字 3652460*60, 而不是現實世界中的一年,由於現實世界中有會有 閏秒。如合同中需用到準確的一年,須要外部預言機(oracle)。

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

block:塊

  • block.blockhash(uint blockNumber) returns (bytes32): 傳入 blockNumber,返回塊的哈希值
  • block.coinbase (address): 挖到當前塊礦工的地址
  • block.difficulty (uint): 當前塊的難度
  • block.gaslimit (uint): 當前塊最多的 gas
  • block.number (uint): 當前塊是第幾個
  • block.timestamp (uint): 當前塊建立的時間戳
  • now (uint): block.timestamp 的別名

msg: 當執行某一個函數的時候,函數想要知道調用函數的數據信息

  • msg.data (bytes): 包括函數名字等等,一些沒有通過加工的信息。
  • msg.gas (uint): 函數調用方攜帶的 gas
  • msg.sender (address): 函數調用方的地址
  • msg.sig (bytes4): 整個 msg.data 的前 4 個 byte
  • msg.value (uint): 函數調用方攜帶的 gas,以 wei 爲單位計價。

關鍵詞

  • constant 用於變量: 代表當前變量不可修改。若是修改,編輯器會報錯。
  • constant 用於函數: 代表當前函數中,不該該修改狀態。但要十分當心,由於即使修改了,編譯器也不會報錯。
  • view : 和 constant 用於函數時功能同樣。另外使用 Remix 時,能夠方便查看函數返回值。使用 view 時,Remix 會把調用函數的輸出值放在函數右邊顯示,而不是在 details 裏。
  • payable: 代表調用函數能夠接受以太幣。
  • this: 指向的是當前合同的 address
  • revert(): 函數執行失敗,須要經過調用 revert() 拋異常告訴函數調用方。調用後恢復合同狀態,並將剩餘 gas 返還。等同於 throw

省幣祕訣

當你激活一個智能合約的時候,你在要求整個網絡內的每一個礦工個體分別執行裏面的運算。這會花費他們的時間和精力,Gas是你爲這項服務向礦工們支付的機制。 報酬是小額的以太幣,想要運行智能合約的人的須要支付報酬來使合約工做。付款款項(單位以太幣)= Gas數量(單位Gas) x Gas price(單位以太幣/Gas) 智能合約越複雜(計算步驟的數量和類型,佔用的內存等),用來完成運行就須要越多Gas。

Gas是由一開始發起transaction的地址進行支付。再好比"合約A調用合約B再調用合約C"這個過程當中所產生的全部計算步驟所消耗的Gas也都是由調用合約A的人來承擔的。

  • fn() 代替 this.fn():經過 this.fn() 調用函數,在 EVM 底層是經過 msg來調用合約函數的。相對於直接調用 fn() 花費的 gas 更多。
  • 減小重複計算。Solidity 編譯器沒有對重複計算作優化,需開發者手動使用臨時變量保存重複計算的值。
function(int a, int b){
    // 錯誤。應該使用 int x = a + b 減小重複計算
    if(a + b > 0) {
        int y = a + b; 
    }
}

安全

  • 必定要把內部變量修改完成以後,再給外部錢。涉及到以太坊智能合約的re-entry攻擊的問題。
frank.transfer(salary);
// 錯誤,應該將先修改內部變量,再 transfer。
lastPayday = lastPayday + payDuration;

其餘

  • 合約是中介:因爲調用函數的動做是在挖礦時執行的,因此Solidity 沒有原生定時器,不經過合約自己自動觸發函數執行。應該將合約看作一箇中介,須要外部來觸發合約函數的執行。
  • 本地狀態變量聲明提高:相似於 JS 用 val 聲明變量。
contract SimpleStorage {
    function set(uint data){
        if (true) {
            uint temp = 1; // 本地狀態變量
        }
        uint temp; // 報錯,由於聲明本地狀態變量的做用域是函數,而不是 {}。
    }
}

代碼見/payroll.sol

參考資料:

相關文章
相關標籤/搜索