使用csv导入导出数据_使用CSV数据验证合同

使用csv导入导出数据

Corda中的附件不仅可以是随事务一起发送的PDF。 实际上,可以在运行流程时甚至在合同的verify功能内部以编程方式使用它们。 您为什么要这么做? when考虑时,答案很有意义。 让我们以包含CSV数据的附件为例。 实际上,这就是这篇文章的主题。 无论如何。 附件可以包含允许状态具有的所有有效ID(或其他任何有效ID)。 现在,可以在您的代码中完成此操作,但这对于需要随时间变化的系统而言不切实际。 也许有新的ID需要添加。 如果它们存在于代码中,则只要允许新值,就需要编译并分发更新的CorDapp。 不完全实用。 但是,上载包含数据的文件的新版本可以使验证随时间变化,而无需重新编译。 那是一个很合理的想法。

现在,我们同意根据附件数据验证事务的内容是一个好主意。 下一个问题是在哪里进行验证的最佳位置。 如前所述,有两种选择。 在流程内部还是在合同中。 在这里将其放入合同中是最有意义的,因为接收交易的所有各方都必须运行附件的验证。 保证他们都对交易的有效性达成共识🤝。

知道您可以使用附件来验证合同是一件很重要的事情,但是在实现之前,仍有一些代码需要编写。 不过不要担心,我得到了你。

与附件建立交易

保持简短简短,因为可以在docs中找到有关此内容的更多信息。 以下是来自流程的一些代码,该流程构建包含附件的事务(假定附件已经存在):

private fun transaction(): TransactionBuilder =
  TransactionBuilder(notary()).apply {
    val attachmentId = attachment()
    addOutputState(message)
    addCommand(Command(Send(attachmentId), message.participants.map(Party::owningKey)))
    addAttachment(attachmentId)
  }

private fun attachment(): AttachmentId {
  return serviceHub.attachments.queryAttachments(
    AttachmentQueryCriteria.AttachmentsQueryCriteria(
      filenameCondition = Builder.equal(
        attachmentName
      )
    )
  ).first()
}

将附件添加到事务中不包含任何特殊代码。 检索附件要复杂一些,但也不难组合。 在此示例中,按名称查询AttachmentId ,然后将返回的AttachmentId传递到TransactionBuilderaddAttachment (它使用AttachmentId而不是附件本身)。 使用附件名称是我的偏爱,但AttachmentId是您事先知道,也可以传入AttachmentId

您可能还没有捕获到另一段偷偷摸摸的代码。 我将AttachmentId传递Send命令。 这样做可以使附件的哈希值已知,并使以后从事务中检索它变得容易得多。 我将快速向您展示我是如何做到的:

class MessageContract : Contract {

  interface Commands : CommandData {
    class Send(attachmentId: AttachmentId) : CommandWithAttachmentId(attachmentId), Commands
  }

  abstract class CommandWithAttachmentId(val attachmentId: AttachmentId) : CommandData {
    override fun equals(other: Any?) = other?.javaClass == javaClass
    override fun hashCode() = javaClass.name.hashCode()
  }
}

确认合同

这就是魔法🧙‍♀️发生的地方。 通过使用先前添加到事务中的附件,可以提取其中的数据并将其用于验证事务中的状态。 以下是合同的verify功能:

override fun verify(tx: LedgerTransaction) {
  val command = tx.commands.requireSingleCommand<Commands>()
  when (command.value) {
    is Commands.Send -> requireThat {
      "No inputs should be consumed when sending a message." using (tx.inputs.isEmpty())
      "Only one output state should be created when sending a message." using (tx.outputs.size == 1)
    }
    is Commands.Reply -> requireThat {
      "One input should be consumed when replying to a message." using (tx.inputs.size == 1)
      "Only one output state should be created when replying to a message." using (tx.outputs.size == 1)
    }
  }
  require(isMessageInCsv(tx)) {
    "The output message must be contained within the csv of valid messages. " +
            "See attachment with hash = ${tx.attachments.first().id} for its contents"
  }
}

private fun isMessageInCsv(tx: LedgerTransaction): Boolean {
  val message = tx.outputsOfType<MessageState>().first()
  val attachmentId = tx.commandsOfType<CommandWithAttachmentId>().single().value.attachmentId
  return tx.getAttachment(attachmentId).openAsJAR().use { zipInputStream: JarInputStream ->
    zipInputStream.nextJarEntry.name
    val csv = CSVFormat.DEFAULT.withHeader("valid_messages")
      .withFirstRecordAsHeader()
      .parse(InputStreamReader(zipInputStream))
    csv.records.any { row -> row.get("valid messages") == message.contents }
  }
}

在此示例中,有趣的内容位于isMessageInCsv函数内。 可能看起来有些令人生畏,但也许我只需要整理一下…………忘了我这么说吧。 它只需要一些解释(或者可能是一些代码注释)。

可以通过事务的commands属性或getAttachment函数(采用position或AttachmentId / SecureHash )从事务中检索AttachmentId 。 在此示例中,使用getAttachment检索包含CSV数据的附件以及先前传递到命令中的ID。

至此,附件已被检索到。 现在,需要解析其中的CSV数据并将其与交易状态进行比较。 为了简化这一过程,我使用了Apache Commons CSV 。 使用openAsJAR打开附件(附件存储为zip),并利用该库读取其中的数据。 读取每一行时,它将检查MessageStatecontents是否与当前行匹配。 如果有的话,那就太好了。 该州通过了测试,该合同被视为有效。 如果不匹配,将输出以下错误,您将需要再次尝试。

net.corda.core.contracts.TransactionVerificationException$ContractRejection: Contract verification failed: 
The output message must be contained within the csv of valid messages. See attachment with hash = 3E3031BA98F3F01843E8FD0A1B34E21C599C9C8F09765C2F820E45D6E8770948 for its contents, 
contract: com.lankydanblog.tutorial.contracts.MessageContract, transaction: 5C8963497E493684A78C0A95A30E4C30029E6C36A792DE8A1B1733DE5358BB15

该代码确实假定CSV的内容遵循某种格式。 换句话说, "valid messages"被硬编码为文件中的标头。 如果不存在,那么验证将变得毫无意义。 如果发生这种情况,它将失败,这实际上是一件好事,但是如果您还记得我在引言中所说的话。 这引入了更多的硬编码,并降低了合同的灵活性。 如果您想改善这一点,则可以将命令中要使用的行的名称传递给命令,就像我之前在其中添加AttachmentId一样。

结语

如您所见,使用CSV附件中的数据验证交易不需要太多工作。 实际上,我敢打赌,如果您想自己实现这一点,您的代码将与我的代码几乎相同。 我的意思是,只有这么多种方式可以做到这一点。 通过委托给图书馆,大部分工作将为您完成。

要总结这些步骤,需要使用一些CSV数据验证交易:

  • 将CSV上载到节点
  • 在流内部,检索它并将其添加到事务中
  • 在合同内,从交易中获取CSV,最后将交易内容与其进行比较。

这样做将使您的验证可以随时间变化,而无需重新编译CorDapp。 听起来不错吧? 我认同。 如果您不这样做,则应该这样做,因为它绝对很棒😎。

这篇文章中使用的代码可以在我的GitHub上找到

翻译自: https://www.javacodegeeks.com/2019/08/verifying-a-contract-with-csv-data.html

使用csv导入导出数据

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值