全部产品

智能合约Go开发指南

更新时间:2019-10-15 19:36:45

链码结构

Go 语言的链码主要由以下方法组成:

  1. // Chaincode interface must be implemented by all chaincodes. The fabric runs
  2. // the transactions by calling these functions as specified.
  3. type Chaincode interface {
  4. // Init is called during Instantiate transaction after the chaincode container
  5. // has been established for the first time, allowing the chaincode to
  6. // initialize its internal data
  7. Init(stub ChaincodeStubInterface) pb.Response
  8. // Invoke is called to update or query the ledger in a proposal transaction.
  9. // Updated state variables are not committed to the ledger until the
  10. // transaction is committed.
  11. Invoke(stub ChaincodeStubInterface) pb.Response
  12. }
  • Init: 链码在初始化和升级时调用此接口,初始化相关的数据。
  • Invoke:主要用于实现链码的内部业务逻辑,用户可以在该方法中实现相关的业务。
  • 在上述方法实现过程中,用户可以调用 ChaincodeStubInterface 的 API 接口和链上进行交互。

链码示例

Hyperledger Fabric 提供了很多官方链码样例,具体请参考fabric 官方示例。 我们以 Hyperledger Fabric 官方提供的 example02 样例为例,为大家介绍链码的开发规范。

简单示例

首先,我们看一个空链码结构的示例代码

  1. package main
  2. import (
  3. "github.com/hyperledger/fabric/core/chaincode/shim"
  4. "github.com/hyperledger/fabric/protos/peer"
  5. "fmt"
  6. )
  7. // 一个管理资产的简单链码
  8. type SimpleAsset struct {
  9. }
  10. //在链码实例化期时调用Init初始化数据。
  11. func (self * SimpleAsset)Init(stub shim.ChaincodeStubInterface) peer.Response {
  12. return shim.Success(nil)
  13. }
  14. // Invoke在每一比交易时都会被调用,该方法应该包含 set 以及 get 来创建和获取对应的键值
  15. func (self * SimpleAsset)Invoke(stub shim.ChaincodeStubInterface) peer.Response {
  16. return shim.Success(nil)
  17. }
  18. func main() {
  19. if err := shim.Start(new(SimpleAsset)); nil != err {
  20. fmt.Printf("实例化链码失败,err := %n", err.Error())
  21. }
  22. }

Init 示例

Init 函数在链码实例化以及升级的时候会被调用。在实现 Init 函数的过程中,可使用 Go 语言版本的合约 API 列表来对参数和分布式账本进行操作。

  1. func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
  2. fmt.Println("ex02 Init")
  3. // 调用 GetFunctionAndParameters 方法对参数进行解析
  4. _, args := stub.GetFunctionAndParameters()
  5. var A, B string // Entities
  6. var Aval, Bval int // Asset holdings
  7. var err error
  8. if len(args) != 4 {
  9. return shim.Error("Incorrect number of arguments. Expecting 4")
  10. }
  11. // 初始化相关数据
  12. A = args[0]
  13. Aval, err = strconv.Atoi(args[1])
  14. if err != nil {
  15. return shim.Error("Expecting integer value for asset holding")
  16. }
  17. B = args[2]
  18. Bval, err = strconv.Atoi(args[3])
  19. if err != nil {
  20. return shim.Error("Expecting integer value for asset holding")
  21. }
  22. fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
  23. // 调用 PutState 方法将数据写入账本中
  24. err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
  25. if err != nil {
  26. return shim.Error(err.Error())
  27. }
  28. err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
  29. if err != nil {
  30. return shim.Error(err.Error())
  31. }
  32. return shim.Success(nil)
  33. }

本示例要求用户输入的参数为KEY1_NAME, VALUE1, KEY2_NAME, VALUE2,并初始化2个键值对,调用 PutStat 将数据写入分布式账本中。

Invoke 示例

Invoke 函数是对用户具体业务逻辑的实现,用户可以根据不同的业务处理逻辑,调用不用的业务函数,如invokedeletequery 函数。

  1. // Invoke把用户调用的function细分到几个子function, 包含invoke,delete和query
  2. func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
  3. fmt.Println("ex02 Invoke")
  4. // 调用 GetFunctionAndParameters 方法对参数进行解析
  5. function, args := stub.GetFunctionAndParameters()
  6. if function == "invoke" {
  7. // Make payment of X units from A to B
  8. return t.invoke(stub, args)
  9. } else if function == "delete" {
  10. // Deletes an entity from its state
  11. return t.delete(stub, args)
  12. } else if function == "query" {
  13. // the old "Query" is now implemtned in invoke
  14. return t.query(stub, args)
  15. }
  16. return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
  17. }

invoke 函数

业务逻辑 invoke 函数实现了业务逻辑中的资产转移,将 A 的资产转移 X 个单位给 B。

  1. // Transaction makes payment of X units from A to B
  2. // invoke实现了两个键之间的value转移,输入参数为KEY1_NAME, KEY2_NAME,VALUE
  3. func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
  4. var A, B string // Entities
  5. var Aval, Bval int // Asset holdings
  6. var X int // Transaction value
  7. var err error
  8. if len(args) != 3 {
  9. return shim.Error("Incorrect number of arguments. Expecting 3")
  10. }
  11. A = args[0]
  12. B = args[1]
  13. // 获取A、B的当前资产情况
  14. Avalbytes, err := stub.GetState(A)
  15. if err != nil {
  16. return shim.Error("Failed to get state")
  17. }
  18. if Avalbytes == nil {
  19. return shim.Error("Entity not found")
  20. }
  21. Aval, _ = strconv.Atoi(string(Avalbytes))
  22. Bvalbytes, err := stub.GetState(B)
  23. if err != nil {
  24. return shim.Error("Failed to get state")
  25. }
  26. if Bvalbytes == nil {
  27. return shim.Error("Entity not found")
  28. }
  29. Bval, _ = strconv.Atoi(string(Bvalbytes))
  30. // Perform the execution
  31. X, err = strconv.Atoi(args[2])
  32. if err != nil {
  33. return shim.Error("Invalid transaction amount, expecting a integer value")
  34. }
  35. // 业务逻辑:实现资产的转移
  36. Aval = Aval - X
  37. Bval = Bval + X
  38. fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
  39. // 将更新后的资产更新到账本中
  40. err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
  41. if err != nil {
  42. return shim.Error(err.Error())
  43. }
  44. err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
  45. if err != nil {
  46. return shim.Error(err.Error())
  47. }
  48. return shim.Success(nil)
  49. }
  • 使用 API GetState 获取到 KEY_NAME 对应的资产总值
  • 调用业务逻辑实现 X 个资产单位的转移
  • 调用 API PutState 将更新后的资产情况写入到账本中

注:上述实现的是一个类似转账的简单逻辑,但并未对参数的合法性诸如转账金额大于零、余额不为负等进行校验。

delete 函数

业务逻辑 delete 函数实现了业务逻辑中的账户删除功能。

  1. // Deletes an entity from state
  2. func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
  3. if len(args) != 1 {
  4. return shim.Error("Incorrect number of arguments. Expecting 1")
  5. }
  6. A := args[0]
  7. // Delete the key from the state in ledger
  8. err := stub.DelState(A)
  9. if err != nil {
  10. return shim.Error("Failed to delete state")
  11. }
  12. return shim.Success(nil)
  13. }

query 函数

业务逻辑 query 函数实现了业务逻辑中的账户查询功能,通过调用 API GetState 查询对应账户的资产。

  1. // query callback representing the query of a chaincode
  2. func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
  3. var A string // Entities
  4. var err error
  5. if len(args) != 1 {
  6. return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
  7. }
  8. A = args[0]
  9. // Get the state from the ledger
  10. Avalbytes, err := stub.GetState(A)
  11. if err != nil {
  12. jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
  13. return shim.Error(jsonResp)
  14. }
  15. if Avalbytes == nil {
  16. jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
  17. return shim.Error(jsonResp)
  18. }
  19. jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
  20. fmt.Printf("Query Response:%s\n", jsonResp)
  21. return shim.Success(Avalbytes)
  22. }