日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

jpa query oracle 参数int为空_撸一个预言机(Oracle)服务,真香!—中篇

發布時間:2024/10/8 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jpa query oracle 参数int为空_撸一个预言机(Oracle)服务,真香!—中篇 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文作者:六天

一、文章結構

本文將通過上、中、下三篇文章帶領大家一步步開發實現一個中心化的 Oracle 服務,并通過一個抽獎合約演示如何使用我們的 Oracle 服務。文章內容安排如下:

  • 上篇:Oracle 簡介及合約實現
  • 中篇:使用 go 語言開發 Oracle 服務
  • 下篇:抽獎合約調用 Oracle 服務示例

在上篇中,我們實現了一個通用的 Oracle 合約,其主要有一個接收用戶請求的 Query 方法;回調用戶合約的 Response 方法和一個供 Oracle 后端服務訂閱的 QueryInfo 事件。

本篇是中篇,主要使用 go 語言開發實現 Oracle 的后端服務。

本文作者六天,文中的 Oracle 服務完整代碼地址:https://github.com/six-days/ethereum-oracle-service

二、服務架構

Oracle 后端服務整體包含事件訂閱模塊、查詢模塊和回調模塊,架構如下圖所示。

服務開啟后,首先會通過以太坊 ws 協議的 jsonrpc,在區塊鏈上注冊事件訂閱,訂閱成功后開啟一個 for 循環,接收并處理事件消息。

代碼如下所示。

// start monitor oracle contract event func (e *EventWatch) Start() {if err := e.subscribeEvent(); err != nil {return}e.dealEvent() }func (e *EventWatch) dealEvent() {for {select {case err := <-e.Subscription.Err():logs.Error("[dealEvent] Subscription err: ", err)e.subscribeEvent()case vLog := <-e.EventChan:// 處理查詢請求并回調go e.dealQuery(vLog)}} }

三、事件訂閱

事件訂閱必須使用 ws 協議的 jsonrpc,http 協議的 jsonprc 無法訂閱事件。

事件訂閱的核心是通過 ethclient 的 SubscribeFilterLogs 方法,其中 query 參數是訂閱的過濾條件。其中

  • Addresses 是 Oracle 合約地址;
  • Topics 參數是過濾主題,是一個二維數組,這里我們的主題只指定了事件的名稱。

代碼如下所示。

func (e *EventWatch) subscribeEvent() error {query := ethereum.FilterQuery{Addresses: []common.Address{common.HexToAddress(e.Config.OracleContractAddress),},Topics: [][]common.Hash{{e.OracleABI.Events[OracelEventName].ID()},},}events := make(chan types.Log)sub, err := e.Client.SubscribeFilterLogs(context.Background(), query, events)if err != nil {logs.Error("[SubscribeEvent]fail to subscribe event:", err)return err}e.EventChan = eventse.Subscription = subreturn nil }

四、查詢模塊

1、日志解析

事件日志解析我們用 go-ethereum 的 abi 模塊的 Unpack 方法,將日志解析為我們定義好的結構體。

代碼如下所示。

type OracleQueryInfo struct {QueryId [32]byteRequester common.AddressFee *big.IntCallbackAddr common.AddressCallbackFUN stringQueryData []byteRaw types.Log // Blockchain specific contextual infos }type QueryRequest struct {URL string `json:"url,omitempty"`ResponseParams []string `json:"responseParams,omitempty"` }func (e *EventWatch) dealQuery(vLog types.Log) error {queryInfo := &OracleQueryInfo{}err := e.OracleABI.Unpack(queryInfo, OracelEventName, vLog.Data)if err != nil {return fmt.Errorf("[dealQuery] unpack event log failed:%v", err)}reqData := &QueryRequest{}if err = json.Unmarshal(queryInfo.QueryData, reqData); err != nil {return fmt.Errorf("[dealQuery] unmarshal query data failed:%v", err)} }

2、查詢請求

查詢請求比較簡單,就是根據用戶提供的 url 發送請求。代碼如下所示。

// sendQueryRequest 根據客戶端指定的查詢地址發送請求 func (e *EventWatch) sendQueryRequest(reqData *QueryRequest, resParamType string) (interface{}, error) {req, err := http.NewRequest("GET", reqData.URL, nil)if err != nil {return nil, fmt.Errorf("[sendQueryRequest] NewRequest failed: %v", err)}res, err := http.DefaultClient.Do(req)if err != nil {return nil, fmt.Errorf("[sendQueryRequest] http get request failed: %v", err)}defer res.Body.Close()body, err := ioutil.ReadAll(res.Body)if err != nil {return nil, fmt.Errorf("[sendQueryRequest] read response data failed: %v", err)}logs.Trace("[sendQueryRequest] get ", reqData.URL, " response is: ", string(body))queryRes, err := ParseResponeData(body, reqData.ResponseParams, resParamType)if err != nil {return nil, err}return queryRes, nil }

查詢可能失敗,這里需要增加失敗重試機制,代碼比較簡單,就不寫出來了。

3、結果解析

這里使用 go-simplejson 庫將查詢結果進行 JSON 解析,并且提取用戶指定所需要的字段,將字段轉換為用戶合約中回調方法接收的數據類型。

// ParseResponeData 解析鏈下獲取到的數據,提取用戶所需要的字段,并轉換為對應的數據類型 func ParseResponeData(repData []byte, keys []string, resParamType string) (interface{}, error) {resData, err := simplejson.NewJson(repData)if err != nil {return nil, fmt.Errorf("[ParseResponeData] unmarshal response data failed:%v", err)}for _, paramName := range keys {resData = resData.Get(paramName)}if resData == nil {return nil, fmt.Errorf("[ParseResponeData] response data not exist request key:%v", keys)}var resValue interface{}var coverErr errorswitch resParamType {case "uint256":resUint64Value, coverErr := resData.Uint64()if coverErr == nil {resValue = big.NewInt(int64(resUint64Value))}case "bytes":resValue, coverErr = resData.Bytes()default:return nil, fmt.Errorf("[ParseResponeData] unsupport response data type %s", resParamType)}if coverErr != nil {return nil, fmt.Errorf("[ParseResponeData] response data type %s error:%v", resParamType, err)}return resValue, nil }

五、回調模塊

回調模塊相對比較簡單,首先將 Oracle 合約實例化了一個 BoundContract 對象,然后調用 Transact 方法發送交易。其中第一個參數是使用私鑰實例化的一個 TransactOpts 對象。

在 TransactOpts 對象中可以配置 nonce、gasLimit、gasPrice 等值,如果不指定,Transact 方法會自己補充上。除此之外,Transact 方法也會調用 TransactOpts 對象的 Signer 方法對消息進行簽名。

Transact 方法源碼詳見:https://github.com/six-days/go-ethereum/blob/master/accounts/abi/bind/base.go

回調模塊代碼如下所示。

// sendQueryResponse 將查詢到的結果發送給客戶端合約指定方法 func (e *EventWatch) sendQueryResponse(res interface{}, stateCode uint64, queryInfo *OracleQueryInfo, resParamType string) error {in := []interface{}{queryInfo.QueryId,queryInfo.CallbackAddr,queryInfo.CallbackFUN,stateCode,res,}var responseName stringswitch resParamType {case "bytes":responseName = OracelResponseBytesNamecase "uint256":responseName = OracelResponseUint256Namedefault:return fmt.Errorf("[SendQueryResponse] unsupport response data type")}transaction, err := e.BoundContract.Transact(e.TransactOpts, responseName, in...)if err != nil {return fmt.Errorf("[SendQueryResponse] Transact failed: %v", err)}logs.Trace("[SendQueryResponse] call back tx:", transaction.Hash().Hex())return nil }

回調也可能失敗,服務對 sendQueryResponse 方法的調用也增加了失敗重試機制。

六、編譯與運行

1、編譯

go build

編譯完成后查看幫助信息

./oracle-service -h oracle_service version: 1.0.0 Usage: oracle_service [-h help] [-v version] [-c config path] [-l log path]

2、配置

配置信息如下:

# 合約地址 OracleContractAddress = "" # 網絡ws地址 NetworkWS = "ws://" # 調用合約的私鑰 PrivateKey = ""

3、運行

./oracle-service -c ./conf/app.conf -l logs/

七、可以優化的地方

至此,我們的 V1 版的 Oracle 服務已開發完成,服務已能滿足基本需求,但還有一些方面需要進一步優化,我這里列出了三點。

1、Nonce 托管

在回調模塊中,調用合約時,我們并沒有指定發起交易賬號的 Nonce 值,而是由 Transact 方法在每次發起交易時,動態計算。這就會限制我們交易的并發。

在高并發的情況下,肯定會出現多筆交易 Nonce 值相同的情況,后發起交易覆蓋前交易,造成前交易失敗。

針對這種情況,我的思路是對 Nonce 進行托管:

  • 在緩存(內存或 Redis 等)中維護賬號對應的 Nonce
  • 每次發起交易時,從緩存中獲取,每獲取一次,緩存中的 Nonce 累加 1
  • 緩存中的 Nonce 定期和鏈上進行校對和同步
  • 對于可能出現的空洞情況,使用空交易填補

2、Gas 優化

這段時間以太坊網絡比較擁堵,導致手續費居高不下。對于我們 Oracle 服務來說,節省 Gas 是很重要的一個優化方向。

這里我的思路是可以從以下幾個方面優化:

  • 引入動態 GasPrice,可以從 https://ethgasstation.info 網站中獲取實時的 GasPrice
  • 指定 GasLimit,防止由于合約問題消耗過多 Gas
  • 余額檢查,防止由于余額不足造成交易失敗,浪費了手續費
  • 接收回調數據的用戶合約方法盡量簡單,分離業務邏輯

3、支持 http 協議 jsonrpc

有的網絡節點沒有開啟 ws 服務,而使用 http 協議的網絡 jsonrpc 又無法直接訂閱事件。這時可以采取迂回策略,模擬事件訂閱,具體思路如下:

  • 開啟網絡區塊監控
  • 監控到有新區塊產生,查詢區塊中的日志
  • 如果有我們 Oracle 合約產生的查詢日志,則進入后續的查詢和回調流程

大家對于優化有其他思路或疑問,歡迎留言探討。

下篇中,我將以一個抽獎合約為示例,介紹如何使用我們開發的 Oracle 服務來對抽獎合約提供一個隨機數。


原文鏈接: 擼一個預言機(Oracle)服務,真香!—中篇 | 登鏈社區 | 深入淺出區塊鏈技術

作者主頁: 六天 的個人主頁 - 登鏈社區 | 深入淺出區塊鏈技術,歡迎閱讀作者更多好文

登鏈社區 - 區塊鏈技術愛好者的家園

總結

以上是生活随笔為你收集整理的jpa query oracle 参数int为空_撸一个预言机(Oracle)服务,真香!—中篇的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。