-
Notifications
You must be signed in to change notification settings - Fork 256
GRPC 交易接口
[TOC]
要发送一个交易,均需要经过三个步骤:构造交易->交易签名->发送交易;
**构造交易:**填写交易的关键信息,从而构造一条完整的交易数据; **交易签名:**对交易数据进行签名,即标识交易的所有者身份,也防止交易数据被篡改; **发送交易:**将交易数据发送到区块链上去执行;
下面分别介绍三个接口
调用接口
rpc CreateRawTransaction(CreateTx) returns (UnsignTx) {}
参数:
- ctx context.Context 类型,默认可以用context.Background(), 其他用法可以参考context 包内容
- CreateTx 结构体参数展示
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 | 交易对象的十六进制字符串编码 |
调用接口
rpc SignRawTx(ReqSignRawTx) returns (ReplySignRawTx) {}
参数:
message ReqSignRawTx {
string addr = 1;
string privkey = 2;
string txHex = 3;
string expire = 4;
int32 index = 5;
// 签名的模式类型
// 0:普通交易
// 1:隐私交易
// int32 mode = 6;
string token = 7;
int64 fee = 8;
// bytes newExecer = 9;
string newToAddr = 10;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
addr | string | 否 | addr与privkey可以只输入其一,如果使用addr则依赖钱包中存储的私钥签名 |
privkey | string | 否 | addr与privkey可以只输入其一,如果使用privkey则直接签名 |
txHex | string | 是 | 上一步生成的原始交易数据 |
expire | string | 是 | 过期时间可输入如"300s","-1.5h"或者"2h45m"的字符串,有效时间单位为"ns", "us" (or "µs"), "ms", "s", "m", "h" |
index | int32 | 否 | 若是签名交易组,则为要签名的交易序号,从1开始,小于等于0则为签名组内全部交易 |
token | string | 否 | token 名称 |
fee | int64 | 是 | 费用 |
newToAddr | string | 否 | - |
返回数据:
message ReplySignRawTx {
string txHex = 1;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
txHex | string | 交易签名后的十六进制字符串 |
调用接口
rpc SendTransaction(Transaction) returns (Reply) {}
参数:
message Transaction {
bytes execer = 1;
bytes payload = 2;
Signature signature = 3;
int64 fee = 4;
int64 expire = 5;
//随机ID,可以防止payload 相同的时候,交易重复
int64 nonce = 6;
//对方地址,如果没有对方地址,可以为空
string to = 7;
int32 groupCount = 8;
bytes header = 9;
bytes next = 10;
int32 chainID = 11;
}
message Signature {
int32 ty = 1;
bytes pubkey = 2;
bytes signature = 3;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
execer | bytes | 是 | 资产的执行器名称,如果是普通转账,此处应填coins,如果是构造平行链的基础代币,此处要填写user.p.xxx.coins token同上 |
payload | bytes | 是 | 参数 |
signature | Signature | 是 | 签名 |
signature.ty | int32 | 是 | 签名类型,1 -> secp256k1; 2 -> ed25519; 3 -> sm2; 4 -> OnetimeED25519; -> RingBaseonED25519 |
signature.pubkey | bytes | 是 | 公钥 |
signature.signature | bytes | 是 | 签名 //当ty为5时,格式应该用RingSignature去解析 |
fee | int64 | 是 | 手续费,注意基础货币单位为10^8,默认10^5,即Fee=1e5 |
expire | int64 | 否 | 超时时间 |
nonce | int64 | 否 | 随机ID |
to | string | 是 | 发送到地址;如果是合约的充提则为合约地址,合约地址为execName转换而来,详情查看ConvertExectoAddr接口 |
groupCount | int32 | 否 | 交易组 |
header | bytes | 否 | 区块头 |
next | bytes | 否 | 下一个交易的哈希 |
chainID | bool | 是 | chain ID |
返回数据:
message Reply {
bool isOk = 1;
bytes msg = 2;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
isOk | bool | 执行是否成功,true 成功 |
msg | bytes | 交易信息 |
go 代码示例: 假定 grpc服务地址:localhost:8802
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: "16hXXX", Execer: "coins"})
if err != nil {
panic(err)
}
signedTx, err := gclient.SignRawTx(context.Background(), &chan33Ty.ReqSignRawTx{Privkey: "xxx", TxHex: common.ToHex(txData.Data)})
if err != nil {
panic(err)
}
txBytes, err := common.FromHex(signedTx.TxHex)
if err != nil {
panic(err)
}
var tx chan33Ty.Transaction
chan33Ty.Decode(txBytes, &tx)
reply, err := gclient.SendTransaction(context.Background(), &tx)
if err != nil {
panic(err)
}
t.Log(reply.IsOk, common.ToHex(reply.Msg))
}
在平行链的使用场景中,可以发送不收手续费的交易,具体步骤如下: 构造交易 -> 平行链交易包装 -> 交易签名 -> 发送交易 后面的交易签名步骤里需要注意一点,参数index需填2
它和普通交易相比,多了一个 平行链交易包装 的步骤,对应接口如下
调用接口
rpc CreateNoBalanceTransaction(NoBalanceTx) returns (ReplySignRawTx) {}
参数:
message NoBalanceTx {
string txHex = 1;
string payAddr = 2;
string privkey = 3;
string expire = 4;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
txHex | string | 是 | 未签名的原始交易数据 |
payAddr | string | 是 | 用于付费的地址,这个地址要在主链上存在,并且里面有比特元用于支付手续费, payAddr与privkey可以只输入其一,如果使用payAddr则依赖钱包中存储的私钥签名 |
privkey | string | 否 | 对应于payAddr的私钥。如果payAddr已经导入到平行链,可以只传地址 |
expire | string | 否 | 过期时间可输入如"300s","-1.5h"或者"2h45m"的字符串,有效时间单位为"ns", "us" (or "µs"), "ms", "s", "m", "h", 不传递默认设置永不过期 |
返回数据:
message ReplySignRawTx {
string txHex = 1;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
txHex | string | 未签名的原始交易数据 |
同1.1.4 一样,可以创建发送不需要手续费的交易,不同的是,1.1.4接口只能构造单笔免手续费交易,而本接口可以创建多笔免手续费交易
调用接口
rpc CreateNoBalanceTxs(NoBalanceTxs) returns (ReplySignRawTx) {}
参数:
message NoBalanceTxs {
repeated string txHexs = 1;
string payAddr = 2;
string privkey = 3;
string expire = 4;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
txHexs | []string | 是 | 未签名的原始交易数据 |
payAddr | string | 是 | 用于付费的地址,这个地址要在主链上存在,并且里面有比特元用于支付手续费, payAddr与privkey可以只输入其一,如果使用payAddr则依赖钱包中存储的私钥签名 |
privkey | string | 否 | 对应于payAddr的私钥。如果payAddr已经导入到平行链,可以只传地址 |
expire | string | 否 | 过期时间可输入如"300s","-1.5h"或者"2h45m"的字符串,有效时间单位为"ns", "us" (or "µs"), "ms", "s", "m", "h", 不传递默认设置永不过期 |
返回数据:
message ReplySignRawTx {
string txHex = 1;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
txHex | string | 未签名的原始交易数据 |
支持对原始交易或交易组参数重写
调用接口
rpc ReWriteTx(ReWriteRawTx)returns(UnsignTx){}
参数:
message ReWriteRawTx {
string tx = 1;
// bytes execer = 2;
string to = 3;
string expire = 4;
int64 fee = 5;
int32 index = 6;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
to | string | 否 | 重写交易的目的地址,只有单笔交易生效,交易组不生效 |
fee | int64 | 否 | 重写交易的费用,交易组只会修改第一笔交易的费用 |
tx | string | 是 | 需要重写的原始交易数据 |
expire | string | 否 | 过期时间可输入如"300ms",”-1.5h”或者”2h45m”的字符串,有效时间单位为”ns”, “us” (or “µs”), “ms”, “s”, “m”, “h” |
index | int32 | 否 | 若是交易组,则为要重写的交易序号,从1开始,小于等于0则为交易组内全部交易 |
返回数据:
message UnsignTx {
bytes data = 1;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
data | bytes | 重写之后交易的十六进制字符串 |
code | output | description |
---|---|---|
txExistErr | transaction exists | 该交易已存在mempool中 |
lowFeeErr | low transaction fee | 交易费过低 |
manyTxErr | you have too many transactions | 同一账户在mempool中有超过10笔交易 |
signErr | wrong signature | 签名错误 |
lowBalanceErr | low balance | 余额不足 |
bigMsgErr | message too big | 消息过大 |
expireErr | message expired | 消息过期 |
loadAccountsErr | loadacconts error | 匹配账户错误 |
emptyTxErr | empty transaction | 交易为空 |
dupTxErr | duplicated transaction | 重复交易 |
memNotReadyErr | mempool not ready | mempool未启动 |
memFullErr | mempool is full | mempool已满 |
调用接口
rpc QueryTransaction(ReqHash) returns (TransactionDetail) {}
参数:
message ReqHash {
bytes hash = 1;
bool upgrade = 2;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
hash | bytes | 是 | 交易哈希 |
upgrade | bool | 否 | 是否升级 |
返回数据:
message TransactionDetail {
Transaction tx = 1;
ReceiptData receipt = 2;
repeated bytes proofs = 3;
int64 height = 4;
int64 index = 5;
int64 blocktime = 6;
int64 amount = 7;
string fromaddr = 8;
string actionName = 9;
repeated Asset assets = 10;
repeated TxProof txProofs = 11;
bytes fullHash = 12;
}
message ReceiptData {
int32 ty = 1;
repeated ReceiptLog logs = 3;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
tx | Transaction | 交易基本信息 |
receipt | ReceiptData | 交易执行结果信息 |
actionName | string | 操作名称,不同的执行器可能会有不同的值,如coins(transfer,withdraw,genesis),ticket(genesis,open,close,miner) |
receipt.Ty | int32 | receipt.ty == 1 表示执行失败;receipt.ty == 2 表示执行成功 |
调用接口
rpc GetTransactionByAddr(ReqAddr) returns (ReplyTxInfos) {}
参数:
message ReqAddr {
string addr = 1;
//表示取所有/from/to/其他的hash列表
int32 flag = 2;
int32 count = 3;
int32 direction = 4;
// height start
int64 height = 5;
int64 index = 6;
int64 heightEnd = 7;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
addr | string | 是 | 要查询的账户地址 |
count | int32 | 是 | 返回的数据条数 |
direction | int32 | 是 | 查询的方向;0:正向查询,区块高度从低到高;-1:反向查询; |
flag | int32 | 否 | 交易类型;0:所有涉及到addr的交易; 1:addr作为发送方; 2:addr作为接收方; |
height | int64 | 否 | 交易所在的block高度,-1:表示从最新的开始向后取;大于等于0的值,从具体的高度+具体index开始取 |
index | int64 | 否 | 交易所在block中的索引,取值0--100000 |
返回数据:
message ReplyTxInfos {
repeated ReplyTxInfo txInfos = 1;
}
message ReplyTxInfo {
bytes hash = 1;
int64 height = 2;
int64 index = 3;
repeated Asset assets = 4;
}
message Asset {
string exec = 1;
string symbol = 2;
int64 amount = 3;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
txInfos | []ReplyTxInfo | 交易数组;包含交易的哈希、高度、以及资产信息; |
txInfos.hash | bytes | 交易 id,可以通过接口 QueryTransaction 获取具体的交易信息 |
txInfos.assets | []Asset | 资产信息, 列出交易相关的资产。 可能整个数组 为 null |
调用接口
rpc GetTransactionByHashes(ReqHashes) returns (TransactionDetails) {}
参数:
message ReqHashes {
repeated bytes hashes = 1;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
hashes | []bytes | 是 | 交易ID列表 |
返回数据:
message TransactionDetails {
repeated TransactionDetail txs = 1;
}
message TransactionDetail {
Transaction tx = 1;
ReceiptData receipt = 2;
repeated bytes proofs = 3;
int64 height = 4;
int64 index = 5;
int64 blocktime = 6;
int64 amount = 7;
string fromaddr = 8;
string actionName = 9;
repeated Asset assets = 10;
repeated TxProof txProofs = 11;
bytes fullHash = 12;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
txs | []TransactionDetail | 单个交易详情信息,请参考 QueryTransaction接口 |
调用接口
rpc GetHexTxByHash(ReqHash) returns (HexTx) {}
参数:
message ReqHash {
bytes hash = 1;
bool upgrade = 2;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
hash | bytes | 是 | 交易哈希 |
返回数据:
message HexTx {
string tx = 1;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
tx | string | 交易对象的十六进制编码数据 |
调用接口
rpc GetAddrOverview(ReqAddr) returns (AddrOverview) {}
参数:
message ReqAddr {
string addr = 1;
//表示取所有/from/to/其他的hash列表
int32 flag = 2;
int32 count = 3;
int32 direction = 4;
int64 height = 5;
int64 index = 6;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
addr | string | 是 | 要查询的账户地址 |
count | int32 | 是 | 返回的数据条数 |
direction | int32 | 是 | 查询的方向;0:正向查询,区块高度从低到高;-1:反向查询; |
flag | int32 | 否 | 交易类型;0:所有涉及到addr的交易; 1:addr作为发送方; 2:addr作为接收方; |
height | int64 | 否 | 交易所在的block高度,-1:表示从最新的开始向后取;大于等于0的值,从具体的高度+具体index开始取 |
index | int64 | 否 | 交易所在block中的索引,取值0--100000 |
注意:本接口目前只是用到了addr参数,其它参数均无意义;
返回数据:
message AddrOverview {
int64 reciver = 1;
int64 balance = 2;
int64 txCount = 3;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
reciver | int64 | 一共接收的金额 |
txCount | int64 | 交易量计数 |
balance | int64 | 当前余额 |
调用接口
rpc ConvertExectoAddr(ReqString) returns (ReplyString) {}
参数:
message ReqString {
string data = 1;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
data | string | 是 | 执行器名称,如果需要往执行器中转币这样的操作,需要调用些接口将执行器名转成实际地址 |
返回数据:
message ReplyString {
string data = 1;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
data | string | 转换生成的地址字符串 |
调用接口
rpc CreateRawTxGroup(CreateTransactionGroup) returns (UnsignTx) {}
参数:
message CreateTransactionGroup {
repeated string txs = 1;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
txs | []string | 是 | 十六进制格式交易数组 |
返回数据:
message UnsignTx {
bytes data = 1;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
data | bytes | 交易组对象的十六进制字符串 |
调用接口
rpc GetProperFee(ReqProperFee) returns (ReplyProperFee) {}
参数:
message ReqProperFee {
int32 txCount = 1;
int32 txSize = 2;
}
参数说明:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
txCount | int32 | 否 | 预发送的交易个数,单个交易发送默认空即可 |
txSize | int32 | 否 | 预发送交易的大小, 单位Byte, 字节 |
- 通常采用默认传递即可
- 对于阶梯交易费模式,在同时发送多笔交易时, 可以指定将要发送的个数和总大小(字节)进行前瞻性估计
返回数据:
message ReplyProperFee {
int64 properFee = 1;
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
properFee | int64 | 每KB交易大小所需交易费, 单位1/108的BTY |
设置说明:
- 手动设置 上述接口返回的是单元大小的交易费率, 即每KB所需的交易费, 同时交易需要在签名前设置交易费,需要预估签名数据所占用大小,普通交易公私钥签名可预设为300字节
//参考代码
txFee := int64((txSizeKB+300)/1000+1) * properFee
-
自动设置 bityuan 6.2.1+版本, 以下接口支持自动设置合适交易费
- Chain33.SendToAddress
- Chain33.SignRawTx(交易费参数保留未设置或为0)
-
失败处理 在实际应用中, 仍然可能出现交易费过低导致失败情况, 建议代码中增加出错处理, 如失败时将交易费倍数递增(需要重新签名且注意设置上限,通常是0.1), 或者等待一段时间继续尝试 相关错误码:
"ErrTxFeeTooLow" 交易手续费太低
hello world