# GRPC 接口约定

## 1 使用步骤

> grpc 简介: gRPC是一款语言中立、平台中立、开源的远程过程调用系统，即：gRPC 客户端和服务端可以在多种环境中运行交互，例如用java 写一个服务端，可以用go语言写客户端调用gRPC默认使用protocol buffers。

1. 导入 grpc 的 Proto 包文件，例如: `import "github.com/33cn/chain33/types"`
2. 创建 grpc 连接`conn, err := grpc.Dial(target string, opts ...DialOption)`
3. 调用 pb.go 函数，对 conn 进行封装，NewChain33Client(conn)

## 2 例子一 CreateRawTransaction

### 2.1 调用接口

```
rpc CreateRawTransaction(CreateTx) returns (UnsignTx) {}
```

**参数：**

```
message CreateTx {
    string to          = 1;
    int64  amount      = 2;
    int64  fee         = 3;
    bytes  note        = 4;
    bool   isWithdraw  = 5;
    bool   isToken     = 6;
    string tokenSymbol = 7;
    string execName    = 8;
    string execer      = 9;
}
```

**参数说明：**

| 参数        | 类型   | 是否必填 | 说明                                                         |
| :---------- | :----- | :------- | :----------------------------------------------------------- |
| to          | string | 是       | 发送到地址；如果是合约的充提则为合约地址，合约地址为execName转换而来，详情查看ConvertExectoAddr接口 |
| amount      | int64  | 是       | 发送金额，注意基础货币单位为10^8                             |
| fee         | int64  | 是       | 手续费，注意基础货币单位为10^8                               |
| note        | bytes  | 否       | 备注                                                         |
| isToken     | bool   | 否       | 是否是token类型的转账 （非token转账这个不用填 包括平行链的基础代币转账也不用填） |
| isWithdraw  | bool   | 是       | 是否为从合约中提款的交易，普通转账为false                    |
| tokenSymbol | string | 否       | token 的 symbol （非token转账这个不用填）                    |
| execName    | string | 否       | 目标合约名，如果要构造平行链上的转账或普通转账，此参数置空   |
| execer      | string | 是       | 资产的执行器名称，如果是普通转账，此处应填coins，如果是构造平行链的基础代币，此处要填写user.p.xxx.coins token同上 |

**返回数据：**

```
message UnsignTx {
    bytes data = 1;
}
```

**参数说明：**

| 参数 | 类型  | 说明                         |
| :--- | :---- | :--------------------------- |
| data | bytes | 交易对象的十六进制字符串编码 |

### 2.2 代码示例

假定 grpc服务地址：localhost:8802

```go
package main
import (
    "context"
    "github.com/33cn/chain33/common"
    chan33Ty "github.com/33cn/chain33/types"
    "google.golang.org/grpc"
)
func main() {
    conn, err := grpc.Dial("localhost:8802", grpc.WithInsecure(), grpc.WithBlock(), grpc.FailOnNonTempDialError(true))
    if err != nil {
        panic(err)
    }
    defer conn.Close()
    gclient := chan33Ty.NewChain33Client(conn)
    txData, err := gclient.CreateRawTransaction(context.Background(), &chan33Ty.CreateTx{Amount: 10000000, To: "16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp", Execer: "coins", ...})
    if err != nil {
        //异常处理逻辑
        panic(err)
    }
    txData...
}
```

## 3 例子二 QueryChain

### 3.1 调用接口

```
rpc QueryChain(ChainExecutor) returns (Reply) {}
```

**参数：**

```
message ChainExecutor {
    string driver    = 1;
    string funcName  = 2;
    bytes  stateHash = 3;
    bytes  param     = 4;
    bytes  extra     = 5;
}
message ReqTokens {
    bool     queryAll          = 1;
    int32    status            = 2;
    repeated string tokens     = 3;
    bool            symbolOnly = 4;
}
```

**参数说明：**

| 参数       | 类型     | 是否必须 | 说明                                                         |
| :--------- | :------- | :------- | :----------------------------------------------------------- |
| driver     | bytes    | 是       | 执行器名称, 这里固定为 token                                 |
| funcName   | string   | 是       | 操作名称, 这里固定为 GetTokens                               |
| stateHash  | bytes    | 否       | 所有交易在对应的执行器执行后写入KVDB中重新计算得到的新state的哈希值 |
| param      | bytes    | 是       | types.Encode(&ReqString)                                     |
| extra      | bytes    | 否       | 扩展字段，用于额外的用途                                     |
| status     | int32    | 是       | 查询状态                                                     |
| queryAll   | bool     | 是       | 是否查询所有                                                 |
| tokens     | []string | 否       | 查询标识                                                     |
| symbolOnly | bool     | 是       | 是否只返回symbol值 选填（true/false）                        |

**返回数据：**

```
message ReplyTokens {
    repeated LocalToken tokens = 1;
}
message LocalToken {
    string name                = 1;
    string symbol              = 2;
    string introduction        = 3;
    int64  total               = 4;
    int64  price               = 5;
    string owner               = 6;
    string creator             = 7;
    int32  status              = 8;
    int64  createdHeight       = 9;
    int64  createdTime         = 10;
    int64  prepareCreateHeight = 11;
    int64  prepareCreateTime   = 12;
    int32  precision           = 13;
    // 如果需要这个项可以单独做一个域存储
    int64 totalTransferTimes = 14;
    int64 revokedHeight      = 15;
    int64 revokedTime        = 16;
    int32 category           = 17;
}
```

**参数说明：**

| 参数   | 类型        | 说明   |
| :----- | :---------- | :----- |
| tokens | ReplyTokens | Tokens |

### 3.2 代码示例

假定 grpc服务地址：localhost:8802

```go
package main
import (
    "context"
    "encoding/json"
    "github.com/33cn/chain33/types"
    tokenTy "github.com/33cn/plugin/plugin/dapp/token/types"
    "google.golang.org/grpc"
    "testing"
)
func main() {
    con,err:= grpc.Dial("localhost:8802",grpc.WithInsecure())
    if err!=nil{
        panic(err)
    }
    gcli:= types.NewChain33Client(con)
    ctx:=context.Background()
    var queryIn types.ChainExecutor
    var req tokenTy.ReqTokens
    req.QueryAll=true
    req.SymbolOnly=true
    req.Status=0
    queryIn.Driver="token"
    queryIn.FuncName="GetTokens"
    queryIn.Param=types.Encode(&req)
    reply,err:= gcli.QueryChain(ctx,&queryIn)
    if err!=nil{
        t.Log(err)
        return
    }
    if reply.IsOk{
        var tokens tokenTy.ReplyTokens
        err= types.Decode(reply.GetMsg(),&tokens)
        if err!=nil{
            t.Log(err)
            return
        }
        jmb,_:=json.MarshalIndent(tokens,"","\t")
        t.Log("tokens",string(jmb))
        return
    }
    t.Log("msg:",string(reply.GetMsg()))
}
```

