从官方童靴得知,后期会加上历史查询功能,目前如有需要请自行实现
直接从链上查询某个Key的历史功能还是非常有必要的,对于溯源类应用还是很方便的。数据存储解析
总体需要改三个地方,go sdk ,protocol,chainmaker-go
生成protocol
添加grpc 数据位于chainmaker-sdk-go/pb/proto/store/query_result.proto
message KVHistory {
string contract_name = 1;
string key = 2;
string value = 3;
string tx_id = 4;
}
// 返回数据
message KVHistoryArray {
repeated KVHistory kv_arrays = 1;
}
// 参数
message KVHistortParams {
string chain_id = 1;
string key = 2;
string contract_name = 3;
}
添加grpc方法位于chainmaker-sdk-go/pb/proto/api/rpc_node.proto
...
import "store/query_result.proto";
service RpcNode {
// 这个是新增方法
rpc QueryHistory(store.KVHistortParams) returns (store.KVHistoryArray) {};
...
}
生成go文件
cd chainmaker-sdk-go/pb/proto
protoc --gogo_out=plugins=grpc:./ ./api/rpc_node.proto
protoc --gogo_out=plugins=grpc:./ ./store/query_result.proto
将生成的go文件rpc_node.pb.go
和query_result.pb.go
分别替换掉go sdk
和
chainmaker-go
里对应的文件,chainmaker-go
需要替换2个地方分别是tools/sdk/pb/
和/pb/
注(go-sdk 在替换时需要修改一下rpc_node.pb.go的包名)
package api
import (
common "chainmaker.org/chainmaker-sdk-go/pb/protogo/common"
config "chainmaker.org/chainmaker-sdk-go/pb/protogo/config"
store "chainmaker.org/chainmaker-sdk-go/pb/protogo/store"
...
)
...
此时 go sdk和chainmaker-go 具有相同的protocol了
修改chainmaker-sdk-go
添加方法在chainmaker-sdk-go/sdk_client.go
里
func (cc *ChainClient) SendQueryHistoryRequest(ContractName,Key,ChainId string,timeout int64) (*store.KVHistoryArray, error) {
kvHistortParams:= &store.KVHistortParams{}
kvHistortParams.ContractName=ContractName
kvHistortParams.Key=Key
kvHistortParams.ChainId=ChainId
if timeout < 0 {
timeout = GetTxTimeout
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
defer cancel()
ignoreAddrs := make(map[string]struct{})
for {
client, err := cc.pool.getClientWithIgnoreAddrs(ignoreAddrs)
if err != nil {
return nil, err
}
if len(ignoreAddrs) > 0 {
cc.logger.Debugf("[SDK] begin try to connect node [%s]", client.ID)
}
// 查询历史数据
resp, err := client.rpcNode.QueryHistory(ctx, kvHistortParams)
if err!=nil{
return nil,err
}
cc.logger.Debugf("[SDK] sendQueryHistoryRequest resp: %+v", resp)
return resp, nil
}
}
自己封装了一下
// 查询历史数据
func (mg *Manager) QueryHistory(ContractName,Key,ChainId string) (*store.KVHistoryArray, error) {
resp, err := mg.client.SendQueryHistoryRequest(ContractName,Key,ChainId , -1)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("[%s]合约[%s]链查询失败", ContractName, ChainId))
}
fmt.Println(resp)
return resp, nil
}
调用方法
_agree_manager.QueryHistory(contract_name,"fact_json#name","c1628498288425")
注意 这里的key 如果是组合的需要用#连接
我合约里的key是这么定义的if r:=ctx.PutState("fact_json","name", jsonStr);r==1{
修改go-sdk
在服务端修改位置如下module/rpcserver/api_service.go
// 查询历史数据
func (s *ApiService) QueryHistory(ctx context.Context, req *storePb.KVHistortParams) (*storePb.KVHistoryArray, error) {
kvHistoryArray:=&storePb.KVHistoryArray{}
_arrays:=make([]*storePb.KVHistory,0)
// 获取数据库
if store, err := s.chainMakerServer.GetStore(req.ChainId); err != nil {
s.log.Error(err)
return nil,err
}else {
// 查询数据
if datas,err:= store.GetHistoryForKey(req.ContractName,[]byte(req.Key));err!=nil{
s.log.Error(err)
return nil,err
}else {
// 迭代器 循环读取
for datas.Next(){
history_value,err:=datas.Value()
if err!=nil{
s.log.Error(err)
return nil,err
}
kv:=&storePb.KVHistory{}
kv.Key=req.Key
kv.ContractName=req.ContractName
kv.TxId=history_value.TxId
kv.Value=string(history_value.Value)
// history_value还有isdelete 和时间戳
_arrays=append(_arrays,kv)
}
}
}
kvHistoryArray.KvArrays=_arrays
return kvHistoryArray, nil
}
这样就针对某个Key的历史交易记录查询就基本完成了
服务端图如下
仅供参考