區塊鏈100講:Solidity語法難點解析及故障排查

image

1

編輯器說明

一、推薦編輯器編程

目前嘗試 Solidity 編程的最好的方式是使用 Remix(https://remix.ethereum.org/) (須要時間加載,請耐心等待)。Remix 是一個基於 Web 的 IDE,它可讓你編寫 Solidity 智能合約,而後部署並運行該智能合約。json

若是外網不能訪問,能夠訪問歐陽哥哥搭建的REMIX編輯器(http://remix2.ju3ban.net/#optimize=false&version=soljson-v0.4.22+commit.4cb486ee.js)ubuntu

二、Visual Studio Extensionvim

Microsoft Visual Studio 的 Solidity 插件(https://marketplace.visualstudio.com/items?itemName=ConsenSys.Solidity),包含 Solidity 編譯器。visual-studio-code

三、Visual Studio Code extension數組

Microsoft Visual Studio Code 插件(http://juan.blanco.ws/solidity-contracts-in-visual-studio-code/),包含語法高亮和 Solidity 編譯器。安全

2

REMIN的函數引用

function mint(address receiver, uint amount)微信

一、在REMIX輸入時,地址必定要有""表示,不然amount就取不到值。網絡

例如是mint("0xca35b7d915458ef540ade6068dfe2f44e8fa733c",101)編輯器

二、程序中,若是註釋包含中文,單步調試的位置不許確。

3

address相關全局函數

<address>.balance (uint256): 該地址有多少以太坊餘額(wei爲單位)

<address>.transfer(uint256 amount): 發送特定數量(wei爲單位)的以太坊到對應地址,當出現錯誤時會扔出異常,但不會因異常而中止。固定須要耗費2300個gas。

<address>.send(uint256 amount) returns (bool): 發送特定數量(wei爲單位)的以太坊到對應地址,當出現錯誤時會返回flase。固定須要耗費2300個gas。

【警告】send() 執行有一些風險:若是調用棧的深度超過1024或gas耗光,交易都會失敗。所以,爲了保證安全,必須檢查send的返回值,若是交易失敗,會回退以太幣。若是用transfer會更好。

<address>.call(...) returns (bool): CALL的低級調用函數,當失敗時返回false。執行須要消耗不固定的gas。

【說明】不鼓勵使用call函數,後期將會被移除。調用該函數可能形成安全攻擊,詳見後期安全相關文章。

<address>.callcode(...) returns (bool): CALLCODE的低級調用函數,當失敗時返回false。執行須要消耗不固定的gas。

不建議使用,後續版本會刪除。

<address>.delegatecall(...) returns (bool): DELEGATECALL的低級調用函數,當失敗時返回false。執行須要消耗不固定的gas。

【說明】爲了和非ABI協議的合約進行交互,可使用call() 函數, 它用來向另外一個合約發送原始數據,支持任何類型任意數量的參數,每一個參數會按規則(ABI協議)打包成32字節並一一拼接到一塊兒。一個例外是:若是第一個參數剛好4個字節,在這種狀況下,會被認爲根據ABI協議定義的函數器指定的函數簽名而直接使用。若是僅想發送消息體,須要避免第一個參數是4個字節。以下面的例子:

function callfunc(address addr) returns (bool){ bytes4 methodId = bytes4(keccak256("setScore(uint256)")); return addr.call(methodId, 100); }

測試地址和地址調用代碼舉例

pragma solidity ^0.4.18;

contract AddrTest{

    /*event函數知識把參數信息打印在REMIX等編譯器的LOG位置區,不須要函數定義。*/

    event logdata(bytes data);

    event LogContractAddress(address exAccount, address contractAddress);

        uint score = 0;

        /*回調函數,沒有函數名。任何調用不存在的函數,這時被調用的合約的fallback函數會執行。

     payable:若是一個函數須要進行貨幣操做,必需要帶上payable關鍵字*/ 

   function() payable {

        logdata(msg.data);

    }

        /*智能合約構建函數*/

    function AddrTest(){

    LogContractAddress(msg.sender,this);

    }

    function getBalance() returns (uint) {

        return this.balance;

    }

    function setScore(uint s) public {

        score = s;

    }

    function getScore() returns ( uint){ 

       return score;

    }

}

contract CallTest{ 

   /*該函數有函數申明沒有實際函數內容,在remix的value區域設置以太坊的個數,調用該函數會把外部帳戶(ACCOUNT)中的      以太坊轉移到智能合約帳戶中*/

    function deposit() payable {

    }

    event logSendEvent(address to, uint value);

    event LogContractAddress(address exAccount, address contractAddress); 

           /*轉以太坊給目標地址*/ 

   function transferEther(address towho) payable { 

       towho.transfer(10);/*單位爲wei*/   

            logSendEvent(towho, 10); 

   }

       /*不指定調用函數,則調用無函數名的回調函數*/ 

   function callNoFunc(address addr) returns (bool){ 

       return addr.call("tinyxiong", 1234); 

   }

    /*制定調用函數的方法*/ 

   function callfunc(address addr) returns (bool){ 

       bytes4 methodId = bytes4(keccak256("setScore(uint256)"));  

      return addr.call(methodId, 100); 

   } 

     /*返回當前合約的以太坊餘額*/

    function getBalance() returns (uint) {

        return this.balance;//0

    } 

         /*銷燬智能合約,把以太坊餘額返回給當前外部帳戶*/

    function ContractSuide() {  

      LogContractAddress(this,msg.sender);  

      suicide(msg.sender); 

   }

}

4

Contract Related

this (current contract’s type): 表示當前合約,能夠顯式的轉換爲Address

selfdestruct(address recipient): destroy the current contract, sending its funds to the given Address

銷燬當前合約,發送當前以太坊餘額到給定的地址 suicide(address recipient):

selfdestruct的別名函數

5

區塊和交易屬性

**block.blockhash(uint blockNumber) returns (bytes32): **

給定區塊的哈希—僅對最近的 256 個區塊有效而不包括當前區塊

**block.coinbase (address): **

挖出當前區塊的礦工地址

**block.difficulty (uint): **

當前區塊難度

block.gaslimit (uint):

 當前區塊 gas 限額

**block.number (uint): **

當前區塊號

block.timestamp (uint):

自 unix epoch 起始當前區塊以秒計的時間戳

**msg.data (bytes): **

完整的 calldata

**msg.gas (uint): **

剩餘 gas

msg.sender (address):

消息發送者(當前調用)

**msg.sig (bytes4): **

calldata 的前 4 字節(也就是函數標識符)

msg.value (uint):

隨消息發送的 wei 的數量

**now (uint): **

目前區塊時間戳(block.timestamp)

**tx.gasprice (uint): **

交易的 gas 價格

**tx.origin (address): **

交易發起者(徹底的調用鏈)

註解 對於每個外部函數調用,包括 msg.sender 和 msg.value 在內全部 msg 成員的值都會變化。這裏包括對庫函數的調用。

註解 不要依賴 block.timestamp、 now 和 block.blockhash 產生隨機數,除非你知道本身在作什麼。

時間戳和區塊哈希在必定程度上均可能受到挖礦礦工影響。例如,挖礦社區中的惡意礦工能夠用某個給定的哈希來運行賭場合約的 payout 函數,而若是他們沒收到錢,還能夠用一個不一樣的哈希從新嘗試。 當前區塊的時間戳必須嚴格大於最後一個區塊的時間戳,但這裏惟一能確保的只是它會是在權威鏈上的兩個連續區塊的時間戳之間的數值。

註解 基於可擴展因素,區塊哈希不是對全部區塊都有效。你僅僅能夠訪問最近 256 個區塊的哈希,其他的哈希均爲零。

6

錯誤處理

assert(bool condition): 若是條件不知足就拋出—用於內部錯誤。

require(bool condition): 若是條件不知足就拋掉—用於輸入或者外部組件引發的錯誤。

revert(): 終止運行並恢復狀態變更。

7

數學和密碼學函數

addmod(uint x, uint y, uint k) returns (uint): 計算 (x + y) % k,加法會在任意精度下執行,而且加法的結果即便超過 2**256 也不會被截取。從 0.5.0 版本的編譯器開始會加入對 k != 0 的校驗(assert)。

mulmod(uint x, uint y, uint k) returns (uint): 計算 (x * y) % k,乘法會在任意精度下執行,而且乘法的結果即便超過 2**256 也不會被截取。從 0.5.0 版本的編譯器開始會加入對 k != 0 的校驗(assert)。

keccak256(...) returns (bytes32): 計算 (tightly packed) arguments 的 Ethereum-SHA-3 (Keccak-256)哈希。

sha256(...) returns (bytes32): 計算 (tightly packed) arguments 的 SHA-256 哈希。

sha3(...) returns (bytes32): 等價於 keccak256。

ripemd160(...) returns (bytes20): 計算 (tightly packed) arguments 的 RIPEMD-160 哈希。

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address) : 利用橢圓曲線簽名恢復與公鑰相關的地址,錯誤返回零值。(example usage:https://ethereum.stackexchange.com/q/1777/222)

上文中的「tightly packed」是指不會對參數值進行 padding 處理(就是說全部參數值的字節碼是連續存放的,譯者注),這意味着下邊這些調用都是等價的:

keccak256("ab", "c") keccak256("abc") keccak256(0x616263) keccak256(6382179) keccak256(97, 98, 99)

若是須要 padding,可使用顯式類型轉換:keccak256("\x00\x12") 和 keccak256(uint16(0x12)) 是同樣的。

請注意,常量值會使用存儲它們所須要的最少字節數進行打包。例如:keccak256(0) == keccak256(uint8(0)),keccak256(0x12345678) == keccak256(uint32(0x12345678))。

在一個私鏈上,你頗有可能碰到因爲 sha25六、ripemd160 或者 ecrecover 引發的 Out-of-Gas。這個緣由就是他們被當作所謂的預編譯合約而執行,而且在第一次收到消息後這些合約才真正存在(儘管合約代碼是硬代碼)。發送到不存在的合約的消息很是昂貴,因此實際的執行會致使 Out-of-Gas 錯誤。在你的合約中實際使用它們以前,給每一個合約發送一點兒以太幣,好比 1 Wei。這在官方網絡或測試網絡上不是問題。

8

solidity常見錯誤提示及緣由分析

一、智能合約執行失敗

告警描述: " Warning! Error encountered during contract execution [Out of gas] "

發生場景: 執行官網衆籌智能合約代碼,在給智能合約打代幣前,往智能合約地址帳戶打ETH,交易失敗,提示以下:

點擊查看信息連接:https://ropsten.etherscan.io/tx/0x8b4da573b36dbf7361c95a0156dfe060553874fbb4d401f989c7a4a6d539ebfa

代碼及緣由分析: 下面是執行的回調函數,其中「tokenReward.transfer(msg.sender, amount / price);」的意思就是把智能合約的代幣打給發送者帳號。這個帳號尚未代幣,因此確定會執行失敗。

function () payable {

        require(!crowdsaleClosed);

        uint amount = msg.value;

       balanceOf[msg.sender] += amount; 

       amountRaised += amount; 

       tokenReward.transfer(msg.sender, amount / price); 

       FundTransfer(msg.sender, amount, true);

    }

解決方法: 先往智能合約帳號打代幣,而後打ETH,就不會執行失敗了。

二、 REMIX+MetaMASK的場景下,調用智能合約函數,提示不可知地址

告警描述:

REMIX輸出框輸出如下告警內容: transact to Crowdsale.checkGoalReached errored: Unknown address - unable to sign transaction for this address: "0x3d7dfb80e71096f2c4ee63c42c4d849f2cbbe363"

發生場景: 更換了Meta帳號後,調用以前帳號建立的智能合約的函數

緣由分析: Remix輸出框提示未知地址:

image

告警描述

查看MetaMask的當前帳號,發現是Account 1的帳號,因此沒法執行合約。

image

MetaMask的當前帳號

解決方法:

更換MetaMask爲Account8(地址爲0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363)的帳號便可正常運行。

三、GETH安裝時出現異常

告警描述: GETH安裝時出現如下告警:

E: Failed to fetch http://101.110.118.22/ppa.launchpad.net/ethereum/ethereum/ubuntu/pool/main/e/ethereum/ethereum_1.8.10+build13740+artful_i386.deb  Could not connect to 101.110.118.22:80 (101.110.118.22), connection timed out

E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?

發生場景:

GETH安裝,執行如下命令出現。

sudo apt-get install ethereum

緣由分析:

解決方法:

<1> 參考網上解決方案 sudo vim /etc/resolv.conf ,添加:

nameserver 8.8.8.8

而後執行:

sudo /etc/init.d/networking restart

仍是不行。

<2> 從綠地金融中心搬到家裏,作相同的操做,就能夠安裝成功了。 應該是綠地金融中心的路由器作了某些配置,致使下載不成功。

四、問題描述模板

告警描述: 發生場景: 緣由分析: 解決方法:

9

常見問題及解答

1).modifer函數是幹什麼的?

2).如何打幣回支付帳號?

3).智能合約的定時器和系統函數是什麼?

4).當建立一個智能合約時,msg.sender和this的區別?

答覆:msg.sender是指外部帳戶的地址,this是指當前建立的智能合約的地址。

contract AddrTest{ 

   event LogContractAddress(address exAccount, address contractAddress); 

       function AddrTest(){ 

   LogContractAddress(msg.sender,this);

    }    

}

在remix運行,能夠證實這個推測

image

LOG說明

10

參考資料

1,官方中文網站 http://solidity-cn.readthedocs.io/zh/develop/

2, tiny熊翻譯系列(https://blog.csdn.net/xilibi2003)

1] Solidity教程序列1 - 類型介紹 2] 智能合約語言Solidity教程系列2 - 地址類型介紹 3] 智能合約語言 Solidity 教程系列3 - 函數類型 4] 智能合約語言 Solidity 教程系列4 - 數據存儲位置分析 5] 智能合約語言 Solidity 教程系列5 - 數組介紹 6] 智能合約語言 Solidity 教程系列6 - 結構體與映射 7] 智能合約語言 Solidity 教程系列7 - 以太單位及時間單位 8] 智能合約語言 Solidity 教程系列8 - Solidity API 9] 智能合約語言 Solidity 教程系列9 - 錯誤處理 10] 智能合約語言 Solidity 教程系列10 - 徹底理解函數修改器

本文做者:HiBlock區塊鏈技術佈道羣-輝哥

原文發佈於簡書

原文連接:https://www.jianshu.com/p/275ed3d7aeb7

加微信baobaotalk_com,加入技術佈道羣

線下活動推薦

技術工坊|如何利用ERC875協議開發世界盃區塊鏈門票?(北京)

相關文章
相關標籤/搜索