以太坊Solidity程式語言開發框架————7、合約互動

FLy_鵬程萬里發表於2018-07-13

背景

標準的與以太坊網路互動的方法是通過以太坊官方構建的Web3庫。儘管這個庫非常有用,但使用其提供介面與合約互動有些困難,特別是以太坊的新手。為降低學習曲線,Truffle使用Ether Pudding庫,它也是基於Web3的基礎之上,目的是為了讓互動更簡單。

讀寫資料

以太坊網路把在網路上讀與寫資料進行了區分,這個區分對於如何寫程式影響很大。通常來說,寫資料被稱作交易(transaction),讀資料被稱作呼叫(call)。對於交易與呼叫,他們分別有如下特性:

交易(Transaction)

交易本質上改變了整個以太坊網路的資料狀態。一個交易可以是向另一個帳戶傳送ether(以太坊網路代幣)這樣的簡單行為,也可以是執行合約函式,新增一個新合約到以太坊網路這樣的複雜行為。交易的典型特徵是寫入(或修改)資料。交易需要花費ether,也被稱作gas,交易的執行需要時間。當你通過交易執行一個合約的函式時,你並不能立即得到執行結果,因為交易並不是立即執行的。大多婁情況下,通過執行交易不會返回值;它會返回一個交易的ID.總的來說,交易具有如下特徵:

  • 需要gas(Ether)
  • 改變網路的狀態
  • 不會立即執行
  • 不會暴露返回結果(僅有交易ID)

呼叫

呼叫,則與上述的交易非常不同。呼叫可以在網路上執行程式碼,但沒有資料會被改變(也許僅僅是些臨時變數被改變)。呼叫的執行是免費的,典型的行為就是讀取資料。通過呼叫執行一個合約函式,你會立即得到結果。總的來說,呼叫具有如下特徵:

  • 免費(不花費gas)
  • 不改變網路狀態
  • 立即執行
  • 有返回結果。

如果選擇,取決於你想幹什麼,或者說想寫資料,還是讀資料。

介面(abstract)

為了來體驗一下合約介面的作用,我們使用框架自帶的預設metacoin的合約例子。

import "ConvertLib.sol";

contract MetaCoin {
  mapping (address => uint) balances;

    event Transfer(address indexed _from, address indexed _to, uint256 _value);

    function MetaCoin() {
        balances[tx.origin] = 10000;
    }

    function sendCoin(address receiver, uint amount) returns(bool sufficient) {
        if (balances[msg.sender] < amount) return false;
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        Transfer(msg.sender, receiver, amount);
        return true;
    }
    function getBalanceInEth(address addr) returns(uint){
        return ConvertLib.convert(getBalance(addr),2);
    }
    function getBalance(address addr) returns(uint) {
        return balances[addr];
    }
}

合約有三個方法和一個構造方法。所有三個方法可以被執行為交易或呼叫。

現在我們來看看Truffle和Ether Pudding為我們提供的叫MetaCoin的Javascript物件,可以在前端中使用:

// Print the deployed version of MetaCoin
console.log(MetaCoin.deployed());

// outputs:
//
// Contract
// - address: "0xa9f441a487754e6b27ba044a5a8eb2eec77f6b92"
// - allEvents: ()
// - getBalance: ()
// - getBalanceInEth: ()
// - sendCoin: ()

介面層提供了合約中以應的函式名。它還包含一個地址,指向到MetaCoin合約的部署版本。

執行合約函式

通過這套框架為我們提供的介面,我們可以簡單的在以太坊網路上執行合約函式。

執行交易

在上述例子MetaCoin合約中,我們有三個可以執行的函式。如果你對這三個函式稍加分析就會發現,只有sendCoin會對網路造成更改。sendCoin函式的目標將Meta Coin從一個帳戶傳送到另一些帳戶,這些更改需要被永久存下來。

當呼叫sendCoin,我們將把他們作為一個交易來執行。下面的例子我們來演示下把10個幣,從一個帳戶發到另一個帳戶,改變要永久的儲存在網路上:

var account_one = "0x1234..."; // an address
var account_two = "0xabcd..."; // another address

var meta = MetaCoin.deployed();
meta.sendCoin(account_two, 10, {from: account_one}).then(function(tx_id) {
  // If this callback is called, the transaction was successfully processed.
  // Note that Ether Pudding takes care of watching the network and triggering
  // this callback.
  alert("Transaction successful!")
}).catch(function(e) {
  // There was an error! Handle it.
})

上述程式碼有一些有趣點,我們來了解一下:

  • 我們直接呼叫介面的sendCoin函式。最終是預設以交易的方式來執行的。
  • 交易被成功執行時,回撥函式會直到交易被執行時才真正被觸發。這樣帶來的一個好處是你不用一直去檢查交易的狀態。
  • 我們對sendCoin函式傳遞了第三個引數,需要注意的是原始合約函式的定義中並沒有第三個引數。這裡你看到的是一個特殊的物件,用於編輯一些交易中的指定細節,它可以總是做為第三個引數傳進。這裡,我們設定from的地址為account_one.

執行呼叫

繼續用MetaCoin的例子。其中的getBalance函式就是一個很好的從網路中讀取資料的例子。它壓根不需要進行任何資料上的變更,它只是返回傳入的地址的帳戶餘額,我們來簡單看一下:

var account_one = "0x1234..."; // an address

var meta = MetaCoin.deployed();
meta.getBalance.call(account_one, {from: account_one}).then(function(balance) {
  // If this callback is called, the call was successfully executed.
  // Note that this returns immediately without any waiting.
  // Let's print the return value.
  console.log(balance.toNumber());
}).catch(function(e) {
  // There was an error! Handle it.
})

一些有意思的地方如下:

  • 我們必須通過.call()來顯示的向以太坊網路表明,我們並不會持久化一些資料變化。
  • 我們得到了返回結果,而不是一個交易ID。這裡有個需要注意的是,以太坊網網路可以處理非常大的數字,我們被返回了一個BigNumber物件,框架再將這個物件轉化了一個number型別。

警告:我們在上述的例子中將返回值轉成了一個number型別,是因為例子中的返回值比較小,如果將一個BigNumber轉換為比javascript支援的number最大整數都大,你將會出現錯誤或不可預期的行為。

捕捉事件(Catching Events)

你的合約可以觸發事件,你可以進行捕捉以進行更多的控制。事件API與Web3一樣。可以參考Web3 documentation來了解更多。

var meta = MetaCoin.deployed();
var transfers = meta.Transfer({fromBlock: "latest"});
transfers.watch(function(error, result) {
  // This will catch all Transfer events, regardless of how they originated.
  if (error == null) {
    console.log(result.args);
  }
}

METHOD:DEPLOYED()

每一個抽象出來的合約介面都有一個deployed()方法,上述例子中,你已經見到過。呼叫這個函式返回一個例項,這個例項代表的是之前部署到網路的合約所對應的抽象介面的例項。

var meta = MetaCoin.deployed();

警告:這僅對使用truffle deploy部署的合約,且一定是在project configuration中配置釋出的才有效。如果不是這樣,這個函式執行時會丟擲異常。

METHOD:AT()

類似於deployed(),你可以通過一個地址來得到一個代表合約的抽象介面例項。當然這個地址一定是這個合約的部署地址。

var meta = MetaCoin.at("0x1234...")

警告:當你的地址不正確,或地址對應的合約不正確時,這個函式並不會丟擲異常。但呼叫介面時會報錯。請保證在使用at()時輸入正確的地址。

METHOD:NEW()

你可以通過這個方法來部署一個完全全新的合約到網路中。

MetaCoin.new().then(function(instance) {
  // `instance` is a new instance of the abstraction.
  // If this callback is called, the deployment was successful.
  console.log(instance.address);
}).catch(function(e) {
  // There was an error! Handle it.
});

需要注意的是這是一個交易,會改變網路的狀態。

如果任何問題,歡迎留言批評指正。

相關文章