區塊鏈-智能合約簡單腳本編寫方法(solidity)

        “一個智能合約是一套以數字形式定義的承諾(promises) ,包括合約參與方可以在上面執行這些承諾的協議。”協議是技術實現(technical implementation),在這個基礎上,合約承諾被實現,或者合約承諾實現被記錄下來。選擇哪個協議取決於許多因素,最重要的因素是在合約履行期間,被交易資產的本質。 再次以銷售合約爲例。假設,參與方同意貨款以比特幣支付。選擇的協議很明顯將會是比特幣協議,在此協議上,智能合約被實施。因此,合約必須要用到的“數字形式”就是比特幣腳本語言。比特幣腳本語言是一種非圖靈完備的、命令式的、基於棧的編程語言,類似於Forth。下面將以一個簡單的合約腳本以及與之對應的合約功能測試腳本來進行相應的解釋。

     下面是一個簡單的合約腳本:

import "ConvertLib.sol";

// This is just a simple example of a coin-like contract.
// It is not standards compatible and cannot be expected to talk to other
// coin/token contracts. If you want to create a standards-compliant
// token, see: https://github.com/ConsenSys/Tokens. Cheers!

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

	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;
		return true;
	}
	function getBalanceInEth(address addr) returns(uint){
		return ConvertLib.convert(getBalance(addr),2);
	}
  	function getBalance(address addr) returns(uint) {
    	return balances[addr];
  	}
}
       腳本合約規定了賬戶初始值的設置以及進行轉賬時的一些規定,基本上所有的合約腳本都遵循這樣的一種代碼組織形式,下面進行一些關鍵字的簡單說明:  

      Address:地址類型,這個地址會在合約的構造函數functionConference()中被賦值。很多時候也稱呼這種地址爲'owner'(所有人)。

      uint.:無符號整型,區塊鏈上的存儲空間很緊張,保持數據儘可能的小。

      Public:這個關鍵字表明變量可以被合約之外的對象使用。private修飾符則表示變量只能被本合約(或者衍生合約)內的對象使用。

      Mapping或數組:在Solidity加入數組類型之前,大家都使用類似mapping (address => uint)的Mapping類型。這個聲明也可以寫作address registrantsPaid[],不過Mapping的存儲佔用更小(smaller footprint)。這個Mapping變量會用來保存參加者(用他們的錢包地址表示)的付款數量以便在退款時使用。

      在寫完合約腳本後,我們需要將其部署在我們的區塊鏈網絡上面去(truffle開源框架已經做得比較好了,感興趣的可以看看相關資料),部署完成後,我們可以寫一些簡單的test case來驗證我們的合約腳本是否可以被區塊鏈中的對等節點正確的調用,比如:

      

contract('MetaCoin', function(accounts) {
  it("should put 10000 MetaCoin in the first account", function(done) {
	 
	//it should be executed every time to any testcase
	
    var meta = MetaCoin.deployed();
    /*代碼中的那些then和return就是Promise。它們的作用寫成一個深深的嵌套調用鏈的話會是這樣
	conference.numRegistrants.call().then(
    function(num) {
    assert.equal(num, 0, "Registrants should be zero!");
    conference.organizer.call().then(
     function(organizer) {
        assert.equal(organizer, accounts[0], "Owner doesn't match!");
        }).then(
          function(...))
            }).then(
              function(...))
            // Because this would get hairy...
	*/
    meta.getBalance.call(accounts[0]).then(function(balance) {
      assert.equal(balance.valueOf(), 10000, "10000 wasn't in the first account");
	  
	  //stops tests at this point
    }).then(done).catch(done);
  });
  
  it("should call a function that depends on a linked library  ", function(done){
	  
    var meta = MetaCoin.deployed();
    var metaCoinBalance;
    var metaCoinEthBalance;

    meta.getBalance.call(accounts[0]).then(function(outCoinBalance){
      metaCoinBalance = outCoinBalance.toNumber();
      return meta.getBalanceInEth.call(accounts[0]);
    }).then(function(outCoinBalanceEth){
      metaCoinEthBalance = outCoinBalanceEth.toNumber();
      
    }).then(function(){
      assert.equal(metaCoinEthBalance,2*metaCoinBalance,"Library function returned unexpeced function, linkage may be broken");
      
    }).then(done).catch(done);
  });
  
  it("should send coin correctly", function(done) {
	  
    var meta = MetaCoin.deployed();

    // Get initial balances of first and second account.
    var account_one = accounts[0];
    var account_two = accounts[1];

    var account_one_starting_balance;
    var account_two_starting_balance;
    var account_one_ending_balance;
    var account_two_ending_balance;

    var amount = 10;

    meta.getBalance.call(account_one).then(function(balance) {
      account_one_starting_balance = balance.toNumber();
      return meta.getBalance.call(account_two);
    }).then(function(balance) {
      account_two_starting_balance = balance.toNumber();
      return meta.sendCoin(account_two, amount, {from: account_one});
    }).then(function() {
      return meta.getBalance.call(account_one);
    }).then(function(balance) {
      account_one_ending_balance = balance.toNumber();
      return meta.getBalance.call(account_two);
    }).then(function(balance) {
      account_two_ending_balance = balance.toNumber();

      assert.equal(account_one_ending_balance, account_one_starting_balance - amount, "Amount wasn't correctly taken from the sender");
      assert.equal(account_two_ending_balance, account_two_starting_balance + amount, "Amount wasn't correctly sent to the receiver");
    }).then(done).catch(done);
  });
  
});
     其中關鍵字段已經給出了一些簡單的解釋,重點是其中的then語句的使用,對於then語句,其實就是我們合約中預先規定的合約辦法,即一個事件來了後該做怎麼樣的處理。

發佈了212 篇原創文章 · 獲贊 32 · 訪問量 38萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章