Corda节点为相同的状态生成不同的线性id

时间:2018-05-15 14:53:46

标签: corda

您好我在参与者列表中有两个实施LinearState的州。作为覆盖linearid的一部分,我将字符串数据传递给UniqueIdentifier构造函数。以下是州定义:

  data class PurchaseOrderState(
        val buyer: Party,
        val seller: Party,
        val props: PurchaseOrderProperties,
        val invoiceAssigned: Boolean

):LinearState{

    override val participants = listOf(buyer, seller)
    override val linearId : UniqueIdentifier = UniqueIdentifier(props.purchaseOrderID)

}

@CordaSerializable
data class PurchaseOrderProperties(
        val purchaseOrderID: String,
        val invoiceStateReference: StateRef?
)   
}

以下是我的流程,其中包含构建交易的逻辑:

object PurchaseOrderFlow {


@InitiatingFlow
@StartableByRPC
class SendPurchaseOrder(val purchaseOrderState: PurchaseOrderState) : FlowLogic<SignedTransaction>(){

companion object{
    object CREATING_BUILDER : ProgressTracker.Step("Creating a Transaction builder for the Purchase Order transaction")
    object ISSUING_PURCHASE_ORDER : ProgressTracker.Step("Creating and signing a new Purchase order")
    object ADDING_STATES : ProgressTracker.Step("Adding Purchase order State in the transation output")
    object ADDING_COMMAND : ProgressTracker.Step("Adding a Create Command in the transaction")
    object VERIFIYING_TX : ProgressTracker.Step("Verifying the txn")
    object SIGNING_TX : ProgressTracker.Step("Signing the transaction")
    object SENDING_TX : ProgressTracker.Step("Sending and committing the transaction")

    fun tracker() = ProgressTracker(CREATING_BUILDER, ISSUING_PURCHASE_ORDER, ADDING_STATES, ADDING_COMMAND, SIGNING_TX, VERIFIYING_TX, SENDING_TX  )
}

override val progressTracker: ProgressTracker = tracker()

@Suspendable
override fun call(): SignedTransaction {

    val notary = serviceHub.networkMapCache.notaryIdentities.first()

    progressTracker.currentStep = CREATING_BUILDER
    val buider = TransactionBuilder(notary)
    buider.setTimeWindow(Instant.now(), Duration.ofSeconds(60))

    progressTracker.currentStep = ISSUING_PURCHASE_ORDER
    buider.addOutputState(purchaseOrderState, PurchaseOrderContract.PO_CONTRACT_ID)
    logger.info("Linerid in PO"+" "+purchaseOrderState.linearId)

    progressTracker.currentStep = ADDING_COMMAND
    val command = Command(PurchaseOrderContract.Commands.Create(), listOf(purchaseOrderState.buyer.owningKey))
    buider.addCommand(command)
   // buider.addAttachment(purchaseOrderState.props.purchaseOrderAttachmentHash)

    progressTracker.currentStep = VERIFIYING_TX
    buider.verify(serviceHub)

    progressTracker.currentStep = SIGNING_TX
    val stx = serviceHub.signInitialTransaction(buider)

    progressTracker.currentStep = SENDING_TX
    return subFlow(FinalityFlow(stx))

}

}

}

以下是流程测试代码:

class PurchaseOrderFlowTests {
lateinit var network: MockNetwork
lateinit var a: StartedMockNode
lateinit var b: StartedMockNode

@Before
fun setup() {
    network = MockNetwork(listOf("com.example.contract"))
    a = network.createPartyNode()
    b = network.createPartyNode()
    // For real nodes this happens automatically, but we have to manually register the flow for tests.
   // listOf(a, b).forEach{ it.registerInitiatedFlow(PurchaseOrderFlow.SendPurcahseOrder::class.java)}
    network.runNetwork()
}

@After
fun tearDown() {
    network.stopNodes()
}

@Test
fun `Create a puchase Order`() {

    val importerCompany = TradeFinanceDataStructures.Company("Importer", "India", null, null)
    val exporterCompany = TradeFinanceDataStructures.Company("Exporter", "EU", null, null)
    val props = PurchaseOrderProperties("abcdefg", importerCompany, exporterCompany, LocalDate.of(2018, 5, 15), SecureHash.randomSHA256(), null, listOf(TradeFinanceDataStructures.Item("LedTv", "45424", "Led Tv by LG India", "gms", 5, 15000.0, 60000.0)))
    val postate = PurchaseOrderState(a.services.myInfo.singleIdentity(), b.services.myInfo.singleIdentity(), props, false)

    val flow = PurchaseOrderFlow.SendPurchaseOrder(postate)
    val future = a.startFlow(flow)
    network.runNetwork()
    val signedTx = future.getOrThrow()
    signedTx.verifySignaturesExcept(b.info.singleIdentity().owningKey)

    listOf(a, b).forEach { node ->
        val locStates = node.transaction {
            print(node.services.vaultService.queryBy<PurchaseOrderState>().states.single().state.data.linearId) // **this was different on both the nodes**
        }

    }
}

在运行上述流程的单元测试之后,我注意到两个节点对于相同的状态具有不同的线性id。上面的代码有什么问题吗?我正在使用corda 3.1版

1 个答案:

答案 0 :(得分:2)

UniqueIdentifier具有以下构造函数:

data class UniqueIdentifier(
    val externalId: String? = null, 
    val id: UUID = UUID.randomUUID()
)

NodeA序列化PurchaseOrderState并将其发送到NodeB时,NodeB会使用州的构造函数重建状态。它使用以下逻辑重新创建状态linearId属性:

override val linearId : UniqueIdentifier = 
    UniqueIdentifier(props.purchaseOrderID)

由于未传递第二个构造函数字段,因此默认为随机UUID。因此,两个节点将看到不同的UniqueIdentifier s,但externalId将是相同的。

为避免这种情况,您应该将linearId传递给构造函数,如下所示:

data class PurchaseOrderState(
    val buyer: Party,
    val seller: Party,
    val props: PurchaseOrderProperties,
    val invoiceAssigned: Boolean,
    override val linearId : UniqueIdentifier = UniqueIdentifier(props.purchaseOrderID)): LinearState {