zoukankan      html  css  js  c++  java
  • 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)
    	}
    }
    
    
  • 相关阅读:
    在Ubuntu中通过update-alternatives切换软件版本
    SCons: 替代 make 和 makefile 及 javac 的极好用的c、c++、java 构建工具
    mongodb 的使用
    利用grub从ubuntu找回windows启动项
    How to Repair GRUB2 When Ubuntu Won’t Boot
    Redis vs Mongo vs mysql
    java script 的工具
    python 的弹框
    how to use greendao in android studio
    python yield的终极解释
  • 原文地址:https://www.cnblogs.com/liuhui5599/p/14187593.html
Copyright © 2011-2022 走看看