Fabric 2.x 智慧合約開發記錄

落雷發表於2023-09-10

表象:Return schema invalid. required items must be unique [recovered]

雖然 Fabric v2.2 已經發布了很久了,但之前因為專案歷史問題,一直使用的都是 Fabric v1.4.8,所以智慧合約也一直使用的都是 github.com/hyperledger/fabric/core/chaincode/shim 包。

在合約開發過程中,我一般都是使用下面的介面格式來定義合約的業務邏輯:

func create(stub shim.ChaincodeStubInterface, payload string) ([]byte, error)

在開發 Fabric v2.2 的智慧合約時, 使用 github.com/hyperledger/fabric-contract-api-go/contractapi 替換 github.com/hyperledger/fabric/core/chaincode/shim,介面格式如下:

func create(ctx contractapi.TransactionContextInterface, payload string) ([]byte, error)

然而這樣的介面在合約示例化的時候翻車了:

Error compiling schema for SmartContract [create]. Return schema invalid. required items must be unique [recovered]

翻閱 github.com/hyperledger/fabric-contract-api-go 時,在其使用教程發現一些限制:

  • 合同的函式只能接受以下型別的引數:
    • string
    • bool
    • int(包括 int8、int16、int32 和 int64)
    • uint(包括 uint8、uint16、uint32 和 uint64)
    • float32
    • float64
    • time.Time
    • 任何允許型別的陣列/切片
    • 結構體(其公共欄位全部屬於允許型別或另一個結構體)
    • 指向結構體的指標
    • 具有鍵型別為 string 和值為任何允許型別的對映
    • interface{}(僅當直接傳入時才允許,在透過事務呼叫時將接收一個 string 型別)
  • 合同的函式還可以接受事務上下文,前提是:
    • 它作為第一個引數傳入
    • 二選一:
      • 它要麼是型別為 *contractapi.TransactionContext 的物件,要麼是在鏈碼中定義的自定義事務上下文,用於合同的使用
      • 它是一個介面,用於合同的事務上下文型別符合該介面,例如 contractapi.TransactionContextInterface
  • 合同的函式只能返回零、一個或兩個值:
    • 如果函式被定義為返回零值,那麼對該合同函式的所有呼叫將返回成功響應
    • 如果函式被定義為返回一個值,那麼該值可以是引數列表中列出的任何允許型別之一(除了 interface{}),或者是錯誤。
    • 如果函式被定義為返回兩個值,那麼第一個值可以是引數列表中列出的任何允許型別之一(除了 interface{}),第二個值必須是錯誤。

仔細閱讀會發現 func create(ctx contractapi.TransactionContextInterface, payload string) ([]byte, error) 並沒有違法上面的規則,但示例化的時候就是無法透過。

上面的報錯資訊也明確說了是返回值不對,那就改下介面的返回值:

func create(ctx contractapi.TransactionContextInterface, payload string) (string, error)
func create(ctx contractapi.TransactionContextInterface, payload string) (*Company, error)
func create(ctx contractapi.TransactionContextInterface, payload string) (int, error)

修改後在進行例項化,這次不再報錯了。

但是明明之前的也沒有違反規則,為什麼會報錯呢?想不通為什麼,所以準備給官方提個Issue,萬一真是個bug呢?

結果就在issues裡發現了這個Possible issues with byte[] as return type,一看日期Oct 20, 2021,快兩年了也沒官方的回應......

結論

最後搜了一圈也沒找到原因,檢視原始碼,感覺問題可能是出在 contractFunctionReturns,具體還得等研究完原始碼之後才能有答案

type contractChaincodeContract struct {
	info                      metadata.InfoMetadata
	functions                 map[string]*internal.ContractFunction
	unknownTransaction        *internal.TransactionHandler
	beforeTransaction         *internal.TransactionHandler
	afterTransaction          *internal.TransactionHandler
	transactionContextHandler reflect.Type
}

// ContractChaincode a struct to meet the chaincode interface and provide routing of calls to contracts
type ContractChaincode struct {
	DefaultContract       string
	contracts             map[string]contractChaincodeContract
	metadata              metadata.ContractChaincodeMetadata
	Info                  metadata.InfoMetadata
	TransactionSerializer serializer.TransactionSerializer
}

// ContractFunction contains a description of a function so that it can be called by a chaincode
type ContractFunction struct {
	function reflect.Value
	callType CallType
	params   contractFunctionParams
	returns  contractFunctionReturns
}

type contractFunctionReturns struct {
	success reflect.Type
	error   bool
}

孟斯特

宣告:本作品採用署名-非商業性使用-相同方式共享 4.0 國際 (CC BY-NC-SA 4.0)進行許可,使用時請註明出處。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 戀水無意


相關文章