网站建设用的服务器,大地资源在线视频资源,网站建设 邯郸网站制作,传统电商平台有哪些oracle连接外部数据库我经常在Corda Slack频道中闲逛#xff0c;并尽可能回答问题。 我尝试回答的合理数量的问题与Oracle有关。 更具体地说#xff0c;何时使用。 我觉得我可以回答#xff0c;“当您需要验证可能经常更改的外部数据时使用Oracle”。 我大概在某个时候写了一… 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
) :FlowLogicSignedTransaction() {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) : FlowLogicUnit() {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服务内部检索股票价格并在Command中包括Oracle的签名。 这些代码添加与使用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
) : FlowLogicTransactionSignature() {Suspendableoverride fun call(): TransactionSignature {val session initiateFlow(oracle)return session.sendAndReceiveTransactionSignature(filtered).unwrap { it }}
} 这些代码所做的全部工作就是将FilteredTransaction发送到Oracle并等待其签名。 此处的代码可以放入主流程中但是在可以的情况下将代码拆分出来是非常好的。 最后从Oracle返回的TransactionSignature会以与接收者的签名之前添加方式相同的方式添加到事务中。 至此由于所有必需的签名者都已经做好了自己的准备因此可以准备提交交易了。 Oracle方面 既然我们已经涵盖了代码的客户端我们就需要研究一下Oracle如何验证事务。 以下是Oracle代码的内容 InitiatedBy(CollectOracleStockPriceSignatureFlow::class)
class OracleStockPriceSignatureResponder(private val session: FlowSession) : FlowLogicUnit() {Suspendableoverride fun call() {val transaction session.receiveFilteredTransaction().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.htmloracle连接外部数据库