Узлы Корды, генерирующие разные линейные идентификаторы для одного и того же состояния - PullRequest
0 голосов
/ 15 мая 2018

Привет, у меня есть штат, реализующий LinearState с двумя участниками в списке участников. Как часть переопределения линеарида, я передаю строковые данные в конструктор 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**
        }

    }
}

После запуска модульных тестов для вышеуказанного потока я заметил, что два узла имеют разные линейные идентификаторы для одного и того же состояния. Что-то не так в приведенном выше коде? Я использую Corda версии 3.1

1 Ответ

0 голосов
/ 15 мая 2018

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 с, хотя 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 {
...