
# GO SDK Demo
本节提供了一个基于Go SDK的Demo，帮助用户开发自己的Go客户端应用程序。
![](https://support.huaweicloud.com/devg-bcs/public_sys-resources/note_3.0-zh-cn.png)
本Demo仅适用于Hyperledger Fabric增强版的区块链实例。
#### 准备工作
- 准备弹性云服务器。
- 在弹性云服务器上安装golang环境，Go版本要求：1.12及以上，1.16以下（\>=1.12，\<1.16）。
- 获取Go SDK源码，获取方法：登录区块链服务管理控制台，进入"应用案例"，单击"GO示例Demo-GO SDK Demo"中Go应用程序源码的"下载"按钮。
 
#### 购买区块链实例
购买区块链实例，具体请参见实例部署-基于CCE集群。具体请参见实例部署-[基于CCE集群](https://support.huaweicloud.com/usermanual-bcs/bcs_usermanual_0014.html)。
#### 安装及实例化链代码
本示例使用链代码文件获取方法：登录区块链服务管理控制台，进入"应用案例"，单击"GO示例Demo-GO SDK Demo"中Go语言示例链代码的"下载"按钮。
参考章节：[用户指南-区块链管理-链代码管理](https://support.huaweicloud.com/usermanual-bcs/bcs_usermanual_0003.html)。
参考章节：用户指南-区块链管理-链代码管理。
 #### 下载SDK和证书
1. 登录区块链服务管理控制台。
2. 在"实例管理"页面，选择"Hyperledger Fabric增强版"页签，单击对应实例卡片上的"获取客户端配置"。
3. 在新打开的页面，勾选"SDK文件"，SDK配置参数如下： 
   
   | 参数         | 值                                                                                                     |
   |:---|:---|
   | 链代码名称      | chaincode 说明： 链代码名称需要和链代码安装\&实例化时的一致。 |
   | 证书存放路径     | /root/gosdkdemo/config                                                                                |
   | 通道名称       | channel                                                                                               |
   | 组织\&Peer节点 | 保持系统默认                                                                                                |
      
   勾选"共识节点证书"。
   勾选"Peer节点证书"，指定节点组织选择organization，勾选"管理员证书"。
   
   
4. 单击"下载"。下载SDK配置文件、orderer组织的管理员证书和organization组织的管理员证书。
 
#### 部署应用
1. 将Go SDK源码下载至准备的弹性云服务器"/root"路径下并解压。 下载方法：登录区块链服务管理控制台，进入"应用案例"，单击"GO示例Demo-GO SDK Demo"中Go应用程序源码的"下载"按钮。
   
2. 将[下载SDK和证书]步骤中的zip文件解压后，把configs文件夹中的orderer文件夹、peer文件夹、sdk-config.json、sdk-config.yaml文件全部复制到/root/gosdkdemo/config/目录下。
3. 在代码中找到"/gosdkdemo/src/main.go"文件，进行以下修改：
   1. 将configFile中的值修改为实际的SDK配置文件名称，例如：demo-channel-sdk-config.yaml。
   
   2. 将org的值修改为organization对应的组织哈希值。
      在通道管理页面，单击"查看节点"获取组织的哈希值（MSP标识去掉"MSP"后缀即为对应组织的哈希）。
      ```
      var (    
          configFile = "/root/gosdkdemo/config/go-sdk-demo-channel-sdk-config.yaml"    
          org = " 9103f17cb6b4f69d75982eb48bececcc51aa3125"
       )
      ```
      
    
4. 使用go mod方式配置GOPATH路径，请根据实际安装路径进行配置。
   1. 设置环境变量GO111MODULE为on。
      ```
      export GO111MODULE=on
      ```
      
   
   2. go.mod文件如图所示，用户需要根据实际安装路径修改replace代码。
      ```
      module main  
      go 1.15
      // 指定导入的依赖包及其版本
      require ( 
              github.com/bitly/go-simplejson v0.5.0 
              github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869// indirect 
              github.com/ghodss/yaml v1.0.0 
              github.com/hyperledger/fabric-sdk-go v1.0.0 
              github.com/pkg/errors v0.9.1 
              github.com/spf13/viper v1.7.1 
      )  
      // 以项目路径为/root/gosdkdemo/src为例 
      replace github.com/hyperledger/fabric-sdk-go => /root/gosdkdemo/src/github.com/hyperledger/fabric-sdk-go
      ```
      
    
5. 找到"gosdkdemo/src"路径下的main.go文件，执行如下命令：
   ```
   go run main.go
   ```
   ![](https://support.huaweicloud.com/devg-bcs/zh-cn_image_0000001127876741.png "点击放大")
   
 
#### 通过内存传入私钥
如果用户需要对私钥文件进行加密，并在demo中解密后传入FabricSDK。
1. 对于TLS私钥 在main.go文件的initializeSdk函数中，按如下方式调用函数：
   ```
   encryptedtlsKey,err := GetTlsCryptoKey(org)  //从配置文件指定路径下读取加密过的TLS私钥
   //用户按照自行规定的加解密方法对encryptedtlsKey进行解密得到decryptedTlsKey字符串
   SetClientTlsKey(decryptedTlsKey)      //将解密后的TLS私钥传入Fabric-SDK，实现通过内存传入TLSKey
   ```
   
2. 对于MSP私钥 在main.go文件的insert函数中，按如下方式调用函数：
   ```
   encryptedbytekey,_:= GetPrivateKeyBytes(org)  //从配置文件指定路径下读取加密过的MSP私钥
   //用户按照自行规定的加解密方法对encryptedbytekey进行解密得到decryptedKey字符串
   SetPrivateKey(decryptedKey) //将解密后的MSP私钥赋值给全局变量privateKey并通过该变量传入
   ```
   
#### 常用API接口
Fabric-sdk-go的主要入口是FabricSDK类，这个可以通过NewSDK()方法分别可以生成。
Fabric-sdk-go的常用操作基本都可以用这四个client实现：FabricClient，ChannelClient，ChannelMgmtClient，ResourceMgmtClient。
- **FabricSDK**
  FabricSDK在pkg\\fabsdk\\fabsdk.go中，通过New ()方法生成object。New ()方法支持可变参数Option，以下是生成FabricSDK的例子:
  var opts \[\]fabsdk.Option
  opts = append(opts, fabsdk.WithOrgid(org))
  opts = append(opts, fabsdk.WithUserName("Admin"))
  sdk, err = fabsdk.New(config.FromFile(configFile), opts...)
  configFile是SDK配置文件的路径。OrgId是SDK配置文件中的组织id。
  FabricSDK在def/fabapi/fabapi.go中，通过NewSDK()方法生成object。NewSDK()方法有一个Options参数，以下是生成Options参数的例子:
  ```
  deffab.Options{ConfigFile: configFile, LoggerFa
  logging.LoggerProvider(), UserName: sysadmin}
  ```
  ConfigFile是SDK配置文件的路径。LoggerFactory是可选的，不提供的话default会log到console。
  
- **FabricClient**
  FabricClient主要有以下常用的接口。
  
  | 接口名称                     | 描述                           | 参数值                                                              | 返回值                                                                                   |
  |:---|:---|:---|:---|
  | CreateChannel            | 创建Channel的接口，用于创建channel。    | request CreateChannelRequest      | txn.TransactionID, error                                                              |
  | QueryChannelInfo         | 查询Channel的接口，用于查询Channel的信息。 | name string, peers \[\]Peer      | Channel, error                                                                        |
  | InstallChaincode         | 安装链码的接口，安装链码到区块链中。           | request InstallChaincodeRequest | \[\]\*txn.TransactionPropos lResponse, string, error |
  | QueryChannels            | 查询channel的接口，查询区块链中已创建的通道。   | peer Peer                                                        | \*pb.ChannelQueryResponse, error                                                      |
  | QueryInstalledChaincodes | 查询已安装链码的接口，查询区块链中已安装的链码。     | peer Peer                                                        | \*pb.ChaincodeQueryResponse, error                                                    |
     
  
- **ChannelClient**
  ChannelClient主要包括链码查询和链码调用两类接口。
  
  | 接口名称              | 描述                                                                          | 参数值                                                                                                  | 返回值                                                   |
  |:---|:---|:---|:---|
  | Query             | 链码查询接口，调用链码进行查询。                                                            | request QueryRequest                                                | \[\]byte, error                                       |
  | QueryWithOpts     | 带options的链码查询接口，与Query类似，但是可以通过QueryOpts指定notifier, peers,和timeout。         | request QueryRequest, opt QueryOpts | \[\]byte, error                                       |
  | ExecuteTx         | 链码调用接口，用于链码的调用。                                                             | request ExecuteTxRequest                                             | TransactionID, error |
  | ExecuteTxWithOpts | 带options的链码调用接口，与ExecuteTx类似，但是可以通过ExecuteTxOpts指定notifier, peers,和timeout。 | request ExecuteTxRequestopt ExecuteTxOpts                           | TransactionID, error |
     
  

- **ChannelMgmtCLient**
  ChannelMgmtClient 只有两个接口SaveChannel(req SaveChannelRequest) error 和SaveChannelWithOpts(req SaveChannelRequest, opts SaveChannelOpts) error 这两个接口是用于创建channel用的，这两个接口里面具体实现会调用到FabricClient里createChannel()接口。
  

- **ResourceMgmtClient**
  ResourceMgmtClient主要就是与链码生命周期相关的接口和一个peer加入通道的接口。
  ![](https://support.huaweicloud.com/devg-bcs/public_sys-resources/note_3.0-zh-cn.png)
  链码的删除接口为BCS增加的接口，目前只实现了删除链码安装包的功能。
  
  | 接口名称                  | 描述                                                                      | 参数值                                                             | 返回值                          |
  |:---|:---|:---|:---|
  | InstallCC             | 安装链码，用于安装链码。                                                            | reqInstallCCRequest                                             | \[\]InstallCCResponse, error |
  | InstallCCWithOpts     | 带options的链码安装，与InstallCC类似，但是可以通过InstallCCOpts指定peers。                  | reqInstallCCRequest,opts InstallCCOpts                          | \[\]InstallCCResponse, error |
  | InstantiateCC         | 实例化链码接口，用于实例化链码。                                                        | channelID string,reqInstantiateCCRequest                        | error                        |
  | InstantiateCCWithOpts | 带options的链码实例化，与InstantiateCC类似，但是可以通过InstantiateCCOpts指定peers和timeout。 | channelID string,reqInstantiateCCRequest, optsInstantiateCCOpts | error                        |
  | UpgradeCC             | 升级链码，用于链码的升级。                                                           | channelID string,reqUpgradeCCRequest                            | error                        |
  | UpgradeCCWithOpts     | 带options的链码升级，与UpgradeCC类似，但是可以通过UpgradeCCOpts指定peers和timeout。          | channelID string,reqUpgradeCCRequest, optsUpgradeCCOpts         | error                        |
  | DeleteCC              | 删除链码，用于链码的删除，目前只有删除安装包的功能。                                              | channelID string,reqDeleteCCRequest                             | error                        |
  | DeleteCCWithOpts      | 带options的链码删除，与DeleteCC类似，但是可以通过DeleteCCWithOpts指定peers和timeout。        | channelID string,reqDeleteCCRequest,opts DeleteCCOpts           | error                        |
  | JoinChannel           | Peers加入Channel的接口，用于peers加入Channel。                                     | channelID string                                                | error                        |
  | JoinChannelWithOpts   | 带options的Peers加入Channel的接口，与JoinChannel类似，但是可以通过JoinChannelOpts指定peers。 | channelID string,optsJoinChannelOpts                            | error                        |
     
  ![](https://support.huaweicloud.com/devg-bcs/public_sys-resources/note_3.0-zh-cn.png)
  带options的接口都可以指定peers，peers可通过def/fabapi/pkgfactory.go 里的NewPeer(userName string, orgName string, url string, certificate string, serverHostOverride string, config config.Config) (fab.Peer, error) 生成。这个method比原生的NewPeer多两个参数userName, orgName, 这两个参数用于peer双向tls找到对应的tls证书。
  
 
#### 调用合约
Main.go是一个简单的客户端应用示例程序，主要是为了方便用户熟悉客户端开发的流程，主要包含以下步骤：
```
//1.导入相关包：Sdk包中提供了一些API，以便用户的应用程序能够访问链代码。
import (
   "fmt"  
   "github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
   "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"    ……
)
//2.创建文件配置：这部分封装了应用开发必要的一些公共配置，包括sdk配置文件路径、组织名
var (
   configFile = "/root/fabric-go-demo/config/go-sdk-demo-channel-sdk-config.yaml"
   org = "9103f17cb6b4f69d75982eb48bececcc51aa3125"
   ……
)
//3.加载配置文件
loadConfig()
//4. 初始化sdk
initializeSdk()
//5. 执行链代码，将数据写入账本，key = "testuser"，value= "100"
insert("insert",[][]byte{
   []byte("testuser"),
   []byte("100"),
})
//6.查询链代码，输出查询结果，key = "testuser" 
query("query",
[][]byte{
[]byte("testuser"),
})
```
表1调用函数介绍 
| 函数名                        | 说明                                                          |
|:---|:---|
| getOptsToInitializeSDK     | 解析配置文件，创建并返回fabsdk.Option对象。                                |
| GetDefaultChaincodeId      | 解析配置文件，返回chaincodeID。                                       |
| GetDefaultChannel          | 解析配置文件，返回channelID。                                         |
| UserIdentityWithOrgAndName | 用户身份验证，输入为组织名和用户名，返回为验证结果。                                  |
| ChannelClient              | 创建\*channel.Client对象，输入为组织名、用户名以及通道ID，返回\*channel.Client对象。 |
| insert                     | 将数据写入账本，输入参数为链码的对应方法名称以及要插入的键值对，返回为写入的结果。                   |
| query                      | 查询链上信息，输入参数为链码的对应方法名称以及要查询的数据，返回为查询的结果。                     |
   
