Hyperledger fabric 鏈碼篇GO(四)

Hyperledger fabric 鏈碼篇GO(四)

fabric中的鏈碼也就是我們區塊鏈所認知的智能合約,fabric中可由nodejs,java,go編寫,本篇只針對GO語言編寫鏈碼。將詳細介紹鏈碼編寫所必須的函數以及相關功能函數。

1、常識

  • 鏈碼的包名指定

    // xxx.go
    package main
    
  • 必須要引入的包

    import(
        "github.com/hyperledger/fabric/core/chaincode/shim"
        pb "github.com/hyperledger/fabric/protos/peer"
        //pb爲別名的意思
    )
    
  • 鏈碼的書寫要求

    //自定義結構體,類,基於這個類實現接口函數
    type Test struct{
    	//空着即可
    }
    
    
  • 鏈碼API查詢

    //go語言的鏈碼包shim
    https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim
    

注意事項

要注意put寫入的數據狀態不會立刻得到獲取,因爲put只是執行鏈碼的模擬交易,並不會真正將狀態保存在賬本中,必須經過orderer達成共識之後,將數據狀態保存在區塊中,然後保存在各peer節點的賬本中

2、函數及鏈碼基本結構

  • success

    // Success ... status:200
    //鏈碼執行後返回成功信息
    func Success(payload []byte) pb.Response {
    	return pb.Response{
    		Status:  OK,
    		Payload: payload,
    	}
    }
    
  • error

    // Error ...status:500
    //鏈碼執行後返回錯誤信息
    func Error(msg string) pb.Response {
    	return pb.Response{
    		Status:  ERROR,
    		Message: msg,
    	}
    }
    

2.1、基本結構

下面是編寫一個鏈碼的基本框架,由空結構體,Init函數和Invoke函數,主函數組成

package main
//引入必要的包
import (
	"fmt"
	"strconv"
	"github.com/hyperledger/fabric/core/chaincode/shim"
    //別名pb
	pb "github.com/hyperledger/fabric/protos/peer"
)
// 聲明一個結構體,必備,爲空
type SimpleChaincode struct {
}
//添加Init函數
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
		//實現鏈碼初始化或升級時的處理邏輯
       //編寫時可靈活使用stub中的API
}
//添加Invoke函數
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	//實現鏈碼運行中被調用或查詢時的處理邏輯
    //編寫時可靈活使用stub中的API
}
//主函數,需要調用shim.Start()方法啓動鏈碼
func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

2.2、Init函數

Init方法是系統初始化方法,當執行命令peer chaincode instantiate實例化chaincode時會調用該方法,同時命令中-c選項後面內容爲會作爲參數傳入Init方法中。

#shell 命令
$ peer chaincode instantiate -o orderer.example.com:7050  -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a","100", "b", "100"]}' 

Args中共有5個參數,第一個爲固定值,後面4個爲參數,如果傳入數據太多,可以採用json等數據格式

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
		 _ ,args := stub.GetFunctionAndParameters()
    return shim.Success ([]byte("success init!!!"))
}

2.3、Invoke函數

Invoke方法的主要作用爲寫入數據,比如發起交易等,在執行命令 peer chaincode invoke 時系統會調用該方法,並把-c後的參數傳入invoke方法中。

參數原理和init類似

3、賬本操作API

ChaincodeStubInterface

3.1、參數解析API

含義:調用鏈碼時需要給被調用的目標函數/方法傳遞參數,與參數解析相關的API 提供了獲取這些參數(包括被調用的目標函數/方法名稱)的方法

GetFunctionAndParameters

  • 獲取客戶端傳入的參數GetFunctionAndParameters()
//源碼
func (s *ChaincodeStub) GetFunctionAndParameters() (function string, params []string) {
	allargs := s.GetStringArgs()
	function = ""
	params = []string{}
	if len(allargs) >= 1 {
        //返回時第一個爲函數名,後面爲其他參數
		function = allargs[0]
		params = allargs[1:]
	}
	return
}
//執行命令
peer chaincode invoke -o orderer.example.com:7050  -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["invoke","a", "b", "10"]}' 
//示例代碼
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    _ ,args := stub.GetFunctionAndParameters()
    //獲取執行命令中的參數a,b,10
    var a_parm = args[0]
    var b_parm = args[1]
    var c_parm = args[2]
    return shim.Success ([]byte("success invoke!!!"))
}

3.2、賬本數據狀態操作API

含義:該類型API提供了對賬本數據狀態進行操作的方法,包括對狀態數據的查詢以及事務處理

GetState

  • GetState(key string) ([]byte, error) :根據指定的key查詢相應的數據狀態

    func (s *ChaincodeStub) GetState(key string) ([]byte, error) {
    	// Access public data by setting the collection to empty string
    	collection := ""
    	return s.handler.handleGetState(collection, key, s.ChannelID, s.TxID)
    }
    //示例代碼
    func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
        keyvalue, err := stub.GetState("user1")
        return shim.Success(keyvalue)
    }
    

PutState

  • PutState(key string, value []byte) error:根據指定的key,將相應的value保存在分類賬本中

    func (s *ChaincodeStub) PutState(key string, value []byte) error {
    	if key == "" {
    		return errors.New("key must not be an empty string")
    	}
    	// Access public data by setting the collection to empty string
    	collection := ""
    	return s.handler.handlePutState(collection, key, value, s.ChannelID, s.TxID)
    }
    //示例代碼
    func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
        stub.PutState("user1",[]byte("putvalue"))
        return shim.Success([]byte("sucess invoke putstate"))
    }
    

DelState

  • DelState(key string) error:根據指定的key將對應的數據狀態刪除

    func (s *ChaincodeStub) DelState(key string) error {
    	// Access public data by setting the collection to empty string
    	collection := ""
    	return s.handler.handleDelState(collection, key, s.ChannelID, s.TxID)
    }
    func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
        err :=stub.DelState("user1")
        return shim.Success("delete success")
    }
    

GetStateByRange

  • GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error):根據指定的開始key和結束key,查詢範圍內的所有數據狀態,注意,結束key對應的數據狀態不包含在返回的結果集中

    func (s *ChaincodeStub) GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) {
    	if startKey == "" {
    		startKey = emptyKeySubstitute
    	}
    	if err := validateSimpleKeys(startKey, endKey); err != nil {
    		return nil, err
    	}
    	collection := ""
    
    	// ignore QueryResponseMetadata as it is not applicable for a range query without pagination
    	iterator, _, err := s.handleGetStateByRange(collection, startKey, endKey, nil)
    	return iterator, err
    }
    //示例代碼
    func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
        startKey = "startKey"
        endKey = "endKey"
        //根據範圍查詢,得到StateQueryIteratorInterface迭代器接口
        keyIter , err := stub.getStateByRange(startKey,endKey)
        //關閉迭代器接口
        defer keyIter.close()
        var keys []string
        for keyIter.HasNext(){            //如果有下一個節點
            //得到下一個鍵值對
            response, iterErr := keysIter.Next()
            if iterErr != nil{
            	return shim.Error(fmt.Sprintf("find an error  %s",iterErr))
            }
            keys = append(keys,response.Key)            //存儲鍵值到數組中
        }
        //遍歷key數組
        for key, value := range keys{
            fmt.Printf("key %d contains %s",key ,value)
        }
        //編碼keys數組爲json格式
        jsonKeys ,err = json.Marshal(keys)
        if err != nil{
            return shim.Error(fmt.Sprintf("data marshal json error:  %s",err))
        }
        //將編碼之後的json字符串傳遞給客戶端
        return shim.Success(jsonKeys)
    }
    

GetHistoryForKey

  • GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error):根據指定的key查詢所有的歷史記錄信息

    func (s *ChaincodeStub) GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) {
    	response, err := s.handler.handleGetHistoryForKey(key, s.ChannelID, s.TxID)
    	if err != nil {
    		return nil, err
    	}
    	return &HistoryQueryIterator{CommonIterator: &CommonIterator{s.handler, s.ChannelID, s.TxID, response, 0}}, nil
    }
    
    //示例代碼
    func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
        
        keyIter , err := stub.GetHistoryForKey("user1")
        if err != nil{
                return shim.Error(fmt.Sprintf("GetHistoryForKey error:  %s",err))
            }
        //關閉迭代器接口
        defer keyIter.close()
        var keys []string
        for keyIter.HasNext(){            //如果有下一個節點
            //得到下一個鍵值對
            response, iterErr := keysIter.Next()
            if iterErr != nil{
            	return shim.Error(fmt.Sprintf("find an error  %s",iterErr))
            }
            //交易編號
            txid := response.TxId 
            //交易的值
            txvalue = response.Value
            //當前交易的狀態
            txstatus = response.IsDelete	
            //交易發生的時間戳
            txtimestamp = response.Timestamp
            keys = append(keys,txid)            //存儲鍵值到數組中
        }
        //編碼keys數組爲json格式
        jsonKeys ,err = json.Marshal(keys)
        if err != nil{
            return shim.Error(fmt.Sprintf("data marshal json error:  %s",err))
        }
        //將編碼之後的json字符串傳遞給客戶端
        return shim.Success(jsonKeys)
    }
    

CreateCompositeKey

  • CreateCompositeKey(objectType string, attributes []string) (string, error):創建一個複合鍵

    func (s *ChaincodeStub) CreateCompositeKey(objectType string, attributes []string) (string, error) {
    	return CreateCompositeKey(objectType, attributes)
    }
    
    func CreateCompositeKey(objectType string, attributes []string) (string, error) {
    	if err := validateCompositeKeyAttribute(objectType); err != nil {
    		return "", err
    	}
    	ck := compositeKeyNamespace + objectType + string(minUnicodeRuneValue)
    	for _, att := range attributes {
    		if err := validateCompositeKeyAttribute(att); err != nil {
    			return "", err
    		}
    		ck += att + string(minUnicodeRuneValue)
    	}
    	return ck, nil
    }
    

SplitCompositeKey

  • SplitCompositeKey(compositeKey string) (string, []string, error):對指定的複合鍵進行分割

    func (s *ChaincodeStub) SplitCompositeKey(compositeKey string) (string, []string, error) {
    	return splitCompositeKey(compositeKey)
    }
    
    func splitCompositeKey(compositeKey string) (string, []string, error) {
    	componentIndex := 1
    	components := []string{}
    	for i := 1; i < len(compositeKey); i++ {
    		if compositeKey[i] == minUnicodeRuneValue {
    			components = append(components, compositeKey[componentIndex:i])
    			componentIndex = i + 1
    		}
    	}
    	return components[0], components[1:], nil
    }
    

GetQueryResult

  • GetQueryResult(query string) (StateQueryIteratorInterface, error) :對狀態數據庫進行富查詢,目前支持富查詢的只有CouchDB

    func (s *ChaincodeStub) GetQueryResult(query string) (StateQueryIteratorInterface, error) {
    	// Access public data by setting the collection to empty string
    	collection := ""
    	// ignore QueryResponseMetadata as it is not applicable for a rich query without pagination
    	iterator, _, err := s.handleGetQueryResult(collection, query, nil)
    
    	return iterator, err
    }
    

InvokeChaincode

  • InvokeChaincode(chaincodeName string, args [][]byte, channel string):調用其他鏈碼

    //鏈碼名,傳遞參數,通道名
    func (s *ChaincodeStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response {
    	// Internally we handle chaincode name as a composite name
    	if channel != "" {
    		chaincodeName = chaincodeName + "/" + channel
    	}
    	return s.handler.handleInvokeChaincode(chaincodeName, args, s.ChannelID, s.TxID)
    }
    //示例代碼
    func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
        //設置參數,a向b轉賬11
        trans := [][]byte{[]byte("invoke"),[]byte("a"),[]byte("b"),[]byte("11")}
        //調用chaincode
        response := stub.InvokeChaincode("mycc",trans,"mychannel")
        //判斷是否操作成功
        if  response.Status != shim.OK {
            errStr := fmt.Sprintf("Invoke failed ,error : %s ", response.Payload)
            return shim.Error(errStr)
        }
        return shim.Success([]byte"轉賬成功")
    }
    

3.3、交易信息API

含義:獲取提交的交易信息的相關API

GetTxID

  • GetTxID() string :返回交易提案中指定的交易ID

    func (s *ChaincodeStub) GetTxID() string {
    	return s.TxID
    }
    

GetChannelID

  • GetChannelID() string:返回交易提案中指定通道的ID

    func (s *ChaincodeStub) GetChannelID() string {
    	return s.ChannelID
    }
    

GetTxTimestamp

  • GetTxTimestamp() (*timestamp.Timestamp, error):返回交易創建的時間戳,這個時間戳時peer接收到交易的具體時間

    func (s *ChaincodeStub) GetTxTimestamp() (*timestamp.Timestamp, error) {
    	hdr := &common.Header{}
    	if err := proto.Unmarshal(s.proposal.Header, hdr); err != nil {
    		return nil, fmt.Errorf("error unmarshaling Header: %s", err)
    	}
    
    	chdr := &common.ChannelHeader{}
    	if err := proto.Unmarshal(hdr.ChannelHeader, chdr); err != nil {
    		return nil, fmt.Errorf("error unmarshaling ChannelHeader: %s", err)
    	}
    
    	return chdr.GetTimestamp(), nil
    }
    

GetBinding

  • GetBinding() ([]byte, error):返回交易的綁定信息,如一些臨時性信息,以避免重複性攻擊

    func (s *ChaincodeStub) GetBinding() ([]byte, error) {
    	return s.binding, nil
    }
    

GetSignedProposal

  • GetSignedProposal() (*pb.SignedProposal, error):返回與交易提案相關的簽名身份信息

    func (s *ChaincodeStub) GetSignedProposal() (*pb.SignedProposal, error) {
    	return s.signedProposal, nil
    }
    

    GetCreator

  • GetCreator() ([]byte, error) :返回該交易提交者的身份信息

    func (s *ChaincodeStub) GetCreator() ([]byte, error) {
    	return s.creator, nil
    }
    

GetTransient

  • GetTransient() (map[string][]byte, error):返回交易中不會被寫至賬本中的一些臨時性信息

    func (s *ChaincodeStub) GetTransient() (map[string][]byte, error) {
    	return s.transient, nil
    }
    

3.4、事件處理API

含義:與事件處理相關的API

SetEvent

  • SetEvent(name string, payload []byte) error:設置事件,包括事件名稱和內容

    // SetEvent documentation can be found in interfaces.go
    func (s *ChaincodeStub) SetEvent(name string, payload []byte) error {
    	if name == "" {
    		return errors.New("event name can not be empty string")
    	}
    	s.chaincodeEvent = &pb.ChaincodeEvent{EventName: name, Payload: payload}
    	return nil
    }
    

3.5、對PrivateData操作的API

含義:hyperledger fabric在1.2.0版本中新增的對私有數據操作的相關API

//根據指定key,從指定的私有數據集中查詢對應的私有數據
func (s *ChaincodeStub) GetPrivateData(collection string, key string) ([]byte, error)
//根據給定的部分組合鍵的集合,查詢給定的私有狀態
func (s *ChaincodeStub) GetPrivateDataByPartialCompositeKey(collection, objectType string, attributes []string) (StateQueryIteratorInterface, error)
//根據指定的開始key和結束key查詢範圍內的私有數據(不包括結束key)
func (s *ChaincodeStub) GetPrivateDataByRange(collection, startKey, endKey string) (StateQueryIteratorInterface, error)
//根據指定的查詢字符串執行富查詢
func (s *ChaincodeStub) GetPrivateDataQueryResult(collection, query string) (StateQueryIteratorInterface, error)
//將指定的key與value保存到私有數據集中
func (s *ChaincodeStub) PutPrivateData(collection string, key string, value []byte) error
//根據key刪除相應數據
func (s *ChaincodeStub) DelPrivateData(collection string, key string) error

4、背書策略

背書的過程就是一筆交易被確認的過程,而背書策略就是用來指示相關的參與方如何對交易進行確認。背書策略的設置是通過部署鏈碼實例化時instantiate命令中的-P參數來設置的

peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.member','Org2MSP.member')"

此命令表示需要組織1和組織2中任意一個用戶共同來參與交易的確認並且同意,這樣的交易才能生效並被記錄到去區塊鏈中

#也可以指定背書節點,使用peerAddresses來指定背書節點
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'
  • 背書規則示例1

    #需要組織中任意用戶共同參與背書
    "AND ('Org1MSP.member',  'Org2MSP.member')"
    
  • 背書規則示例2

    #只要組織中任意一個用戶驗證即可
    "OR ('Org1MSP.member',  'Org2MSP.member')"
    
  • 背書規則示例3

    #兩種方法讓交易生效
    #    1、組織1中有成員驗證成功
    #    2、組織2和組織3中有成員共同參與驗證成功
    "OR ('Org1MSP.member', AND ('Org2MSP.member',  'Org3MSP.member'))"
    

注意:背書規則只針對寫入數據的操作進行校驗,對於查詢類的背書不操作

5、應用示例

設計了一個鏈碼,實現了資產管理,轉賬交易的功能,主要實現了以下幾個函數

  • init函數

    初始化鏈碼,設定賬戶名稱及資產額度

  • invoke函數

    實現函數的識別,並轉到不同的函數功能

  • del函數

    刪除賬戶

  • find函數

    查看賬戶額度

  • set函數

    根據賬戶名稱設定要存入的金額

  • get函數

    根據賬戶名稱設定要取出的金額

package main

import (
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/protos/peer"
	"strconv"
)

type AssetChaincode struct {
	//empty
}

//Init function
func (t *AssetChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response {
	_, args := stub.GetFunctionAndParameters()
	if len(args) != 4 {
		return shim.Error("Must four initialization parameters, representing name and money ,respectively")
	}
	var a = args[0]
	var acount = args[1]
	var b = args[2]
	var bcount = args[3]
	if len(a) < 2 {
		return shim.Error("the length of name must not less than 2")
	}
	if len(b) < 2 {
		return shim.Error("the length of name must not less than 2")
	}
	_, err := strconv.Atoi(acount)
	if err != nil {
		return shim.Error(a + " account is false")
	}
	_, err = strconv.Atoi(bcount)
	if err != nil {
		return shim.Error(b + " account is false")
	}

	err = stub.PutState(a, []byte(acount))
	if err != nil {
		return shim.Error(a + " Occuring error when saving the data")
	}
	err = stub.PutState(b, []byte(bcount))
	if err != nil {
		return shim.Error(b + " Occuring error when saving the data")
	}
	return shim.Success([]byte("Init success"))
}

//Invoke function
func (t *AssetChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
	fun, args := stub.GetFunctionAndParameters()
	if fun == "set" {
		return set(stub, args)
	} else if fun == "get" {
		return get(stub, args)
	} else if fun == "payment" {
		return payment(stub, args)
	} else if fun == "del" {
		return del(stub, args)
	} else if fun == "find" {
		return find(stub, args)
	}
	return shim.Error("not have this ability")
}

//find function
func find(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("the number of parameters must one")
	}
	result, err := stub.GetState(args[0])
	if err != nil {
		return shim.Error("occor error when reading the data")
	}
	if result == nil {
		return shim.Error("no data by the key")
	}
	return shim.Success(result)
}

//payment function
func payment(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 3 {
		return shim.Error("payment format error")
	}

	var source, target string
	source = args[0]
	target = args[1]
	asset, err := strconv.Atoi(args[2])
	if err != nil {
		return shim.Error("transfer amount atoi failed")
	}
	sourceS, err := stub.GetState(source)
	if err != nil {
		return shim.Error("query source asset failed")
	}
	targetS, err := stub.GetState(target)
	if err != nil {
		return shim.Error("query target asset failed")
	}
	sourceasset, err := strconv.Atoi(string(sourceS))
	if err != nil {
		return shim.Error("source asset transform int failed")
	}
	targetasset, err := strconv.Atoi(string(targetS))
	if err != nil {
		return shim.Error("target asset transform int failed")
	}
	if sourceasset < asset {
		return shim.Error("source asset don't have enough balance")
	}
	sourceasset -= asset
	targetasset += asset
	err = stub.PutState(source, []byte(strconv.Itoa(sourceasset)))
	if err != nil {
		return shim.Error("after save payment soure asset failed")
	}
	err = stub.PutState(target, []byte(strconv.Itoa(targetasset)))
	if err != nil {
		return shim.Error("after save payment target asset failed")
	}
	return shim.Success([]byte("payment success"))
}

//delete function
func del(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args)!=1{
		return shim.Error("elete account format error")
	}
	err := stub.DelState(args[0])
	if err!= nil{
		return shim.Error("delete account error")
	}
	return shim.Success([]byte("delete account success"+args[0]))
}

//set function
func set(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 2 {
		return shim.Error("set account asset format error")
	}
	result, err := stub.GetState(args[0])
	if err != nil {
		return shim.Error("occor error when reading the data")
	}
	if result == nil {
		return shim.Error("no data by the key")
	}
	asset,err := strconv.Atoi(string(result))
	if err!= nil{
		return shim.Error("transfrom account balance error")
	}
	val,err := strconv.Atoi(string(args[1]))
	if err!= nil{
		return shim.Error("transfrom set balance error")
	}
	val += asset
	err = stub.PutState(args[0],[]byte(strconv.Itoa(val)))
	if err != nil {
		return shim.Error("save balance error")
	}
	return shim.Success([]byte("set asset success!"))
}

//get function
func get(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 2 {
		return shim.Error("t account asset format error")
	}
	result, err := stub.GetState(args[0])
	if err != nil {
		return shim.Error("occor error when reading the data")
	}
	if result == nil {
		return shim.Error("no data by the key")
	}
	asset,err := strconv.Atoi(string(result))
	if err!= nil{
		return shim.Error("transfrom account balance error")
	}
	val,err := strconv.Atoi(string(args[1]))
	if err!= nil{
		return shim.Error("transfrom set balance error")
	}
	if asset<val{
		return shim.Error("not have enough asset")
	}
	asset -= val
	err = stub.PutState(args[0],[]byte(strconv.Itoa(asset)))
	if err != nil {
		return shim.Error("save balance error")
	}
	return shim.Success([]byte("get asset success!"))
}

//main function
func main() {
	err := shim.Start(new(AssetChaincode))
	if err != nil {
		fmt.Printf("start assetchaincode error!,the message is :%s", err)
	}
}

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