智慧合約和solidity簡介
智慧合約
首先要強調的是,以太坊和比特幣的區別。比特幣主要用途是用於進行交易,定位是數字貨幣的支付功能。以太坊相比比特幣是一個巨大的提升,將區塊鏈的應用邊界從貨幣和支付擴大到了更廣的領域(通過智慧合約實現)。
區塊鏈2.0是更巨集觀的對整個市場去中心化,利用區塊鏈技術來轉換許多不同的數字資產,通過轉讓來建立不同資產的價值。區塊鏈技術的去中心化賬本功能可以被用來建立、確認、轉移各種不同型別的資產及合約。幾乎所有型別的金融交易都可以被改造成在區塊鏈上使用,包括股票、私募股權、眾籌、債券和其他型別的金融衍生品如期貨、期權等。
那什麼是智慧合約呢?
智慧合約不是一定要用區塊鏈來實現,很久之前就已經出現了:比如微信和支付寶的信用卡自動還款,也是一種智慧合約。當還款日到了,還款條件也滿足(支付寶的餘額寶、微信支付餘額或者銀行儲蓄卡中餘額充足),系統會自動進行還款,這些都是智慧合約,也沒有使用區塊鏈技術。
而使用區塊鏈的話,結合不可篡改資料無法刪除、修改,只能新增,保證了歷史的可追溯,同時作惡的成本將很高,因為其作惡行為將被永遠記錄,同時擁有高可靠行,我們不用擔心繫統在條件被滿足時不執行合約;然後就是去中心化和給我們帶來的全網備份,完備的記錄完全可以支援支援事後的審計,避免了中心化因素的影響。所以可以想象,以太坊通過在區塊鏈上提供了圖靈完備語言,打開了多麼大的一個市場。
舉一個例子,看看以太坊上的應用:
謎戀貓是世界首款區塊鏈遊戲。“區塊鏈”是支援類似比特幣這樣的加密貨幣的運作技術基礎。儘管謎戀貓不是數字貨幣,但它也能提供同樣的安全保障:每一隻謎戀貓都是獨一無二的,而且100%歸您所有。它無法被複制、拿走、或銷燬。
迷戀貓官方將他們設計的迷戀貓合約釋出到了以太坊上,並公佈了合約內容,其中規定了0代貓只能有他們的CEO、COO來產生,並限定的0代貓最多產生的數量,以及玩家之間如何交易貓,兩隻貓咪之間如何繁育、貓咪備孕週期等等規則,以上規則已經在以太坊上做了公證,以後只能按照這套規則來進行遊戲。
在區塊鏈平臺上的每隻貓咪其實只是存在以太坊中的一段貓咪基因編碼,該段基因編碼決定了貓咪的屬性、外貌等,迷戀貓官方在以太坊之外,提供了一個網站,在網站上將這些貓咪根據其基因編碼展示出來。玩家可以使用自己的以太坊帳戶去購買這些貓咪,並將自己的貓咪去與其他玩家的貓咪繁育以產生下一代貓咪,或者繼續將其拍賣,貓咪之間繁育之後產生的下一代貓咪,其基因編碼是受其兩隻上一代貓咪基因編碼影響的,由於貓咪的基因編碼在以太坊上都是公開的,所以迷戀貓官方並未公佈下一代基因編碼的生成規則,這樣玩家也無法人為的控制,使用兩隻特定基因的貓來繁育出具有特定稀有屬性的下一代。 玩家在以太坊的帳戶是由一段特殊的金鑰保護的,玩家進行購買貓咪、繁育貓咪操作事,這些操作資訊都會被髮布到以太坊上時,並且這些操作行為都會使用玩家的金鑰進行認證,並在以太坊上記錄,其公之於眾。所以所有人都可以看到玩家A買了一隻貓咪Kitty101,玩家B將他的貓咪Kitty201與他的Kitty202進行了繁育,並生下了一隻什麼樣基因的Kitty301。
在此之後,區塊鏈上釋出了不少遊戲,而今年暑假的兩款遊戲(Fomo3d和LastWinner)吸引了大量的眼球,同時也可能預示著在以太坊上游戲開發的沒落。Fomo3d的黑客攻擊技巧可以參看1和2。
由此可以看到,比太幣和以太坊底層框架本身,當前攻擊較少;但是在以太坊上,智慧合約本身的程式碼問題是安全的重災區。
那接下來,我們討論solodity語言,對智慧合約形成一個基本的瞭解。
基本語法以及使用
- test_2.sol 最基本的用法
以下的程式碼中,除了第一行和第二行中的contract,其實就和其他我們比較熟悉的語言差不多。將程式碼拷貝到remix之中,然後deploy到鏈上去,就是智慧合約了。
pragma solidity ^0.4.0;
contract C {
//交換傳入值的順序並返回
function f(uint key, uint value) returns (uint, uint){
return (value, key);
}
function g() public view returns (uint, uint){
//任意順序的通過變數名來指定引數值
return f({value: 2, key: 1});//2,1
}
function h() public view returns (uint){
uint a = 3;
uint b = 4;
return a+b;
}
}
- 以上是單個合約。那我們來試一下在不同的合約之中進行互動。(test_send.sol和simple_set.sol)
pragma solidity ^0.4.0;
//import 'test_send.sol';
contract SimpleStorage {
uint storedData;
function set(uint x) public{
storedData = x;
}
function get() public constant returns (uint retVal) {
return storedData;
}
}
contract SimpleSet {
address instance_address = 0x9351ca1b4cc9c3d11db77374de3987b5abf3d4c8;
SimpleStorage target = SimpleStorage(instance_address);
uint readData;
function read() public view returns (uint) {
readData = target.get();
return readData;
}
function write(uint x) {
target.set(x));
}
}
可以看到,在simple_set中,可以呼叫並修改另外一個合約中的資料。下圖展示的是先通過simple_set修改storeData的值,然後在simpleStorage中去讀取,結果顯示了變化。
- 鑄幣(coin_mint.sol)
pragma solidity ^0.4.0;
contract Coin {
//關鍵字“public”使變數能從合約外部訪問。
address public minter;
mapping (address => uint) public balances;
//事件讓輕客戶端能高效的對變化做出反應。
event Sent(address from, address to, uint amount);
//這個建構函式的程式碼僅僅只在合約建立的時候被執行。
function Coin() {
minter = msg.sender;
}
function mint(address receiver, uint amount) {
if (msg.sender != minter) return;
balances[receiver] += amount;
}
function send(address receiver, uint amount) {
if (balances[msg.sender] < amount) return;
balances[msg.sender] -= amount;
balances[receiver] += amount;
Sent(msg.sender, receiver, amount);
}
}
對這份合約進行操作,譬如通過賬戶7578C將合約部署到鏈上,那麼可以檢視合約的minter;minter可以自己鑄幣,相當於是發給任何使用者一些幣;有幣的使用者又可以進行轉賬。
- 複雜的資料型別 (origin_send.sol)
msg.sender和tx.origin
pragma solidity ^0.4.18;
contract Demo {
event logData(address);
function a(){
logData(tx.origin);
logData(msg.sender);
}
}
contract Demo2{
Demo demo222;
function Demo2(address aimAddr) {
demo222 = Demo(aimAddr);
}
function exp(){
demo222.a();
}
}
tx.origin是一個address型別,表示交易的傳送者,msg.sender則表示為訊息的傳送者。在同一個合約中,他們是等價的。但是在不同合約中,tx.origin表示使用者地址,msg.sender則表示合約地址。
上述程式碼中有兩個合約,需要分別部署。並且demo2在部署時需要傳入引數。
接下來看一下address。
以太坊中的地址的長度為20位元組,一位元組等於8位,一共160位,所以address其實亦可以用uint160來宣告。
屬性:
<address>.balance, 地址的餘額(單位為:wei)
<address>.transfer,傳送以太幣(單位為:wei)到一個地址,如果失敗會停止並丟擲異常。