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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

使用Oracle验证外部数据

發布時間:2023/12/3 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用Oracle验证外部数据 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我經常在Corda Slack頻道中閑逛,并盡可能回答問題。 我嘗試回答的合理數量的問題與Oracle有關。 更具體地說,何時使用一個。 我覺得我可以回答,“當您需要驗證可能經常更改的外部數據時使用Oracle”。 我可能在某個時候寫了一個類似的答案。 我沒辦法做的...告訴某人如何實施。 因此,要糾正這一點。 我寫這篇文章的目的是學習如何實現自己,并與您和我未來的自我分享這些知識。

何時使用Oracle

讓我們從擴展何時使用Oracle開始。 就像我剛才提到的那樣,當您需要驗證可能經常更改的外部數據時,應該使用Oracle。 這可能是諸如匯率,股票價格之類的數據,甚至是我的博客當前處于上升還是下降狀態(盡管我還沒有看到它下降過!)。 我認為經常性部分在這里很重要。 如果數據很少更改,則針對包含與Oracle自己檢索的相同類型的值的附件驗證某些數據可能是可行的。 因此,我認為,僅由Oracle才能驗證匯率等數據。 話雖如此,它實際上取決于您的特定用例。

如何使用Oracle

Oracle如何進行此驗證? 好吧,這取決于您。 但是,它可能會遵循以下步驟:

  • 從節點接收數據
  • 檢索外部數據
  • 根據外部數據驗證接收到的數據
  • 提供交易簽名

這些是我認為大多數Oracle實現將包含的步驟。 可以添加更多步驟,并且完成的驗證可以與用例需求一樣復雜或簡單。 盡管可以增加更多的步驟,但我真的懷疑,排除上面顯示的任何步驟的Oracle是否會有很多用途。

上面顯示的所有步驟僅從Oracle的角度顯示了該過程。 還有更多事情要做,所以我認為一個好的圖表可以幫助我們。 它還將繼續介紹我將在本文中使用的示例。


序列圖顯示了與Oracle交互的過程

這些步驟中有很多是通用步驟,無論您將其放在何處,都將執行這些步驟。 在本節中,我將擴展并顯示實現圖中所示流程所涉及的代碼。 因此值得一看。。。我也花了很多時間使它看起來不錯,所以請看一下!!

哦,在我繼續之前還有一點。 我想強調一下將時序圖組合起來對Corda Flows建模有多大幫助。 它確實突出顯示了涉及的人員,需要進行多少次網絡跳躍以及每個參與者進行了多少工作。 此外,它們是向只對您正在設計和/或實現的更高層次的流程感興趣的人解釋發生了什么的好方法。

客戶端/不是Oracle端

如前所述,這里的某些代碼是通用代碼,您很可能會將其放入編寫的任何Flow中。 我已經展示了所有內容,因此對于正在發生的事情沒有任何歧義,但是我將僅針對需要突出顯示的點進行擴展,因為它們包含特定于與Oracle交互的代碼。

@InitiatingFlow @StartableByRPC class GiveAwayStockFlow(private val symbol: String,private val amount: Long,private val recipient: String ) :FlowLogic<SignedTransaction>() {@Suspendableoverride fun call(): SignedTransaction {val recipientParty = party()val oracle = oracle()val transaction =collectRecipientSignature(verifyAndSign(transaction(recipientParty, oracle)),recipientParty)val allSignedTransaction = collectOracleSignature(transaction, oracle)return subFlow(FinalityFlow(allSignedTransaction))}private fun party(): Party =serviceHub.networkMapCache.getPeerByLegalName(CordaX500Name.parse(recipient))?: throw IllegalArgumentException("Party does not exist")private fun oracle(): Party = serviceHub.networkMapCache.getPeerByLegalName(CordaX500Name("Oracle","London","GB"))?: throw IllegalArgumentException("Oracle does not exist")@Suspendableprivate fun collectRecipientSignature(transaction: SignedTransaction,party: Party): SignedTransaction {val signature = subFlow(CollectSignatureFlow(transaction,initiateFlow(party),party.owningKey)).single()return transaction.withAdditionalSignature(signature)}private fun verifyAndSign(transaction: TransactionBuilder): SignedTransaction {transaction.verify(serviceHub)return serviceHub.signInitialTransaction(transaction)}private fun transaction(recipientParty: Party, oracle: Party): TransactionBuilder =TransactionBuilder(notary()).apply {val priceOfStock = priceOfStock()addOutputState(state(recipientParty, priceOfStock), StockContract.CONTRACT_ID)addCommand(GiveAway(symbol, priceOfStock),listOf(recipientParty, oracle).map(Party::owningKey))}private fun priceOfStock(): Double =serviceHub.cordaService(StockRetriever::class.java).getCurrent(symbol).priceprivate fun state(party: Party, priceOfStock: Double): StockGiftState =StockGiftState(symbol = symbol,amount = amount,price = priceOfStock * amount,recipient = party)private fun notary(): Party = serviceHub.networkMapCache.notaryIdentities.first()@Suspendableprivate fun collectOracleSignature(transaction: SignedTransaction,oracle: Party): SignedTransaction {val filtered = filteredTransaction(transaction, oracle)val signature = subFlow(CollectOracleStockPriceSignatureFlow(oracle, filtered))return transaction.withAdditionalSignature(signature)}private fun filteredTransaction(transaction: SignedTransaction,oracle: Party): FilteredTransaction =transaction.buildFilteredTransaction(Predicate {when (it) {is Command<*> -> oracle.owningKey in it.signers && it.value is GiveAwayelse -> false}}) }@InitiatedBy(GiveAwayStockFlow::class) class SendMessageResponder(val session: FlowSession) : FlowLogic<Unit>() {@Suspendableoverride fun call() {subFlow(object : SignTransactionFlow(session) {override fun checkTransaction(stx: SignedTransaction) {}})} }

首先,讓我們看一下如何構建事務:

private fun transaction(recipientParty: Party, oracle: Party): TransactionBuilder =TransactionBuilder(notary()).apply {val priceOfStock = priceOfStock()addOutputState(state(recipientParty, priceOfStock), StockContract.CONTRACT_ID)addCommand(GiveAway(symbol, priceOfStock),listOf(recipientParty, oracle).map(Party::owningKey))}private fun priceOfStock(): Double =serviceHub.cordaService(StockRetriever::class.java).getCurrent(symbol).price

這與我創建不涉及Oracle的事務的方式沒有太大不同。 僅有的兩個區別是,從外部來源(隱藏在StockRetriever服務內部)檢索股票價格,并在命令中包括Oracle的簽名。 這些代碼添加內容與使用Oracle的原因相吻合。 外部數據包含在事務中,并且Oracle需要驗證它是否正確。 為了證明甲骨文認為交易有效,我們需要其簽名。

我們將仔細研究分別獲取外部數據的方法。

接下來是收集收件人簽名:

@Suspendable private fun collectRecipientSignature(transaction: SignedTransaction,party: Party ): SignedTransaction {val signature = subFlow(CollectSignatureFlow(transaction,initiateFlow(party),party.owningKey)).single()return transaction.withAdditionalSignature(signature) }

收集交易對手的簽名并不是Flow真正不平凡的一步,但CollectSignatureFlow不同的是,這里使用CollectSignatureFlow而不是通常使用的CollectSignaturesFlow (注意中間缺少“ s”)。 這是由于在事務中需要Oracle的簽名。 調用CollectSignaturesFlow將會從所有必需的簽名者(包括Oracle)中檢索簽名。 這將Oracle視為“正常”參與者。 這不是我們想要的。 取而代之的是,我們需要單獨地或手動地獲得收件人和Oracle的簽名。 手動部分是使用transaction.withAdditionalSignature 。

現在,接收者已經簽署了交易,Oracle需要簽署它:

@Suspendable private fun collectOracleSignature(transaction: SignedTransaction,oracle: Party ): SignedTransaction {val filtered = filteredTransaction(transaction, oracle)val signature = subFlow(CollectOracleStockPriceSignatureFlow(oracle, filtered))return transaction.withAdditionalSignature(signature) }private fun filteredTransaction(transaction: SignedTransaction,oracle: Party ): FilteredTransaction =transaction.buildFilteredTransaction(Predicate {when (it) {is Command<*> -> oracle.owningKey in it.signers && it.value is GiveAwayelse -> false}})

在將事務發送給Oracle之前,建議對其進行過濾,以刪除Oracle不需要的任何信息。 這可以防止Oracle看到不應共享的信息。 請記住,Oracle可能是另一個組織控制的節點,而不是您試圖與其共享狀態和事務的參與者。

SignedTransaction提供了buildFilteredTransaction函數,該函數僅包含與傳入的謂詞匹配的對象。在上面的示例中,它過濾掉了GiveAway (我創建的命令)命令之外的所有命令,該命令還必須具有Oracle作為簽名者。

這將輸出一個FilteredTransaction ,并傳遞給CollectOracleStockPriceSignatureFlow :

@InitiatingFlow class CollectOracleStockPriceSignatureFlow(private val oracle: Party,private val filtered: FilteredTransaction ) : FlowLogic<TransactionSignature>() {@Suspendableoverride fun call(): TransactionSignature {val session = initiateFlow(oracle)return session.sendAndReceive<TransactionSignature>(filtered).unwrap { it }} }

這些代碼所做的全部工作就是將FilteredTransaction發送到Oracle并等待其簽名。 此處的代碼可以放入主流程中,但是在可以的情況下將代碼拆分出來是非常好的。

最后,從Oracle返回的TransactionSignature以與接收者簽名之前添加方式相同的方式添加到事務中。 此時,由于所有必需的簽名者都已做好了自己的準備,因此可以準備提交交易。

甲骨文方面

既然我們已經覆蓋了代碼的客戶端,我們就需要研究一下Oracle如何驗證事務。 以下是Oracle代碼的內容:

@InitiatedBy(CollectOracleStockPriceSignatureFlow::class) class OracleStockPriceSignatureResponder(private val session: FlowSession) : FlowLogic<Unit>() {@Suspendableoverride fun call() {val transaction = session.receive<FilteredTransaction>().unwrap { it }val key = key()val isValid = transaction.checkWithFun { element: Any ->when {element is Command<*> && element.value is GiveAway -> {val command = element.value as GiveAway(key in element.signers).also {validateStockPrice(command.symbol,command.price)}}else -> false}}if (isValid) {session.send(serviceHub.createSignature(transaction, key))} else {throw InvalidStockPriceFlowException("Transaction: ${transaction.id} is invalid")}}private fun key(): PublicKey = serviceHub.myInfo.legalIdentities.first().owningKeyprivate fun validateStockPrice(symbol: String, price: Double) = try {serviceHub.cordaService(StockPriceValidator::class.java).validate(symbol, price)} catch (e: IllegalArgumentException) {throw InvalidStockPriceFlowException(e.message)} }

StockPriceValidator隱藏了一些應在此處的代碼,該代碼檢索外部股票價格并將其與傳遞給Oracle的價格進行比較。 它沒有太多的代碼,并且其驗證是基本的,因此我將不對其進行詳細說明。 簡短地說,我現在不妨展示一下:

@CordaService class StockPriceValidator(private val serviceHub: AppServiceHub) :SingletonSerializeAsToken() {fun validate(symbol: String, price: Double) =serviceHub.cordaService(StockRetriever::class.java).getCurrent(symbol).let {require(price == it.price) { "The price of $symbol is ${it.price}, not $price" }} }

返回到OracleStockPriceSignatureResponder 。 首先,調用receive來獲取客戶端發送的FilteredTransaction 。 然后使用其checkWithFun函數進行檢查。 這是一個方便的函數,它查看每個對象并期望返回Boolean 。 使用此方法,如果事務中包含的所有內容都是GiveAway命令(Oracle是簽名者),并且最重要的是檢查該命令中包含的外部數據是否正確,則該事務被視為有效。 如果您回想起以前的代碼,則會傳入正確的命令和簽名者。唯一剩下的驗證是在外部數據上。 如果一切正常,那么Oracle將接受該事務并將其簽名發送回請求該請求的客戶端。

我選擇通過引發異常(連同錯誤消息)來完成驗證,然后將異常傳播到請求方。 我認為,這使您更容易了解出了什么問題,以便可以正確地進行處理,而不僅僅是直接的“失敗驗證”消息。 如果Oracle正在執行的驗證很復雜,則這些錯誤消息將變得更加有價值。

檢索外部數據

您應該已經看到StockRetriever類現在彈出兩次。 請求方和Oracle中都使用了它。 我已經在兩種類型的節點(普通節點和Oracle)之間共享了此代碼,但這可能不適用于您自己的用例。 此外,您如何選擇檢索外部數據取決于您,我只是提供一種可能的解決方案。

該代碼可以在下面找到:

@CordaService class StockRetriever(serviceHub: AppServiceHub) :SingletonSerializeAsToken() {private val client = OkHttpClient()private val mapper = ObjectMapper()fun getCurrent(symbol: String): Stock {val response = client.newCall(request(symbol)).execute()return response.body()?.let {val json = it.string()require(json != "Unknown symbol") { "Stock with symbol: $symbol does not exist" }val tree = mapper.readTree(json)Stock(symbol = symbol,name = tree["companyName"].asText(),primaryExchange = tree["primaryExchange"].asText(),price = tree["latestPrice"].asDouble())} ?: throw IllegalArgumentException("No response")}private fun request(symbol: String) =Request.Builder().url("https://api.iextrading.com/1.0/stock/$symbol/quote").build() }

StockRetriever是一個很好的小服務,它使用OkHttpClient ( OkHttp )向API(由IEX Trading使用其Java庫提供)發出HTTP請求,該API在提供股票代號時返回股票信息。 您可以使用任何想要發出HTTP請求的客戶端。 我在一個示例CorDapp中看到了這個,并且我自己考慮了它。 就我個人而言,我也已經習慣了Spring,所以除了RestTemplate之外,我真的不認識其他任何客戶。

返回響應后,它將轉換為Stock對象,并傳遞回函數的調用者。 那是所有人。

結論

總之,當您的CorDapp需要頻繁更改的外部數據且需要先進行驗證才能提交事務時,應使用Oracle。 就像狀態中保存的數據一樣,外部數據非常重要,可能是最重要的,因為它很可能確定事務的主要內容。 因此,所有參與者都必須對數據的正確性感到放心,而不僅僅是憑空提出。 為此,Oracle還將檢索外部數據,并根據事務表明數據應具有的內容對其進行驗證。 此時,Oracle將簽署交易或引發異常并將其視為無效。 由于不需要采取許多步驟,因此實現方面相當簡單。 檢索數據,將FilteredTransaction發送到包含將在其中進行驗證的數據的Oracle。 是的,閱讀本文后,您將了解更多內容。 但是,對于基本流程而言,差不多就可以了。 就像我剛開始說的那樣,Oracle如何進行驗證可以根據需要簡單或復雜。 雖然,我認為大多數將遵循此處所示的相同過程。

現在得出主要結論……總之,您現在已經掌握了在閑聊的渠道中回答有關Oracle的問題的知識,或者知道了在不可行的情況下將其發送到哪里!

這篇文章中使用的代碼可以在我的GitHub上找到 。

如果您認為這篇文章有幫助,可以在Twitter上@LankyDanDev關注我,以跟上我的新文章。

翻譯自: https://www.javacodegeeks.com/2019/01/validating-external-data-oracle.html

總結

以上是生活随笔為你收集整理的使用Oracle验证外部数据的全部內容,希望文章能夠幫你解決所遇到的問題。

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