R3 Corda: 升级 CorDapp(非平台版本升级)- Contract 和 state 版本

原文地址:https://docs.corda.net/upgrading-cordapps.html#contract-and-state-versioning

这里有两种类型的 contract/state 升级:

  1. 隐式的升级:使用约束(constraints)允许提前对于 contract 开发多种实现
  2. 显式的升级:创建一个特殊的更新合约的 transaction然后使用升级合约 flows 来获得 state 的所有参与者的签名

这里我们会关注显式的升级。

在显式的升级中,contracts 和 state 可以按照任何的方式来变化,这些变化仅仅在 state 的所有参与者对这个升级都同意的条件下才会生效。下边是可能的更新组合:

  • Contract 更新了,但是 state 定义没有更新
  • State 更新了,但是 contract 保持不变
  • Contract 和 State 都更新了

使用一个特殊日子(flag-day) 的方式来更新 state 或者 contract 非常简单:

  • 更新并且测试 state 或者 contract
  • 生成一个新的 CorDapp JAR 文件并且分发给所有相关的节点
  • 每个节点的操作者停止他们的节点,将已经存在的 JAR 文件用新版本的替换,然后重启节点。他们可能会首先执行节点排空(node drain)以避免当 flow 还在进行的过程中的时候关于 state 或者 contract 的定义却变了。
  • 在每个节点上对每个需要升级的 state 运行合约升级授权(contract upgrade authorisation)flow。
  • 针对于每个 state,某节点应该运行合约升级初始化 flow,它会同其他的节点进行沟通。

更新流程

编写新的 state 和 contract 定义

由更新 contract 和/或 state 定义开始。对于如何更新 states,并没有任何的限制。但是更新 contracts 必须要实现 UpgradedContract 接口。接口定义如下:

interface UpgradedContract : Contract {
    val legacyContract: ContractClassName
    fun upgrade(state: OldState): NewState
}

upgrade 方法描述了旧的 state 类型是如何更新成新的 state 类型的。如果 state 没有更新的话,那可以给旧的和新的 state 类型参数使用相同的 state 类型。

新的 contract 默认只能够更新在白名单中的已有的 states。如果使用了 hash 或者其他的约束类型话,新的 contract 必须要实现 UpgradedContractWithLegacyConstraint,并且需要显式地指明是哪种约束:

interface UpgradedContractWithLegacyConstraint : UpgradedContract {
    val legacyContractConstraint: AttachmentConstraint
}

比如,如果是 hash 约束的话,那么原始的 JAR 文件的 hash 需要被提供:

override val legacyContractConstraint: AttachmentConstraint
    get() = HashAttachmentConstraint(SecureHash.parse("E02BD2B9B010BBCE49C0D7C35BECEF2C79BEB2EE80D902B54CC9231418A4FA0C"))

升级授权 Authorising

如果新的 states 和 contracts 已经被放到了所有节点的 classpath 下之后,下一步就是每个节点去运行 ContractUpgradeFlow.Authorise flow。这个 flow 会带有一个需要更新的 StateAndRef 的 state,还有一个对新的 contract 的引用,这个 contract 必须要实现 UpgradedContract 接口。

在任何时间,节点的管理员都可以通过运行 ContractUpgradeFlow.Deauthorise flow 来不通过一个 contract 的升级。

执行升级

当所有的节点都执行完了授权流程后,必须要选择一个参与节点通过 ContractUpgradeFlow.Initiate flow 来初始对每个 state 对象的更新。这个 flow 有这样的特点:

class Initiate(
    originalState: StateAndRef,
    newContractClass: Class>
) : AbstractStateReplacementFlow.Instigator>>(originalState, newContractClass)

这个 flow 是 AbstractStateReplacementFlow 的子类(sub-class),它也可以用来对不需要更新 contract 的 state 对象进行更新。

当 flow 成功结束后,所有参与节点的旧的 state 对象应该被更新为升级过的 state 对象了,他们也会指向新的 contract code。

需要注意的点

Contract 更新 flows 的能力

  • 不需要管它的名字,ContractUpgradeFlow 同样可以处理 state 对象的更新
  • 在一次升级中,State 可以彻底的发生改变。比如可以从一个 state 变成 state,只需要确保所有 state 的参与者都同意这个变化
  • 同样,state 对象还可以完全不变
  • 如果一个节点没有运行 contract 升级授权 flow 的话,他们将不会更新 contract 和/或 state 对象的更新
  • 被授权的升级可以在后来被取消授权
  • 升级不需要马上执行。在一段时期内,双方还是可以继续使用旧的 state 和 contracts
  • State schema 改动需要单独处理

编写新的 states 和 contracts

  • 如果一个属性从一个 state 中被移除的话,那么在 contract code 中队它的任何引用也需要被移除。负责的话你会不能成功地编译你的 contract code。通常我们不建议从 states 中删除属性,而是将他们标记成已经弃用的(deprecated)
  • 当向一个 state 中添加属性的时候,需要考虑一下新加的属性会对引用这个 state 的 transaction 验证产生怎样的影响。如果新的属性对于 contract code 没有影响的话,那么你可以将这个属性赋任何值
  • 更新后的 state 对象还可以继续使用旧的 contract code,只要在没有必要去更新 contract code 的情况下

对于旧的 contract code JAR 文件

当前,所有的参与节点必须要在节点的 classpath 中保留旧的 state 和 contract 定义,因为在验证 transactions 的时候,他们始终会被要求验证以前使用的旧版本的 state 和 contract。

你需要注意可能的 classpath 冲突。如果你还保留着旧版本的 JAR 文件的话,请确保新版本的不会包含同样名字的类(比如在 Kotlin 中文件级别的声明会被放在一个名字在文件名后边的静态类中)

权限 Permissioning

  • 只有节点管理员才能够运行 contract 升级授权和不授权的 flows

流程 Logistics

  • 所有的节点都需要执行升级授权 flow
  • 只有一个节点应该运行初始 contract 升级 flow。如果多个节点对相同的 StateAndRef 运行了初始化 flow,一个“双花”问题会在双方产生,最先完成的会生效
  • 这里提供的升级 flows 每次只会升级一个 state 对象

你可能感兴趣的:(R3 Corda: 升级 CorDapp(非平台版本升级)- Contract 和 state 版本)