Постоянство корды: неизвестная сущность: PersistentFungibleToken - PullRequest
0 голосов
/ 07 апреля 2020

Общее описание

Добрый день!
Я экспериментирую с токеном версии 4.3 SDK SDK версии 1.1 и хочу попробовать различные сценарии ios с выдачей / выкупом пользовательских токенов Fungible.

Я написал простой поток, описывающий выдачу токена для стороны:
Хотя сам базовый поток c, кажется, работает нормально, в перерывах в subFlow (IssueTokensFlow), при попытке сохранить PersistentFungibleToken в базе данных (трассировка стека ниже). Я пытался реализовать свою собственную схему, но безуспешно, так как функция selectedScema () требует только List.
Я также пытался выдать некоторые токены без использования настроенного FungilbeToken, но безуспешно.
Как это исправить?

Поток


import co.paralleluniverse.fibers.Suspendable
import com.maker.loyalty.states.NativeToken
import com.maker.loyalty.states.ParticipantNativeFungibleToken
import com.r3.corda.lib.tokens.contracts.states.FungibleToken
import com.r3.corda.lib.tokens.contracts.utilities.heldBy
import com.r3.corda.lib.tokens.contracts.utilities.of
import com.r3.corda.lib.tokens.workflows.flows.issue.IssueTokensFlow
import com.r3.corda.lib.tokens.workflows.utilities.getPreferredNotary
import net.corda.core.flows.*
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker

object IssueTokenExperiment {
    private fun tracker() = ProgressTracker(
            NotaryInfo,
            CreateTransaction,
            Verify,
            Finalize,
            ExchangeValuesSingleParty.ExecuteExchange,
            SigningByOtherParty
    )

    private const val defaultAmount: Long = 2
    private val otherPartyDefaultName = CordaX500Name(organisation = "PartyA", locality = "London", country = "GB")

    //Scenario - issue token onto the ledger, verify it was issued

    @InitiatingFlow
    @StartableByRPC
    /**
     * Issues token to self an notifies the counter party about issuance
     * */
    class IssueNativeTokenToSelfInitializer(private val amount: Long = defaultAmount,
                                            private val otherPartyX500Name: CordaX500Name = otherPartyDefaultName) : FlowLogic<SignedTransaction>() {

        override val progressTracker: ProgressTracker = tracker()

        @Suspendable
        override fun call(): SignedTransaction {
            progressTracker.currentStep = NotaryInfo
            getPreferredNotary(serviceHub)

            val otherParty: Party = run {
                serviceHub.networkMapCache.getPeerByLegalName(otherPartyX500Name)
                        ?: throw FlowException("Other party not found")
            }
            val otherPartySession = initiateFlow(otherParty)
            progressTracker.currentStep = CreateTransaction

            val issuedSupplyAmount = NativeToken issuedBy ourIdentity
            val supplyAmount = amount of issuedSupplyAmount
//            val liability = supplyAmount heldBy ourIdentity
            val participantNativeAccountState = ParticipantNativeFungibleToken(
                    amount = supplyAmount,
                    issuer = ourIdentity,
                    participants = listOf(ourIdentity, otherParty),
                    observers = emptyList(),
                    owner = ourIdentity
            )
            return subFlow(IssueTokensFlow(
                    token = participantNativeAccountState as FungibleToken,
                    participantSessions = listOf(otherPartySession)
            ))

        }
    }

}

Трассировка стека


java.lang.IllegalArgumentException: Unknown entity: com.r3.corda.lib.tokens.contracts.internal.schemas.PersistentFungibleToken
java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: Unknown entity: com.r3.corda.lib.tokens.contracts.internal.schemas.PersistentFungibleToken
    at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
    at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
    at net.corda.core.internal.concurrent.CordaFutureImpl.get(CordaFutureImpl.kt)
    at com.maker.loyalty.TwoPartyCoreTests.Create token test EXPERIMENT(TwoPartyCoreTests.kt:91)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
    at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:118)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:175)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:157)
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalArgumentException: Unknown entity: com.r3.corda.lib.tokens.contracts.internal.schemas.PersistentFungibleToken
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:807)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:788)
    at net.corda.node.services.schema.PersistentStateService.persistStatesWithSchema$node(PersistentStateService.kt:48)
    at net.corda.node.services.schema.PersistentStateService.persist(PersistentStateService.kt:40)
    at net.corda.node.services.vault.NodeVaultService.processAndNotify(NodeVaultService.kt:393)
    at net.corda.node.services.vault.NodeVaultService.access$processAndNotify(NodeVaultService.kt:54)
    at net.corda.node.services.vault.NodeVaultService$notifyAll$1.invoke(NodeVaultService.kt:224)
    at net.corda.node.services.vault.NodeVaultService$notifyAll$2.invoke(NodeVaultService.kt:235)
    at net.corda.node.services.vault.NodeVaultService.notifyAll(NodeVaultService.kt:239)
    at net.corda.node.services.api.ServiceHubInternal$Companion$recordTransactions$1.invoke(ServiceHubInternal.kt:128)
    at net.corda.node.services.api.ServiceHubInternal$Companion$recordTransactions$1.invoke(ServiceHubInternal.kt:53)
    at net.corda.nodeapi.internal.persistence.CordaPersistence.transaction(CordaPersistence.kt:235)
    at net.corda.nodeapi.internal.persistence.CordaPersistence.transaction(CordaPersistence.kt:214)
    at net.corda.nodeapi.internal.persistence.CordaPersistence.transaction(CordaPersistence.kt:220)
    at net.corda.node.services.api.ServiceHubInternal$Companion.recordTransactions(ServiceHubInternal.kt:72)
    at net.corda.node.services.api.ServiceHubInternal$DefaultImpls.recordTransactions(ServiceHubInternal.kt:158)
    at net.corda.node.internal.AbstractNode$ServiceHubInternalImpl.recordTransactions(AbstractNode.kt:1008)
    at net.corda.core.node.ServiceHub$DefaultImpls.recordTransactions(ServiceHub.kt:214)
    at net.corda.core.internal.ServiceHubCoreInternal$DefaultImpls.recordTransactions(ServiceHubCoreInternal.kt)
    at net.corda.node.services.api.ServiceHubInternal$DefaultImpls.recordTransactions(ServiceHubInternal.kt)
    at net.corda.node.internal.AbstractNode$ServiceHubInternalImpl.recordTransactions(AbstractNode.kt:1008)
    at net.corda.core.node.ServiceHub$DefaultImpls.recordTransactions(ServiceHub.kt:206)
    at net.corda.core.internal.ServiceHubCoreInternal$DefaultImpls.recordTransactions(ServiceHubCoreInternal.kt)
    at net.corda.node.services.api.ServiceHubInternal$DefaultImpls.recordTransactions(ServiceHubInternal.kt)
    at net.corda.node.internal.AbstractNode$ServiceHubInternalImpl.recordTransactions(AbstractNode.kt:1008)
    at net.corda.core.flows.FinalityFlow.notariseAndRecord(FinalityFlow.kt:205)
    at net.corda.core.flows.FinalityFlow.call(FinalityFlow.kt:147)
    at net.corda.core.flows.FinalityFlow.call(FinalityFlow.kt:39)
    at net.corda.node.services.statemachine.FlowStateMachineImpl.subFlow(FlowStateMachineImpl.kt:330)
    at net.corda.core.flows.FlowLogic.subFlow(FlowLogic.kt:326)
    at com.r3.corda.lib.tokens.workflows.internal.flows.finality.ObserverAwareFinalityFlow.call(ObserverAwareFinalityFlow.kt:75)
    at com.r3.corda.lib.tokens.workflows.internal.flows.finality.ObserverAwareFinalityFlow.call(ObserverAwareFinalityFlow.kt:35)
    at net.corda.node.services.statemachine.FlowStateMachineImpl.subFlow(FlowStateMachineImpl.kt:330)
    at net.corda.core.flows.FlowLogic.subFlow(FlowLogic.kt:326)
    at com.r3.corda.lib.tokens.workflows.flows.issue.IssueTokensFlow.call(IssueTokensFlow.kt:84)
    at com.r3.corda.lib.tokens.workflows.flows.issue.IssueTokensFlow.call(IssueTokensFlow.kt:46)
    at net.corda.node.services.statemachine.FlowStateMachineImpl.subFlow(FlowStateMachineImpl.kt:330)
    at net.corda.core.flows.FlowLogic.subFlow(FlowLogic.kt:326)
    at com.maker.loyalty.flows.IssueTokenExperiment$IssueNativeTokenToSelfInitializer.call(IssueTokenExperiment.kt:64)
    at com.maker.loyalty.flows.IssueTokenExperiment$IssueNativeTokenToSelfInitializer.call(IssueTokenExperiment.kt:37)
    at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:270)
    at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:46)
    at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092)
    at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788)
    at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100)
    at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:63)

Fungible Token

import com.maker.loyalty.contracts.ParticipantNativeAccountContract
import com.maker.loyalty.schemas.ParticipantNativeTokenSchemaV1
import com.r3.corda.lib.tokens.contracts.internal.schemas.FungibleTokenSchema
import com.r3.corda.lib.tokens.contracts.internal.schemas.FungibleTokenSchemaV1
import com.r3.corda.lib.tokens.contracts.internal.schemas.PersistentFungibleToken
import com.r3.corda.lib.tokens.contracts.states.FungibleToken
import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType
import com.r3.corda.lib.tokens.contracts.types.TokenType
import net.corda.core.contracts.Amount
import net.corda.core.contracts.BelongsToContract
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState
import net.corda.core.serialization.CordaSerializable
import java.util.*

@BelongsToContract(ParticipantNativeAccountContract::class)
data class ParticipantNativeFungibleToken(override val amount: Amount<IssuedTokenType>,
                                          override val issuer: Party,
                                          val owner: Party,
                                          val observers: List<AbstractParty> = listOf(),
                                          override val participants: List<AbstractParty> = (mutableListOf(owner, issuer) + observers)
) : FungibleToken(amount, issuer), QueryableState {
    override fun generateMappedObject(schema: MappedSchema): PersistentState {
        return when (schema) {
            is FungibleTokenSchemaV1 -> PersistentFungibleToken(
                    issuer = issuer,
                    tokenIdentifier = amount.token.tokenIdentifier,
                    holder = owner,
                    tokenClass = amount.token.tokenClass,
                    amount = amount.quantity
            )
//            is ParticipantNativeTokenSchemaV1 -> ParticipantNativeTokenSchemaV1.PersistentParticipantNativeToken(
//                    issuer = issuer,
//                    tokenIdentifier = amount.token.tokenIdentifier,
//                    holder = owner,
//                    tokenClass = amount.token.tokenClass,
//                    amount = amount.quantity
//            )
            else -> throw IllegalArgumentException("Unrecognised schema $schema")
        }
    }

    override fun supportedSchemas() = listOf(FungibleTokenSchemaV1)

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as ParticipantNativeFungibleToken
        if (amount != other.amount) return false
        if (holder != other.holder) return false
        if (tokenTypeJarHash != other.tokenTypeJarHash) return false
        return true
    }

    override fun hashCode(): Int {
        var result = amount.hashCode()
        result = 31 * result + holder.hashCode()
        result = 31 * result + (tokenTypeJarHash?.hashCode() ?: 0)
        return result
    }
}

Тест

    @Test
    fun `Issue token for self and notify the other party`() {
        val resultFuture = a.startFlow(IssueTokenExperiment.IssueNativeTokenToSelfInitializer(
                amount = 10L,
                otherPartyX500Name = bNodeName
        ))
        network.runNetwork()
        val res = resultFuture.get()
        assertNotNull(res)
    }

    @Before
    fun setup() {
        network.runNetwork()
        setupClients()
        addBonusToClientAccount()
    }

    @After
    fun tearDown() = network.stopNodes()

    private fun setupClients() {
        a.startFlow(ClientAccountFlows.InitiateClientAccountCreation(person = defaultPerson))
        b.startFlow(ClientAccountFlows.InitiateClientAccountCreation(person = defaultPerson))
        network.runNetwork()
    }

    private fun addBonusToClientAccount() {
        a.startFlow(ClientAccountFlows.AddBonusToClientAccount(person = defaultPerson, addedBonus = 100L))
        b.startFlow(ClientAccountFlows.AddBonusToClientAccount(person = defaultPerson, addedBonus = 100L))
        network.runNetwork()
    }

gradle

apply plugin: 'net.corda.plugins.cordapp'
apply plugin: 'net.corda.plugins.quasar-utils'

cordapp {
    targetPlatformVersion corda_platform_version
    minimumPlatformVersion corda_platform_version
    workflow {
        name "Loyalty Flows"
        versionId 1
    }
}

sourceSets {
    main {
        resources {
            srcDir rootProject.file("config/dev")
        }
    }
    test {
        resources {
            srcDir rootProject.file("config/test")
        }
    }
    integrationTest {
        kotlin {
            compileClasspath += main.output + test.output
            runtimeClasspath += main.output + test.output
            srcDir file('src/integrationTest/kotlin')
        }
    }
}

repositories {
    maven { url 'https://ci-artifactory.corda.r3cev.com/artifactory/corda-lib' }
    maven { url 'https://ci-artifactory.corda.r3cev.com/artifactory/corda-lib-dev' }
}

configurations {
    integrationTestCompile.extendsFrom testCompile
    integrationTestRuntime.extendsFrom testRuntime
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
    testCompile "junit:junit:$junit_version"

    // Corda dependencies.
    cordaCompile "$corda_release_group:corda-core:$corda_release_version"
    cordaRuntime "$corda_release_group:corda:$corda_release_version"
    cordaCompile "$tokens_release_group:tokens-workflows:$tokens_release_version"
    testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
    //tokens sdk
    cordaCompile "$tokens_release_group:tokens-workflows:$tokens_release_version"
    cordaCompile "$tokens_release_group:tokens-money:$tokens_release_version"
    cordapp "$tokens_release_group:tokens-selection:$tokens_release_version"

    // CorDapp dependencies.
    cordapp project(":contracts")
}

task integrationTest(type: Test, dependsOn: []) {
    testClassesDirs = sourceSets.integrationTest.output.classesDirs
    classpath = sourceSets.integrationTest.runtimeClasspath
}

1 Ответ

1 голос
/ 07 апреля 2020

Возможно, вы забыли включить зависимость tokens-contracts (в которой отсутствует отсутствующий класс пользовательской схемы, упомянутый в вашем сообщении об ошибке) внутри определения вашего узла (то есть внутри задачи deployNodes в файле root build.gradle).

Добавьте эту строку:

cordapp("$tokens_release_group:tokens-contracts:$tokens_release_version")

Не забудьте также включить эту зависимость в MockService ваших контрактных тестов и MockNetwork ваших тестов потока:

// Contract tests.
private final MockServices ledgerServices = new MockServices(
            Collections.singletonList("com.r3.corda.lib.tokens.contracts"));    

// Flow tests.
private final MockNetwork network = new MockNetwork(new MockNetworkParameters()
            .withCordappsForAllNodes(ImmutableList.of(                
                TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),



...