Qtum智慧合約使用方法及說明

QTUM量子鏈開發團隊發表於2018-07-16

Qtum智慧合約使用方法及說明

Qtum是一個基於比特幣UTXO模型,權益證明機制(pos)和支援EVM智慧合約的區塊鏈專案。它通過創新的賬戶抽象層(Account Abstraction Layer)實現了比特幣與以太坊兩大生態的融合。如果想了解更多關於Qtum的資訊,可以訪問我們的官網www.qtum.org,歡迎加入我們的社群。

基於Qtum的智慧合約

首先需要說明的是,使用Qtum的智慧合約介面仍然需要一定的技術儲備。本文對智慧合約的操作在命令列中使用qtum-cli,或者在圖形錢包qtum-qt的debug視窗中使用。 為了演示如何簡單建立和操作一個智慧合約,我們將會使用如下的合約程式碼:

pragma solidity ^0.4.0;
contract QtumTest {
   uint storedNumber;
   function QtumTest() {
       storedNumber=1;
   }
   function setNumber(uint number) public{
       storedNumber = number;
   }
   function logNumber() constant public{
        log1("storedNumber", uintToBytes(storedNumber));
   }
   function returnNumber() constant public returns (uint){
       return storedNumber;
   }
   function deposit() public payable{
   }
   function withdraw() public{
       if(!msg.sender.send(this.balance)){
           throw;
       }
   }
   //utility function
   function uintToBytes(uint v) constant returns (bytes32 ret) {
       if (v == 0) {
           ret = '0';
       }
       else {
           while (v > 0) {
               ret = bytes32(uint(ret) / (2 ** 8));
               ret |= bytes32(((v % 10) + 48) * 2 ** (8 * 31));
               v /= 10;
           }
       }
       return ret;
   }
}
複製程式碼

編譯以上的合約程式碼,生成EVM的bytecode如下:

6060604052341561000c57fe5b5b60016000819055505b5b6102bd806100266000396000f30060606040523615610076576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633450bd6a146100785780633ccfd60b1461009e5780633fb5c1cb146100b057806394e8767d146100d05780639f2c436f1461010c578063d0e30db01461011e575bfe5b341561008057fe5b610088610128565b6040518082815260200191505060405180910390f35b34156100a657fe5b6100ae610133565b005b34156100b857fe5b6100ce6004808035906020019091905050610190565b005b34156100d857fe5b6100ee600480803590602001909190505061019b565b60405180826000191660001916815260200191505060405180910390f35b341561011457fe5b61011c610246565b005b61012661028e565b005b600060005490505b90565b3373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051809050600060405180830381858888f19350505050151561018d57610000565b5b565b806000819055505b50565b600060008214156101ce577f3000000000000000000000000000000000000000000000000000000000000000905061023d565b5b600082111561023c5761010081600190048115156101e957fe5b0460010290507f01000000000000000000000000000000000000000000000000000000000000006030600a8481151561021e57fe5b06010260010281179050600a8281151561023457fe5b0491506101cf565b5b8090505b919050565b61025160005461019b565b6000191660405180807f73746f7265644e756d6265720000000000000000000000000000000000000000815250600c01905060405180910390a15b565b5b5600a165627a7a72305820326efcd34df5fdba07e7a1afe7ffd4b42873ef749ae9a5915db46fd20b9c251c0029
複製程式碼

同時還會得到如下的JSON介面檔案:

[{"constant":true,"inputs":[],"name":"returnNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"number","type":"uint256"}],"name":"setNumber","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"v","type":"uint256"}],"name":"uintToBytes","outputs":[{"name":"ret","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"logNumber","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"}]
複製程式碼

任何一個合約的這些資訊都可以通過Browser Solidity這個網站獲得,輸入你的合約程式碼後,在右方點選"contract details"即可查詢。

(提示:如果使用QT錢包中的debug視窗,在以下的命令中都不需要再包含./qtum-cli

首先我們建立合約:

./qtum-cli createcontract 6060604052341561000c57fe5b5b60016000819055505b5b6102bd806100266000396000f30060606040523615610076576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633450bd6a146100785780633ccfd60b1461009e5780633fb5c1cb146100b057806394e8767d146100d05780639f2c436f1461010c578063d0e30db01461011e575bfe5b341561008057fe5b610088610128565b6040518082815260200191505060405180910390f35b34156100a657fe5b6100ae610133565b005b34156100b857fe5b6100ce6004808035906020019091905050610190565b005b34156100d857fe5b6100ee600480803590602001909190505061019b565b60405180826000191660001916815260200191505060405180910390f35b341561011457fe5b61011c610246565b005b61012661028e565b005b600060005490505b90565b3373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051809050600060405180830381858888f19350505050151561018d57610000565b5b565b806000819055505b50565b600060008214156101ce577f3000000000000000000000000000000000000000000000000000000000000000905061023d565b5b600082111561023c5761010081600190048115156101e957fe5b0460010290507f01000000000000000000000000000000000000000000000000000000000000006030600a8481151561021e57fe5b06010260010281179050600a8281151561023457fe5b0491506101cf565b5b8090505b919050565b61025160005461019b565b6000191660405180807f73746f7265644e756d6265720000000000000000000000000000000000000000815250600c01905060405180910390a15b565b5b5600a165627a7a72305820326efcd34df5fdba07e7a1afe7ffd4b42873ef749ae9a5915db46fd20b9c251c0029 300000
複製程式碼

注意,最後的引數300000是表示這筆交易的gas limit,對於這個合約,預設的數值太小,所以我們將其提升到300000。

跑完以上的程式碼之後,我們將會得到一個如下所示的結果:

{
  "txid": "72b0e0576d289c1e4e6c777431e4845f77d0884d3b3cff0387a5f4a1a3a874ea",
  "sender": "qZbjaE8N18ZU1m7851G7QGhvxKL74SRBTt",
  "hash160": "aff3e34ab836edb8d214a993d9da105915e4a6e9",
  "address": "5bde092dbecb84ea1a229b4c5b25dfc9cdc674d9"
}
複製程式碼

現在可以將上面結果中的address儲存起來,方便後面使用。

export CONTRACT=5bde092dbecb84ea1a229b4c5b25dfc9cdc674d9
複製程式碼

現在需要等待你建立的合約被打包進一個區塊,你可以通過如下的方法去確認是否被打包:

./qtum-cli getaccountinfo $CONTRACT
複製程式碼

如果提示說Address does not exist,那麼不是你的交易還未被打包進新的區塊(可以通過getrawtransaction加上你的交易id來確認是否打包),就是你未提供足夠多的gas。如果合約被成功的建立並且被打包到區塊鏈上,你將會看到類似如下的結果:

{
  "address": "5bde092dbecb84ea1a229b4c5b25dfc9cdc674d9",
  "balance": 0,
  "storage": {
    "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": {
      "0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000001"
    }
  },
  "code": "..."
}
複製程式碼

為了執行合約中的函式,你必須要使用JSON的介面檔案來建立ABI資料。有一個簡單的工具叫做ethabi可以幫助你實現這個目的。確保JSON檔案被儲存下來了,假設名字是interface.json

為了得到合約中的變數storedNumber,我們需要呼叫函式returnNumber(),通過ethabi可以構建出ABI的數值:

ethabi encode function ~/interface.json returnNumber
複製程式碼

返回的結果是

3450bd6a
複製程式碼

因為我們的state還沒有發生改變,所以呼叫命令callcontract:

./qtum-cli callcontract $CONTRACT 3450bd6a
複製程式碼

這個命令跑完之後的結果包含了很多有用的欄位,但是當前我們只關於欄位output,它的數值代表了storedNumber

{
  "address": "5bde092dbecb84ea1a229b4c5b25dfc9cdc674d9",
  "executionResult": {
    "gasUsed": 21664,
    "excepted": "None",
    "newAddress": "5bde092dbecb84ea1a229b4c5b25dfc9cdc674d9",
    "output": "0000000000000000000000000000000000000000000000000000000000000001",
    "codeDeposit": 0,
    "gasRefunded": 0,
    "depositSize": 0,
    "gasForDeposit": 0
  },
  "transactionReceipt": {
    "stateRoot": "ffbeb0377d43c6ed443a2840259ff5ead5158016ab54d55ef21b7b11aa71947f",
    "gasUsed": 21664,
    "bloom": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "log": [
    ]
  }
}
複製程式碼

為了改變storeNumber的數值,我們可以使用命令sendtocontract來在鏈上執行合約函式。首先我們還是需要ABI的資料:

ethabi encode function ~/interface.json setNumber -p 123456 --lenient
3fb5c1cb000000000000000000000000000000000000000000000000000000000001e240
複製程式碼

提示,我們加上了--lenient這個選項後,可以不用將引數補齊至256bit。現在我們來呼叫合約:

./qtum-cli sendtocontract $CONTRACT 3fb5c1cb000000000000000000000000000000000000000000000000000000000001e240
複製程式碼

同樣,做完之後我們可以再次呼叫 returnNumber()來檢查output欄位:

"output": "000000000000000000000000000000000000000000000000000000000001e240",
複製程式碼

上面的結果是我們設定的數值123456的16進位制形式。

此外,你還可以使用logNumber()函式來列印日誌。如果你的節點啟動的時候有帶上引數-record-log-opcodes,那麼檔案 vmExecLogs.json 中會包含所有的操作日誌。

你還可以通過deposit()withdraw() 這兩個函式來從測試合約中存入和提取代幣。depositwithdraw的ABI數值分別是d0e30db0和3ccfd60b。 如下的命令將會向合約中傳送10個token:

./qtum-cli sendtocontract $CONTRACT d0e30db0 10
複製程式碼

同樣,從合約中提取代幣也非常的簡單,呼叫如下的命令:

./qtum-cli sendtocontract $CONTRACT 3ccfd60b
複製程式碼

你也可以手動指定以什麼地址為sender呼叫合約,假設我的一個錢包地址是qZbjaE8N18ZU1m7851G7QGhvxKL74SRBTt,我可以使用如下的命令:

./qtum-cli sendtocontract $CONTRACT 3ccfd60b 0 190000 0.0000001 qZbjaE8N18ZU1m7851G7QGhvxKL74SRBTt
複製程式碼

如果你的地址裡沒有足夠的代幣,會提示Sender address does not have any unspent outputs,這個時候你需要先向地址內轉入一定的代幣:

./qtum-cli sendtoaddress qZbjaE8N18ZU1m7851G7QGhvxKL74SRBTt 0.001
複製程式碼

下面就可以呼叫sendtocontract了:

./qtum-cli sendtocontract $CONTRACT 3ccfd60b 0 190000 0.0000001 qZbjaE8N18ZU1m7851G7QGhvxKL74SRBTt
複製程式碼

等以上的交易被打包進一個區塊之後,合約中所有的代幣就會被提取到指定的地址qZbjaE8N18ZU1m7851G7QGhvxKL74SRBTt

FAQ

  • Q: 使用 createcontract建立了合約,但是無法呼叫合約或者合約不在合約列表中

    A:很有可能是你的gas不夠,檢視vm.log日誌可以看到實際需要的gas是多少,然後再手動設定gas limit

  • Q: 我的gas設定的很大,可是多餘的卻沒有退還

    A: 退款將會在coinstake中生成,需要等待500個區塊時間才能被使用

  • Q: 我開啟程式時使用了選項-reindex,然後節點就一直處於同步的狀態

    A: 現在reindex的話,所有的合約都會被重新執行,所以鏈上的合約越多,需要等待的時間越長。未來我們會加速這個過程,同時也會縮短初始的同步時間。

  • Q: "我覺得我發現了一個Qtum的bug"

    A: 歡迎報送到這裡 github.com/qtumproject…

Qtum新增的RPC命令

Qtum支援所有比特幣的RPC命令,並且還新加了如下的命令:

  • createcontract - 在Qtum上建立和釋出智慧合約,會消耗gas
  • callcontract - 這是一個與Qtum上已經部署好的合約進行互動的介面,所有的計算都是在鏈下進行,不需要消耗gas
  • sendtocontract - 這也是一個與Qtum上已經部署好的合約進行互動的介面,但是所有的計算都是在鏈上進行的,並且所有的狀態改變都會同步到鏈上。這個命令可以向合約傳送代幣,會消耗gas
  • getaccountinfo - 這個命令可以返回一個合約的基本資訊,包括bytecode,儲存的資料,合約的餘額等
  • listcontracts - 這個命令會列出當前所有已部署的合約地址和餘額,未來這個命令有可能被變更或者刪除
  • reservebalance - 預留一定數額的代幣,不用參與staking。如果你的這個數值設定的足夠大,超過你的擁有的代幣數量,那麼你就不會參與到staking和創造新區塊的過程中。
  • getstakinginfo - 顯示當前節點的staking狀態,包括當前的難度,生成下個區塊的期望時間等

Qtum新增命令列引數

Qtum支援所有的比特幣命令列引數,另外Qtum新增如下的引數:

  • -record-log-opcodes - 在Qtum的資料目錄(一般是在~/.qtum)下新建一個log檔案,名字是vmExecLogs.json,所有的EVM LOG操作都會被記錄。


相關文章