52
ethereum smart contract 林林林

以太坊智能合約撰寫簡單教學

Embed Size (px)

Citation preview

Page 1: 以太坊智能合約撰寫簡單教學

ethereum smart contract

林修平

Page 2: 以太坊智能合約撰寫簡單教學

線上即時編譯• online compiler :

https://ethereum.github.io/browser-solidity/

Page 3: 以太坊智能合約撰寫簡單教學

手動編譯 (using geth)

• connect to console

• set up a chain

• connect to main chain : go-ethereum/build/bin/geth console

• connect to testnet : go-ethereum/build/bin/geth --testnet console

• build a private net : go-ethereum/build/bin/geth --datadir “your_directory" --rpc --rpcport port --rpccorsdomain "*" --port "30303" --nodiscover --ipcapi "admin,db,eth,debug,miner,net,shh,txpool,personal,web3" --rpcapi "db,eth,net,web3" --autodag --networkid number --nat "any" console

Page 4: 以太坊智能合約撰寫簡單教學

手動編譯 (using geth)

• in console

• var contractABI = web3.eth.contract([{contractABI}]);

• var contract123 = contractABI.new( parameters, {from: address, data: bytecode, gas: gas }, callback_function)

Page 5: 以太坊智能合約撰寫簡單教學

快速介紹

Page 6: 以太坊智能合約撰寫簡單教學

快速介紹• 貨幣單位: ether

• address :代表一個 account ,可提錢存錢。分為兩種• 使用者• 合約:

• 會有一份 code 和這個 address 綁在一起。• 同一份 code 在不同 address 上就是不同合約。

• transaction :• 可為單純的送錢• 或用來執行合約(同時也可附錢)

Page 7: 以太坊智能合約撰寫簡單教學

快速介紹• 合約:分為狀態( state )和動作( function )

• state :可永久保留,使用者用來記錄合約的相關資訊• function :對這合約狀態產生影響的動作• 註: Ethereum 的設計機制是不鼓勵儲存 state 的,用到 state 耗費的 gas 特別高。

• 因為每個節點都要儲存一份一樣的資料,如果大家都用到大量的儲存,會造成所維護的鏈非常笨重。• gas

• 每一個函式都是由許多單元運算所組成,每個運算都有固定的算力消耗,因此每執行一次函式都要付出相對應該付的費用,而這個費用是用 ether 來付。• 當函式寫好, gas 耗費就已經固定,唯一可變的是每單位 gas 所價值的 ether 。你每單位

gas 給越多 ether ,礦工更願意收入你的 transaction ,也就更快被放進鏈裡。

Page 8: 以太坊智能合約撰寫簡單教學

快速介紹• 如果是公鏈,每分錢每分貨,每個狀態都要花成本,所以會斤斤計較• 如果使用的是私鏈呢?

• gas 好像就沒有這麼重要了?• 因為要有多少 ether 有多少 ether

• 但是狀態的儲存就可以想存多少就存多少嗎?• 沒錯,但同樣的,最後儲存的成本還是會轉嫁到每個節點身上。

Page 9: 以太坊智能合約撰寫簡單教學

運作過程

Page 10: 以太坊智能合約撰寫簡單教學

• 狀態• 函式(動作):操作資料• 邏輯:用函式達成目的

運作過程

Page 11: 以太坊智能合約撰寫簡單教學

運作過程

contract Count123{uint counts(0);function incre(){

counts = count + 1;

}}

Transaction(Deploy)

Page 12: 以太坊智能合約撰寫簡單教學

運作過程

contract Count123{uint counts(0);function incre(){

counts = count + 1;

}}

Page 13: 以太坊智能合約撰寫簡單教學

運作過程

Transaction(Invoke)

Count123.incre()contract Count123{

uint counts(0);function incre(){

counts = count + 1;

}}

Page 14: 以太坊智能合約撰寫簡單教學

以 slock.it 為例,寫個簡單的租車合約slock.it

運作過程

Page 15: 以太坊智能合約撰寫簡單教學

狀態宣告

Page 16: 以太坊智能合約撰寫簡單教學

狀態宣告

contract bikeRenting {

address public owner;

address public currentRenter;

uint public expireTime;

}

Page 17: 以太坊智能合約撰寫簡單教學

• 型態 能見度 變數名稱 ( type visibility variable_name)

• type : bool, int, uint, address, mapping, bytes, string, struct

• visibility : public, private(default)

• public : 外界可以直接讀取其值(in contract) uint public totalCount = 3;(in console) mycontract.totalCount(); //3(in other contract) thecontract.totalCount() //3

• variable_name

狀態宣告

Page 18: 以太坊智能合約撰寫簡單教學

• Array in solidity :

• address[] owners; address[3] threeOwners;

• 支援 push : owners.push(address)

• delete— 將該位置的值變成 0 : delete owners[2]

• 缺點:長度不變,只將該位置變 0

• 每刪一個就將該位置後面的都往前移• 在某些情況下改用 mapping

狀態宣告

Page 19: 以太坊智能合約撰寫簡單教學

• mapping :

• mapping(typeA => typeB) map123;

• mapping(address => uint) deposits;

• deposits[0x123456789abcdef] = 10;

• 初始值、未指定過的,皆為零

狀態宣告

Page 20: 以太坊智能合約撰寫簡單教學

• 專有變數:• 單位: ether 、 finney 、 wei

• 時間: seconds 、 weeks 、 years 、 now

• now :目前時間• 更精確來說,是這一個區塊的時間,小心使用

狀態宣告

Page 21: 以太坊智能合約撰寫簡單教學

• 專有變數:• msg :這個 transaction 相關的資訊

• msg.sender :該 transaction 的發起人• msg.value :該 transaction 附帶的錢

• address 相關• address.balance :得知該 address 的餘額• address.send(amount) :送錢到該 address

狀態宣告

Page 22: 以太坊智能合約撰寫簡單教學

• 特殊函式:• throw

• 將該 transaction 做的改變還原,並沒收其附上的所有的gas

狀態宣告

Page 23: 以太坊智能合約撰寫簡單教學

初始化

Page 24: 以太坊智能合約撰寫簡單教學

初始化contract bikeRenting {

address public owner;address public currentRenter;uint public expireTime;uint public unitPrice;

function bikeRenting(uint _unitPrice){

owner = msg.sender;

currentRenter = 0x0;

expireTime = now;

unitPrice = _unitPrice;

}

}

Page 25: 以太坊智能合約撰寫簡單教學

• constructor :• 和 contract 名稱同名• 只會執行一次• 用來做合約剛跑起來所需要做的一次性工作• 非必需

初始化

Page 26: 以太坊智能合約撰寫簡單教學

函式

Page 27: 以太坊智能合約撰寫簡單教學

contract bikeRenting {address public owner;address public currentRenter;uint public expireTime;uint public unitPrice;function bikeRenting(uint _unitPrice){

…}

function rent() payable returns(bool){if(currentRenter != 0x0) return false;else{

if( (msg.value/1 ether) / unitPrice < 1) return false; else {

expireTime = now + 15 minutes * (msg.value/1 ether)/unitPrice; currentRenter = msg.sender; rentingRecord(msg.sender, now, (msg.value/1 ether)/unitPrice);

}}

}

}

目前是否有人租借

一單位多少時間檢查是否有給滿一單位的錢

函式

Page 28: 以太坊智能合約撰寫簡單教學

function functionName(parameter1, parameter2, …) returns(type) {

}

• parameters : bool a, uint b, address c, …

• returns :• 非必需• function foo() returns(uint, address, bool) {…}

• return (1, 0x0, true);

函式

Page 29: 以太坊智能合約撰寫簡單教學

• 函式的能見度( visibility ):• public(default)

• private :只有這個 contract

• internal :這個 contract 和繼承這個 contract 的 contract

• external :除了這個 contract 和繼承這個 contract 的contract之外,即 internal 的相反

• payable :函式是否可以收錢

函式

Page 30: 以太坊智能合約撰寫簡單教學

contract bikeRenting {address public owner;address public currentRenter;uint public expireTime;uint public unitPrice;function bikeRenting(uint _unitPrice){…}function rent() payable returns(bool){…}

function isInUse(bool ifWantToRent) payable returns(bool){if( expireTime > now ) return true;else{

currentRenter = 0x0;if( ifWantToRent == true) rent();

}}

}

檢查是否有人租借

ifWantToRent 由函式呼叫者給定,給 true 表示要順便租借,則繼續執行 rent() 函式

如果過期時間在未來,表示有人租借

函式

Page 31: 以太坊智能合約撰寫簡單教學

函式• 要確保 isInUse 這個函式的呼叫人有直接租借的權利,因為他付出成本來確認是否還在租借• 為什麼需要別人來確認?

• 因為智慧合約並不是活的,它只有在收到呼叫的時候才會動起來。• 所以即便有寫明 expireTime ,智慧合約也沒辦法在 expireTime 到的時候自動執行,需要外界觸發。

• 但靠外界觸發,如果同時有一個人以上呼叫租借的函式,區塊鏈沒辦法保證誰的 transaction 會先被執行。• 所以呼叫 isInUse 函式來檢查的人付出了成本,我們要確保檢查的人如果剛好要租借可以有最優先的順序。

Page 32: 以太坊智能合約撰寫簡單教學

contract bikeRenting {address public owner;address public currentRenter;uint public expireTime;uint public unitPrice;function bikeRenting(uint _unitPrice){…}function rent() payable returns(bool){…}function isInUse(bool ifWantToRent) payable returns(bool){…}

function collectMoney() {if( msg.sender == owner){

owner.send(this.balance) ;}

}

}

確認只有 owner 可以領錢

函式

Page 33: 以太坊智能合約撰寫簡單教學

執行的權限

Page 34: 以太坊智能合約撰寫簡單教學

執行的權限• 合約並沒有原生的限制函式呼叫者的設計,任何人都可以送

transaction 到合約,所以必須在函式內做權限的控制。• 用 msg.sender 和有權限的 address 進行比對function foo() {

if(msg.sender == owner){

}

}

Page 35: 以太坊智能合約撰寫簡單教學

重複性的動作

Page 36: 以太坊智能合約撰寫簡單教學

• modifier

• _;

• 填補 code

• 如果要使用 modifier : function bar() foo1() {…}

• 使用多個 modifier : function bar() foo1() foo2() foo3(){…}

重複性的動作

modifier foo1 { do_something _; or_do_something_here}

Page 37: 以太坊智能合約撰寫簡單教學

contract foo1 {

modifier ownerCheck { if(msg.sender == owner ) _;}function collectMoney() ownerCheck {

owner.send(this.balance) ;}

}

重複性的動作

Page 38: 以太坊智能合約撰寫簡單教學

contract modifierTest{modifier foo1(){

foo1before;_;foo1after;

}modifier foo2(){

foo2before;_;foo2after;

}function bar() foo1() foo2() {

bar;}

}

重複性的動作

呼叫 bar 執行順序:foo1beforefoo2beforebarfoo2afterfoo1after

Page 39: 以太坊智能合約撰寫簡單教學

由合約產生合約

Page 40: 以太坊智能合約撰寫簡單教學

由合約產生合約

contract foo{ … }

address newFooAddr = new foo();

• 回傳的是一個 address

• foo foo1 = foo(newFooAddr); ,回傳的是一個 contract

• 注意:產生 contract 的 gas通常很多,而這些 gas 都算在該次的transaction 上,所以當函式會產生新合約,記得給夠多的 gas

Page 41: 以太坊智能合約撰寫簡單教學

fallback 函式

Page 42: 以太坊智能合約撰寫簡單教學

fallback 函式

• 如果沒有執行任何一個合約裡的函式,則執行 fallback 函式• 函式名給錯、單純送錢

• 存在弱點• 執行的邏輯是由該合約的人決定

function () {…

}

Page 43: 以太坊智能合約撰寫簡單教學

可能危險

Page 44: 以太坊智能合約撰寫簡單教學

可能危險• throw when address.send() fails

• 所有目前執行完的結果都會被還原

for(uint i=0; i<investorsCount; i++) {

if( inverstors[i].send(100) == false )

throw;}

Page 45: 以太坊智能合約撰寫簡單教學

可能危險• throw when address.send() fails

• why address.send() fail?

• 1. out of gas

• supply enough gas

• 2. callstack

• 1024 layer

Page 46: 以太坊智能合約撰寫簡單教學

可能危險• throw when address.send() fails

• use a withdraw pattern

• still, this solution leaves the problems mentioned to the msg sender

function withdraw(amount) {if( balances[msg.sender] >=

amount ) {msg.sender.send(amount);

balances[msg.sender] -= amount;}

}

Page 47: 以太坊智能合約撰寫簡單教學

可能危險• shared state between external call and external callable

functionsfunction extCall() {…external_call();…if(shared_state) {

…}…

}

function extCallable(){do_something_on_shared_state…

}

呼叫其他合約的函式

該函式中又呼叫了我們合約的這個函式

Page 48: 以太坊智能合約撰寫簡單教學

可能危險• state corruption

• 1. sum of sizes of 1st and 2nd state variables are less than 256 bytes

• 2. first variable is not a signed interger or bytesXX typefunction extCall() {

uint32 a;uint32 b;function run() returns(uint32){

a--;return b;

}}

fixed after compiler version 0.4.4

Page 49: 以太坊智能合約撰寫簡單教學

Misc

• selfdestruct(recipient)

• 終結合約,將資料和程式碼清空,並將餘額轉給 recipient ( address )。• event : 將資料寫入 transaction receipt ,可用做紀錄或搜尋

• event paymentRecord(address indexed buyer, uint value)

• indexed屬性:寫入 topics中• 如果寫入的資料超過 32 byte ,則會改存資料的雜湊值

• transaction receipt :• data

• topics :搜尋時可以用此區域的值來當篩選條件

Page 50: 以太坊智能合約撰寫簡單教學

Misc

Page 51: 以太坊智能合約撰寫簡單教學

• contract inheritance : contract Final is most-base-like, …, most-derived {…}

Misc

Page 52: 以太坊智能合約撰寫簡單教學

TIPS

• use delete on array to delete all elements

• --vmdebug, --verbosity

• --targetgaslimit