如何处理相同状态类型

时间:2018-01-29 10:21:45

标签: corda

我有一个用例,在一个原子事务中,我想将两个或多个相同类型的状态转换到不同的生命周期。 (即)

val x : SomeState
val y : SomeState
  • x应通过Command.Approve
  • 过渡到已批准
  • y应转换为使用Command.Audit
  • 审核

verifyApprove上运行x时,合约代码如何仅在verifyAudit上运行y。 可以有无限数量的SomeState可以转换到同一原子事务中的不同生命周期

我是否必须使Command.Approve具有带List<UniqueIdentitifer>的构造函数,以便它可以确定运行其相应验证函数的状态。 随后在流程中填充命令?

4 个答案:

答案 0 :(得分:0)

每个verify函数都会完整地验证事务,而不是其各个状态。但是,您可以在verify内轻松编写逻辑来控制每种状态类型的检查方式。例如,如果XStateYState州共享相同的合同,您可以执行以下操作:

class XAndYContract: Contract {
    override fun verify(tx: LedgerTransaction) {
        val inputXStates = tx.inputsOfType<XState>()
        val outputXStates = tx.outputsOfType<XState>()
        val inputYStates = tx.inputsOfType<YState>()
        val outputYStates = tx.outputsOfType<YState>()

        requireThat {
            "All XState inputs are unapproved." using (inputXStates.all { it.approved == false })
            "All XState outputs are approved" using (outputXStates.all { it.approved == true })
            "All YState inputs are unaudited." using (inputYStates.all { it.audited == false })
            "All YState outputs are audited" using (outputYStates.all { it.audited == true })
        }
    }
}

这只是一个例子。您可以使用Java / Kotlin的全部功能来控制检查哪些状态以及如何检查。

另一种选择是将逻辑拆分为两个合同 - XContractYContract。然后你会:

class XContract : Contract {
    override fun verify(tx: LedgerTransaction) {
        val inputXStates = tx.inputsOfType<XState>()
        val outputXStates = tx.outputsOfType<XState>()

        requireThat {
            "All XState inputs are unapproved." using (inputXStates.all { it.approved == false })
            "All XState outputs are approved" using (outputXStates.all { it.approved == true })
        }
    }
}

class YContract : Contract {
    override fun verify(tx: LedgerTransaction) {
        val inputYStates = tx.inputsOfType<YState>()
        val outputYStates = tx.outputsOfType<YState>()

        requireThat {
            "All YState inputs are unaudited." using (inputYStates.all { it.audited == false })
            "All YState outputs are audited" using (outputYStates.all { it.audited == true })
        }
    }
}

编辑:上面的示例假设有两种类型的状态。假设只有一种状态类型:

class MyState(val lifecycle: Lifecycle, override val linearId: UniqueIdentifier) : LinearState {
    override val participants = listOf<AbstractParty>()
}

Lifecycle定义为:

enum class Lifecycle {
    ISSUED, AUDITED, APPROVED
}

我们希望强加规则,即在每个事务中,ISSUED阶段中的任何输入都会转换为APPROVED,并且APPROVED阶段中的任何输入都会转换为{{ 1}}。

我们可以使用AUDITED

来实现这一目标
groupStates

class MyContract : Contract { override fun verify(tx: LedgerTransaction) { val groups = tx.groupStates { it: MyState -> it.linearId } requireThat { for (group in groups) { val inputState = group.inputs.single() val outputState = group.outputs.single() when (inputState.lifecycle) { Lifecycle.ISSUED -> { "Issued states have been transitioned to approved" using (outputState.lifecycle == Lifecycle.APPROVED) } Lifecycle.APPROVED -> { "Approved states have been transitioned to audited" using (outputState.lifecycle == Lifecycle.AUDITED) } } } } } } 的工作原理是根据某些规则将输入和输出分组在一起。在我们的例子中,我们将共享相同groupStates的输入和输出分组。然后我们可以检查每个输入是否已正确演变为相应的输出。

答案 1 :(得分:0)

要实现该功能,您只需使用命令中的switch语句即可。例如,在验证(tx)中,您可以执行以下操作:

when (command.value) {
        is Commands.Approve -> verifyApprove(tx, setOfSigners)
        is Commands.Audit -> verifyAudit(tx, setOfSigners)
        else -> throw IllegalArgumentException("Unrecognised command")
}

虽然命令可以定义如下:

interface Commands : CommandData {
    class Issue : TypeOnlyCommandData(), Commands
    class Transfer : TypeOnlyCommandData(), Commands
}

并在verifyApprove()和verifyAudit()中,您可以进行验证。

答案 2 :(得分:0)

我尝试将状态传递给命令本身,以便我们知道命令需要注意哪些状态。在验证方法中,然后不要检查单个命令。

interface Commands : CommandData {
    class Create(val outputStates:List<MyState>): TypeOnlyCommandData(), Commands
    class Update(val inOutStates:List<InOutState<MyState>>): TypeOnlyCommandData(), Commands
   }
 ................
@CordaSerializable
data class InOutState<T: ContractState>(val inputState: T, val outputState:T)
 ............
 tx.commands.forEach {

            when (it.value) {
                is Commands.Create -> {
                   val cmd = it.value as Commands.Create

                    requireThat{

                        "Output states must be present" using (cmd.outputStates.isNotEmpty())
                    }
             }
      }

答案 3 :(得分:0)

另一种模式是将Corda contract Command设置为State Object,并在合同验证期间由Command进行分组

@CordaSerializable
interface CommandAwareState<T:CommandData>: LinearState,QueryableState{

     val command: T
}

============
interface LinearStateCommands<T : LinearState> : CommandData {


    fun verify(inOutStates: List<InOutState<T>>, tx: LedgerTransaction)
}
===========


data class StateA(val attribX: String, val attribY: String
                 , override val command: StateAContract.Commands = 
                  StateAContract.Commands.None()): 
                       CommandAwareState<StateAContract.Commands>
 ============

 open class StateAContract : Contract {
companion object {
    @JvmStatic
    val CONTRACT_ID = "com.myproj.contract.StateAContract"
}

interface Commands : LinearStateCommands<DocumentState> {

    class None(): TypeOnlyCommandData(), Commands{
        override fun verify(inOutState: List<InOutState<StateA>>, tx: 
                         LedgerTransaction) {

                requireThat {
                    "There should not be any Input and Outputs" using 
                     (inOutState.isEmpty())
                }

        }


    }

     class Create(): TypeOnlyCommandData(), Commands{

          override fun verify(inOutStates: List<InOutState<DocumentState>>, tx: 
       LedgerTransaction) {
        //WRITE Contract verification
        }  
     }

override fun verify(tx: LedgerTransaction) {

           tx.matchLinearStatesByCommand(DocumentState::class.java).forEach{

               it.key.verify(it.value,tx)
           }
    }
 }
==========

inline fun <T,reified  K : CommandAwareState<T>> LedgerTransaction.matchLinearStatesByCommand(ofType: Class<K>): Map<T,List<InOutState<K>>>{



    groupStates<K,UniqueIdentifier> { it.linearId }.let {

        var mapByCommand = mutableMapOf<T,MutableList<InOutState<K>>>()
        it.forEach {

            if(mapByCommand.containsKey(it.outputs.single().command)){

                mapByCommand.get(it.outputs.single().command)?.add(InOutState(it.inputs.noneOrSingle(),it.outputs.noneOrSingle()))

            }else{

                mapByCommand.put(it.outputs.single().command, mutableListOf(InOutState(it.inputs.noneOrSingle(),it.outputs.noneOrSingle())))

            }


        }
        return mapByCommand
    }

}