BSN智能合約開發培訓-CITA(三)

1586252312303726.jpg

  1

  智能合約的定義

  1994年,計算機科學家和密碼學家 Nick Szabo 首次提出“智能合約”概念。它早於區塊鏈概念的誕生。Szabo 描述了什麼是“以數字形式指定的一系列承諾,包括各方履行這些承諾的協議”。雖然有它的好處,但智能合約的想法一直未取得進展——主要是缺乏可以讓它發揮出作用的區塊鏈。

  直到 2008 年,第一個加密貨幣比特幣纔出現,同時引入了現代區塊鏈技術。區塊鏈最初是以比特幣的底層技術出現的,各種區塊鏈分叉導致發生很大的變化。智能合約在 2008 年依然無法融入比特幣區塊鏈網絡,但在五年後,以太坊讓它浮出水面。從此,涌現出了各種不同形式的智能合約,其中以太坊智能合約使用最廣。

  自以太坊開始,區塊鏈是一個運行着智能合約的分佈式平臺:應用程序可以按照程序運行,不存在故障、審查、欺詐或第三方干預的可能性。智能合約給予了我們使用區塊鏈技術來驗證我們運行的代碼的執行情況的能力。

  智能合約定義

  智能合約(英語:Smart contract )是一種旨在以信息化方式傳播、驗證或執行的計算機協議。智能合約允許在沒有第三方的情況下進行可信交易,這些交易可追蹤且不可逆轉。

  智能合約簡單定義就是智能合約是可以處理 token 的腳本,圍繞它可以發行,轉移和銷燬資產。這裏說的資產是一個泛化的定義,不一定是幣,可以是任何一種虛擬物品(比如應收,支付信息甚至加密貓)和現實世界的物品在區塊鏈上的映射(比如艙單,抵押)。

  CITA 智能合約

  CITA 區塊鏈框架使用的虛擬機 CITA-VM 和 EVM 採取同樣的指令集,所以合約所使用的語言也是 solidity。由於 Ethereum 是目前全球最廣泛的區塊鏈網絡,所以 solidity 也是使用最廣泛的智能合約語言,圍繞它的生態是非常豐富的,包括了合約調試,部署工具和保護合約安全的一些庫。

  這裏再談一下合約是由誰來執行的問題,在公鏈上,比如比特幣或者以太坊,這些合約由我們稱爲“礦工”的參與方強制執行和證明。礦工其實是多臺電腦(也可以稱爲礦機),它們把一項交易(執行智能合約,代幣轉賬等) 以區塊的形式添加到一個公開分賬本上。使用者給這些礦工支付 “Gas”也就是手續費,它是運行一份合約的成本。

  由於 CITA 是針對於企業的開放許可鏈框架,在 CITA 中礦工是出塊節點,使用智能合約所需要的手續費是支付給出塊節點的, gas 在這裏叫做 quota。當然這裏支付比例是可以自定義調整的,具體可以見文檔。同時 CITA 可以調節爲無幣模式,在無幣模式下,不存在手續費。

  2

  智能合約開發

  現在,我們開始智能合約的開發部分,Solidity 與 Javascript 很接近,但它們並不相同。而且不能在一段代碼上強加 JQuery,智能合約是無法調用區塊鏈體系之外的代碼的。同時還有一個特點是,你在開發的時候需要特別注意安全性,因爲在區塊鏈上的交易是不可逆的。

  智能合約定義

  通過一個例子說明基本語法,這裏參考了 ethfans 上的一個例子,如果難以理解的話可以換一個,使用當時 PeckShield 講的一個分餅乾的例子。

  現在,關於我們的第一個例子,我正在考慮一個由電影《時間規劃局》啓發的腳本。電影中,人們生活在一個反烏托邦式的未來,改用時間作爲貨幣流通。他們可以通過掰手腕的方式贏取對手的時間(他們的“手臂”上存儲着時間,輸方的時間將會傳送給贏家),我們也可以這麼做!用智能合約以角力( Wrestling )的方式賺錢。

  首先,solidity 腳本的基礎是下面這段代碼,pragma 指明正在使用的 Solidity 版本。Wrestling 是合約的名稱,是一種與 Javascrip 上的類(class)相似的結構。

  

  我們需要兩個參與者,所以我們要添加兩個保存他們賬戶地址的變量(他們的公鑰),分別是 wrestler1 和 wrestler2 ,變量聲明方式如下。

  

  在我們的小遊戲中,每一輪的比賽,參與者都可以投入一筆錢,如果一個人投入的錢是另一個人的兩倍(總計),那他就贏了。定義兩個玩家是否已經投入的flagwrestler1Played和wrestler2Played以及兩位玩家投入的金額wrestler1Deposit和wrestler1Deposit。

 

  還有判斷遊戲結束與否,贏家和收益的變量。

  

  下面介紹一些關於公鑰/私鑰的規則,在區塊鏈上每一個賬戶都是一對公私鑰,私鑰可以對一個信息進行簽名,從而使這條信息可以被他人驗證,被驗證的時候它的公鑰需要被使用到。在整個簽名和驗證的過程中,沒有信息是加密的,實際上任何信息都是公開課查驗的。

  對於合約裏面的變量,本質上來講,也是可以被公開訪問的。在這裏要注意是的,即使一個變量是私有的,並不是說其他人不能讀取它的內容,而是意味着它只能在合約中被訪問。但實際上,由於整個區塊鏈存儲在許多計算機上,所以存儲在變量中的信息總是可以被其他人看到,這是在區塊鏈中一個很重要額原則。

  另一方面,和很多編程語言很像,編譯器會自動爲公共變量創建 getter 函數。爲了使其他的合約和用戶能夠更改公共變量的值,通知也需要針對不同的變量創建一個 setter 函數。

  現在我們將爲遊戲的每一步添加三個事件。

  開始,參與者註冊;

  遊戲期間,登記每一輪賽果;

  最後,其中一位參與者獲勝。

  事件是簡單的日誌,可以在分佈式應用程序(也稱爲 dapps)的用戶界面中調用 JavaScript 回調函數。在開發過程中,事件甚至可以用於調試的目的,因爲不同於 JavaScript 有console.log() 函數,solidity 中是沒有辦法在 console 中打印出信息的。代碼如下:

  

  現在我們將添加構造函數,在 Solidity 中,它與我們的合約具有相同的名稱,並且在創建合約時只調用一次。在這裏,第一位參與者將是創造合約的人。msg.sender是調用該函數的人的地址。

  

  接下來,我們讓另一個參與者使用以下函數進行註冊:

 

  Require 函數是 Solidity 中一個特殊的錯誤處理函數,如果條件不滿足,它會回滾更改。在我們的示例中,如果變量參與者2等於0x0地址(地址等於0),我們可以繼續;如果參與者2的地址與0x0地址不同,這就意味着某個玩家已經註冊爲對手,所以我們會拒絕新的註冊。可以把它認爲是 solidity 中的if() {} else{}條件判斷。

  再次強調,msg.sender是調用該函數的帳戶地址,並且當我們觸發一個事件,就標誌着角力的開始。

  現在,每一個參與者都會調用一個函數, wrestle(),並投入資金。如果雙方已經玩過這場遊戲,我們就能知道其中一方是否獲勝(我們的規則是其中一方投入的資金必須是另一方的雙倍)。關鍵字payable意味着函數可以接收資金,如果它不是集合,函數則不會接受幣。msg.value是發送到合約中的幣的數量。

 

  請注意,我們不是直接把錢交給贏家,在此情況下這並不重要,因爲贏家會把該合約所有的錢提取出來;而在其他情況下,當多個用戶要把合約中的以太幣提取出來,使用withdraw模式會更安全,可以避免重入,在合約安全部分我們會詳細討論這些情況。

  簡單地說,如果多個用戶都可以從合約中提取資金,那麼任誰都能一次性多次調用withdraw函數並多次得到報酬。所以我們需要以這樣一種方式來編寫我們的取款功能:在他繼續得到報酬之前,他應得的數額會作廢。

  它看起來像這樣:

 

  https://github.com/devzl/ethereum-walkthrough-1/blob/master/Wrestling.sol 有代碼段。

  智能合約的IDE

  在區塊鏈技術中,不僅轉賬是一筆交易,對合約中函數的調用和合約的部署都是以發送交易的方式完成。整個過程比較繁瑣,正如同其他的變成語言一樣,針對於 solidity 智能合約,我們也提供了 IDE (CITA IDE) 來編譯和部署合約。

1586252312382036.jpg

  CITA 的IDE

  CITA IDE 是基於 Ethereum 的 Solidity 編輯器進行修改並適配了 CITA ,是面向 CITA 的智能合約編輯器,能夠編寫、編譯、debug、部署智能合約。可直接運行官方 CITA IDE 1(https://cita-ide.citahub.com)進行體驗。

  使用說明

  browser 內置常用的模板合約,首先從內置合約模板中選擇合適的模板開始開發Compile 本地編譯,選擇當前 solidity 版本,與合約 pragma 一致進入右側的 Run 標籤, 在 Deploy to CITA 中填入相關信息勾選 Auto ValidUntilBlock 則發送交易前會自動更新 validUntilBlock 字段勾選 store ABI on chain 則會在合約部署成功後將合約 ABI 存儲到 CITA 上此處特別注意 Quota 的設置, 一般合約需要較多 Quota, 若 quota 不足, 在交易信息打印的時候可以查看 Error Message 獲知點擊 Load Contracts 加載當前編譯完成的合約, 並選擇要部署的合約點擊 Deploy to CITA 發起部署合約的交易觀察控制檯的輸出, 交易詳細信息會顯示在控制檯上, 當流程結束時, 會輸出交易 hash 和合約地址, 並且以鏈接形式支持到 Microscope 查看

  DApp 及智能合約開發實例

  First Forever 是一個DApp demo,展示了在 CITA 上開發一個最小可用的 DApp 的完整流程。

  FIrst Forever 地址:

  https://github.com/citahub/first-forever-demo/blob/develop/README-CN.md

1586252312441736.jpg

  以下是區塊鏈DApp的開發步驟示意圖:

1586252312496992.jpg

  在該項目中使用了一個簡單的可以存儲用戶提交內容的智能合約,源碼:SimpleStore

  地址:

  https://github.com/citahub/first-forever-demo/blob/develop/src/contracts/SimpleStore.sol

  更詳細的介紹看:如何動手做一個DApp

  地址:https://github.com/citahub/first-forever-demo/blob/develop/README-CN.md

  3

  智能合約安全性

  參考視頻

  [https://www.bilibili.com/video/av58299098]

  因爲智能合約是不可逆的,所以他的交易一旦形成,是無法回退的。在這種情形下,智能合約的安全性尤爲重要。以下先介紹幾種合約常見的合約安全性隱患,然後會給出改善他們的方法。

  智能合約溢出型漏洞

  16bit 整數:0x0000,0x0001,0x0002,…,0xfffd,0xffff

  0x8000 + 0x8000 = 0x10000 = 0x0000 = 0

  0xffff + 0x0003 = 0x10002 = 0x0002 = 2

  0x0000 - 0x0001 = 0xffff = -1 = 65535

 

  這個函數想要做到的是把 msg.sender 在合約中的 token 轉給多個人,amount += _value[j];這個操作會存在溢出的風險,如果在加的時候出現狀況 amount = 0x8000 + 0x8000 = 0,那麼在後面一步的判斷 require(balanceOf[msg.sender] >= amount);中會出現的實際判斷的是balanceOf[msg.sender] >= 0那麼可以從空的賬戶中把錢轉出。

  代碼注入漏洞

 

  可以把這個合約本身擁有的代幣偷走轉給別的用戶,因爲對於extraData 來說,自由度非常高,_spender.call(_extraData)可以是任何一個地址調用任何一個函數。

  itchyDAOin MakerDAO 投票系統

  這個主要是以一個比較複雜的例子來給學員講合約中函數調用需要知道的地方,暗示智能合約還是比較難以把控的,需要多學習

  以下是一個在 MakerDAO 中的投票系統,在這個投票系統中,一個sender 需要根據自己的權重對一個提案進行投票。

  

  以下是投票函數,在投票以後把票數進行 addWeight 和 subWeight 操作。

 

  最後一步是在 lock 一種幣,在 lock 以後可以進行投票操作,在投票完成以後,可以 free 從而退回自己的幣。

  

  4

  智能合約場景

  長遠看,遵循標準有很多不應忽視的益處。首先,如果遵照某個標準生成代幣,那麼每個人都會知道該代幣的基礎功能,並知道如何與之交互,因此就會有更多信任。去中心化程序(DApps)可以直接辨別出其代幣特徵,並通過特定的 UI 來與其打交道。另外,一種代幣智能合約的標準實現已經被社區開發出來,它採用類似 OpenZeppelin 的架構。這種實現已經被很多大神驗證過,可以用來作爲代幣開發的起點。

  本文中會從頭開始提供一個不完整的,但是遵循 ERC20 標準的,基礎版的代幣實現,然後將它轉換成遵循 ERC721 標準的實現。這樣就能讓讀者看出兩個標準之間的不同。

  出發點是希望大家瞭解代幣是如何工作的,其過程並不是一個黑箱;另外,對於 ERC20 這個標準,儘管它至少已經被廣泛接受兩年以上,如果只是從標準框架簡單地生成自己的代幣,也還會存在某些不易發現的故障點。

  ERC20 標準

  ERC20(https://theethereum.wiki/w/index.php/ERC20_Token_Standard)是爲同質(Fungible)代幣標準設立的標準,可以被其它應用(從錢包到去中心化交易所)重複使用。同質意味着可以用同類的代幣互換,換句話說,所有的代幣都是等價的(就像錢幣,某一美金和其它美金之間沒有區別)。而一個非同質代幣(Non-fungible Token)代表一種特定價值(例如房屋,財產,藝術品等)。同質代幣有其內在價值,而非同質代幣只是一種價值智能合約的代表。

  要提供符合ERC20標準的代幣,需要實現如下功能和事件:

  

  標準不提供功能的實現,這是因爲大家可以用自己喜歡的方式寫出任何代碼,如果不需要提供某些功能只需要按照標準(https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md)返回 null/false 的值就可以了。

  注意:這裏並不很強調代碼,大家只需瞭解內部機理,全部代碼將會在文末附上鍊接。

  實現

  首先,需要給代幣起一個名字,因此會採用一個公有變量(Public Variable):

  string public name = “Our Tutorial Coin”;

  然後給代幣起一個代號:

  string public symbol = “OTC”;

  當然還要有具體小數位數:

  uint8 public decimals = 2;

  因爲 Solidity 並不完全支持浮點數,因此必須把所有數表示成整數。例如,對於一個數字 “123456”,如果使用 2 位小數,則代表 “1234.56”;如果採用4位小數,則代表 “12.3456”。0 位小數代表代幣不可分。而以太坊的加密幣以太幣則使用18位小數。一般地,代幣不需要使用18位小數,因爲遵循了以太坊的慣例,也沒有什麼特別的目的。

  你需要統計一共發行了多少代幣,並跟蹤每人擁有多少:

 

  當然,你需要從0個代幣開始,除非在代幣智能合約創建時候就生成了一些,如下例:

  

  totalSupply()函數只是從totalSupply變量中獲取數值:

 

  接下來就是ERC20的神奇之處了,transfer()函數是將代幣從一個地址發送到另外一個地址的函數:

  

  以上基本就是 ERC20 代幣標準的核心內容。

  鑑於 ERC20 還存在其他一些問題,更安全容錯的transferFrom()實現和其它方案被髮布出來(如之前所說,該標準只是一些功能原型和行爲定義,具體細節則靠開發者自己實現),並正在討論中,其中就包括

  ERC223(https://github.com/ethereum/EIPs/issues/223)ERC777(https://github.com/ethereum/EIPs/issues/777)

  ERC223 方案的動機是避免將代幣發送到錯誤地址或者不支持這種代幣的合約上,成千上萬的金錢因爲上述原因丟失,這一需求作爲以太坊後續開發功能的第 223 條記錄第 223 條記錄在案。ERC777 標準在支持其它功能的同時,對接收地址進行“即將收到代幣”的提醒功能,ERC777 方案看起來很有可能替代 ERC20.

  ERC721標準

  ERC721目前看,ERC721 跟 ERC20 及其近親系列有本質上的不同。ERC721 中,代幣都是唯一的。ERC721 提出來後的衆多使用案例中,CryptoKitties,這款使用ERC721標準實現的收集虛擬貓遊戲使得它備受矚目。以太貓遊戲實際就是智能合約中的非同質代幣 (non-fungible token),並在遊戲中用貓的形象來表現出來。

  如果想將一個 ERC20 合約轉變成 ERC721 合約,我們需要知道 ERC721 是如何跟蹤代幣的。在 ERC20 中,每個地址都有一個賬目表,而在 ERC721 合約中,每個地址都有一個代幣列表:

  mapping(address => uint[]) internal listOfOwnerTokens;

  由於 Solidity 自身限制,不支持對隊列進行 indexOF()的操作,我們不得不手動進行隊列代幣跟蹤:

  mapping(uint => uint) internal tokenIndexInOwnerArray;

  當然可以用自己實現的代碼庫來發現元素的索引,考慮到索引時間有可能很長,最佳實踐還是採用映射方式。

  爲了更容易跟蹤代幣,還可以爲代幣的擁有者設置一個映射表:

  mapping(uint => address) internal tokenIdToOwner;

  以上就是兩個標準之間最大的不同,ERC721 中的 transfer()函數會爲代幣設置新的擁有者:

  

  儘管代碼比較長,但卻是轉移代幣流程中必不可少的步驟。

  還必須注意,ERC721 也支持approve()和transferFrom()函數,因此我們必須在 transfer 函數內部加上其它限制指令,這樣一來,當某個代幣有了新的擁有者,之前的被授權地址就無法其代幣進行轉移操作,代碼如下:

  

  挖礦基於以上兩種標準,可能面對同一種需求,要麼產生同質代幣,要麼產生非同質代幣,一般都會用一個叫做Mint()的函數完成。

  實現以上功能函數的代碼如下:

  

  用任意一個數字產生一個新代幣,根據不同應用場景,一般在合約內部只會授權部分地址可以對它進行鑄幣(mint)操作。

  這裏需要注意mint()函數並沒有出現在協議標準定義中,而是我們添加上去的,也就是說我們可以對標準進行擴充,添加其它對代幣的必要操作。例如,可以添加用以太幣來買賣代幣的系統,或者刪除不再需要代幣的功能。


 

來源:溪塔科技

本文來自互聯網,如有侵權請與我們聯繫刪除。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章