http://www.7klian.com

撸一个预言机(Oracle)处事,真香!—中篇

    defer res.Body.Close()
    if resData == nil {
        res,
    query := ethereum.FilterQuery{
    }
        queryInfo.QueryId,
}
    }
这段时间以太坊网络较量拥堵,导致手续费居高不下。对付我们Oracle处事来说,节减Gas是很重要的一个优化偏向。
    CallbackAddr common.Address
    }
}
func (e *EventWatch) sendQueryRequest(reqData *QueryRequest, resParamType string) (interface{}, error) {
        return fmt.Errorf(“[SendQueryResponse] Transact failed: %v”, err)
    if err := e.subscribeEvent(); err != nil {
回调模块相比拟力简朴,首先将Oracle合约实例化了一个BoundContract工具,然后挪用Transact要领发送生意业务。个中第一个参数是利用私钥实例化的一个TransactOpts工具。
    case “bytes”:
        queryInfo := &OracleQueryInfo{}

处事开启后,首先会通过ws协议的jsonrpc,在上注册事件订阅,订阅乐成后开启一个for轮回,吸收并处理惩罚事件动静。
三、事件订阅
        stateCode,
    case “uint256”:
        return nil, fmt.Errorf(“[sendQueryRequest] NewRequest failed: %v”, err)

Oracle后端处事整体包括事件订阅模块、查询模块和回调模块,架构如下图所示。

事件订阅的焦点是通过ethclient的SubscribeFilterLogs要领,个中query参数是订阅的过滤条件。个中
五、回调模块
        return
// sendQueryResponse 将查询到的功效发送给客户端合约指定要领
        queryInfo.CallbackFUN,
    if err != nil {
        responseName = OracelResponseBytesName
func (e *EventWatch) dealQuery(vLog types.Log) error {
    }
    if err != nil {
        logs.Error(“[SubscribeEvent]fail to subscribe event:”, err)
    for _, paramName := range keys {
    var resValue interface{}
    default:
            logs.Error(“[dealEvent] Subscription err: “, err)
        return fmt.Errorf(“[SendQueryResponse] unsupport response data type”)
    events := make(chan types.Log)
至此,我们的V1版的Oracle处事已开拓完成,处事已能满意根基需求,但尚有一些方面需要进一步优化,我这里列出了三点。
        return nil, fmt.Errorf(“[ParseResponeData] unsupport response data type %s”, resParamType)
func ParseResponeData(repData []byte, keys []string, resParamType string) (interface{}, error) {
针对这种环境,我的思路是对Nonce举办托管:
查询大概失败,这里需要增加失败重试机制,代码较量简朴,就不写出来了。
            go e.dealQuery(vLog)
    resData, err := simplejson.NewJson(repData)
•引入动态GasPrice,可以从https://ethgasstation.info网站中获取及时的GasPrice•指定GasLimit,防备由于合约问题耗损过多Gas•余额查抄,防备由于余额不敷造成生意业务失败,挥霍了手续费•吸收回调数据的用户合约要领只管简朴,疏散业务逻辑
            common.HexToAddress(e.Config.OracleContractAddress),
    ResponseParams []string `json:”responseParams,omitempty”`
// ParseResponeData 理会链下获取到的数据,提取用户所需要的字段,并转换为对应的数据范例
        Topics: [][]common.Hash{
        return nil, fmt.Errorf(“[sendQueryRequest] read response data failed: %v”, err)
    if err != nil {
// start monitor oracle contract event
    for {
3、支持http协议jsonrpc

        select {
    logs.Trace(“[SendQueryResponse] call back tx:”, transaction.Hash().Hex())
本篇是中篇,主要利用go语言开拓实现Oracle的后端处事。
}
        },
        resData = resData.Get(paramName)
func (e *EventWatch) Start() {
代码如下所示。
        case err := <-e.Subscription.Err():
    }
    body, err := ioutil.ReadAll(res.Body)

一、文章布局
        case vLog := <-e.EventChan:
    e.Subscription = sub
    }
            resValue = big.NewInt(int64(resUint64Value))
// sendQueryRequest 按照客户端指定的查询地点发送请求
type OracleQueryInfo struct {
        return nil, fmt.Errorf(“[ParseResponeData] unmarshal response data failed:%v”, err)
    return nil
    Requester    common.Address
    }
有的网络节点没有开启ws处事,而利用http协议的网络jsonrpc又无法直接订阅事件。这时可以采纳迂回计策,模仿事件订阅,详细思路如下:
    switch resParamType {
这里我的思路是可以从以下几个方面优化:
}
•在缓存(内存或redis等)中维护账号对应的Nonce•每次提倡生意业务时,从缓存中获取,每获取一次,缓存中的Nonce累加1•缓存中的Nonce按期和链长举办校对和同步•对付大概呈现的空洞环境,利用空生意业务填补
        return nil, err
    }
文中的Oracle处事完整代码地点:https://github.com/six-days/ethereum-oracle-service
    }
func (e *EventWatch) dealEvent() {
    transaction, err := e.BoundContract.Transact(e.TransactOpts, responseName, in…)
    if err != nil {
        responseName = OracelResponseUint256Name
    }
        return fmt.Errorf(“[dealQuery] unmarshal query data failed:%v”, err)
}
事件订阅必需利用ws协议的jsonrpc,http协议的jsonprc无法订阅事件。
    case “uint256”:
    in := []interface{}{
Transact要领源码详见:https://github.com/six-days/go-ethereum/blob/master/accounts/abi/bind/base.go
        queryInfo.CallbackAddr,
    return resValue, nil
这里利用go-simplejson库将查询功效举办json理会,而且提取用户指定所需要的字段,将字段转换为用户合约中回调要领吸收的数据范例。
1、日志理会
        }
1、Nonce托管
References
    reqData := &QueryRequest{}
    }
        if coverErr == nil {
    e.dealEvent()
    Fee          *big.Int
各人对付优化有其他思路或疑问,接待留言探讨。
    }
}
    }
查询请求较量简朴,就是按照用户提供的url发送请求。代码如下所示。
在TransactOpts工具中可以设置nonce、gasLimit、gasPrice等值,假如不指定,Transact要了解本身增补上。除此之外,Transact要领也会挪用TransactOpts工具的Signer要领对动静举办签名。
2、Gas优化
事件日志理会我们用go-ethereum的abi模块的Unpack要领,将日志理会为我们界说好的布局体。
代码如下所示。
        return err
    switch resParamType {
    URL            string   `json:”url,omitempty”`
    if err != nil {
    queryRes, err := ParseResponeData(body, reqData.ResponseParams, resParamType)
        },
    }
•上篇:Oracle简介及合约实现[1]•中篇:利用go语言开拓Oracle处事•下篇:抽奖合约挪用Oracle处事示例
    }
    }
    Raw          types.Log // Blockchain specific contextual infos
    CallbackFUN  string
    }
    logs.Trace(“[sendQueryRequest] get “, reqData.URL, ” response is: “, string(body))
}
func (e *EventWatch) sendQueryResponse(res interface{}, stateCode uint64, queryInfo *OracleQueryInfo, resParamType string) error {
}
    if err = json.Unmarshal(queryInfo.QueryData, reqData); err != nil {
    case “bytes”:
        }
            {e.OracleABI.Events[OracelEventName].ID()},
        return nil, fmt.Errorf(“[ParseResponeData] response data type %s error:%v”, resParamType, err)
        return fmt.Errorf(“[dealQuery] unpack event log failed:%v”, err)
在高并发的环境下,必定会呈现多笔生意业务Nonce值沟通的环境,后提倡生意业务包围前生意业务,造成前生意业务失败。
        return nil, fmt.Errorf(“[sendQueryRequest] http get request failed: %v”, err)
本文将通过上、中、下三篇文章教育各人一步步开拓实现一其中心化的Oracle处事,并通过一个抽奖合约演示如何利用我们的Oracle处事。文章内容布置如下:
func (e *EventWatch) subscribeEvent() error {
    if coverErr != nil {
    res, err := http.DefaultClient.Do(req)
在回调模块中,挪用合约时,我们并没有指定提倡生意业务账号的Nonce值,而是由Transact要领在每次提倡生意业务时,动态计较。这就会限制我们生意业务的并发。
六、可以优化的处所
在上篇中,,我们实现了一个通用的Oracle合约,其主要有一个吸收用户请求的Query要领;回挪用户合约的Response要领和一个供Oracle后端处事订阅的QueryInfo事件。
•开启网络区块监控•监控到有新区块发生,查询区块中的日志•假如有我们Oracle合约发生的查询日志,则进入后续的查询和回调流程
•Addresses是Oracle合约地点;•Topics参数是过滤主题,是一个二维数组,这里我们的主题只指定了事件的名称。
    err := e.OracleABI.Unpack(queryInfo, OracelEventName, vLog.Data)
    var coverErr error
    default:
    return nil
    req, err := http.NewRequest(“GET”, reqData.URL, nil)
二、处事架构

        return nil, fmt.Errorf(“[ParseResponeData] response data not exist request key:%v”, keys)
        resUint64Value, coverErr := resData.Uint64()
    var responseName string
    e.EventChan = events
代码如下所示。
}
3、功效理会
2、查询请求
    sub, err := e.Client.SubscribeFilterLogs(context.Background(), query, events)
回调也大概失败,处事对sendQueryResponse要领的挪用也增加了失败重试机制。
type QueryRequest struct {
            e.subscribeEvent()
四、查询模块
    }
    if err != nil {
        resValue, coverErr = resData.Bytes()
[1] Oracle简介及合约实现: https://learnblockchain.cn/article/1150

回调模块代码如下所示。
    QueryData    []byte
下篇中,我将以一个抽奖合约为示例,先容如何利用我们开拓的Oracle处事来对抽奖合约提供一个随机数。
        Addresses: []common.Address{
    QueryId      [32]byte
    return queryRes, nil
    if err != nil {
    if err != nil {
            // 处理惩罚查询请求并回调

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

相关文章阅读