【问题标题】:Corda - There is more than one CorDapp containing the packageCorda - 包含该软件包的 CorDapp 不止一个
【发布时间】:2020-09-08 01:30:40
【问题描述】:

我正在构建一个需要多个 CorDapps 的应用程序;我们称它们为 LibCorDappAppCorDapp,其中 AppCorDapp 依赖于 LibCorDapp(就像 CorDapp 可能依赖于 Accounts或令牌 SDK)。

LibCorDapp - 合约

这个 CorDapp 的目的是定义一个状态和合约的对象模型,可以开箱即用,但也可以派生出更明确的状态和合约类型。

build.gradle

cordapp {
    targetPlatformVersion cordapp_platform_version
    minimumPlatformVersion cordapp_platform_version
    contract {
        name cordapp_contract_name
        vendor cordapp_vendor_name
        licence cordapp_license
        versionId cordapp_version_id
    }
    signing {
        enabled = cordapp_signing_enabled
    }
}

dependencies {
    implementation "$kotlin_group:kotlin-stdlib-jdk8:$kotlin_version"
    cordaCompile "$corda_group:corda-core:$corda_release_version"

    testRuntimeOnly "$junit_group:junit-jupiter-engine:$junit_version"
    testImplementation "$junit_group:junit-jupiter-api:$junit_version"
    testImplementation "$kotlin_group:kotlin-test:$kotlin_version"
    testImplementation "$corda_group:corda-node-driver:$corda_release_version"
}

示例状态

package com.example.libcordapp.contract

@BelongsToContract(ExampleContract::class)
open class ExampleState<T : Any>(
    override val participants: List<AbstractParty> = emptyList(),
    val value: T
) : ContractState { ... }

合同示例

package com.example.libcordapp.contract

open class ExampleContract {
    final override fun verify(tx: LedgerTransaction) { ... }
    protected open fun onVerifyCreate(tx: LedgerTransaction, signers: Set<PublicKey>) = Unit
}

注意ExampleStateExampleContract 之间的关系。它们都是openExampleContract 包含一个可覆盖的函数,允许派生类在创建时指定额外的合约逻辑。

LibCorDapp - 工作流程

这个 CorDapp 的目的是定义允许创建、更新和使用示例状态的流程。这些流程应该也适用于派生自 ExampleStateExampleContract 的状态和合约。

build.gradle

cordapp {
    targetPlatformVersion cordapp_platform_version
    minimumPlatformVersion cordapp_platform_version
    workflow {
        name cordapp_workflow_name
        vendor cordapp_vendor_name
        licence cordapp_license
        versionId cordapp_version_id
    }
    signing {
        enabled = cordapp_signing_enabled
    }
}

dependencies {
    implementation "$kotlin_group:kotlin-stdlib-jdk8:$kotlin_version"
    cordaCompile "$corda_group:corda-core:$corda_release_version"
    cordapp project(":libcordapp-contract")

    testRuntimeOnly "$junit_group:junit-jupiter-engine:$junit_version"
    testImplementation "$junit_group:junit-jupiter-api:$junit_version"
    testImplementation "$kotlin_group:kotlin-test:$kotlin_version"
    testImplementation "$corda_group:corda-node-driver:$corda_release_version"
}

示例流程

package com.example.libcordapp.workflow

class ExampleFlow(val state: ExampleState) : FlowLogic<SignedTransaction>() { 
    @Suspendable
    override fun call(): SignedTransaction {
        val tx = with(TransactionBuilder(notary)) {
            addOutputState(state)
            addCommand(ExampleContract.Issue, signers)
        }
    }
}

请注意,我没有明确定义输出状态的合约 ID,因为它应该在运行时为每个合约/状态对确定。

AppCorDapp - 合约

此 CorDapp 的目的是演示一个使用 LibCorDapp、创建和测试派生状态和合约的应用程序示例。

build.gradle

cordapp {
    targetPlatformVersion cordapp_platform_version
    minimumPlatformVersion cordapp_platform_version
    contract {
        name "Test CorDapp Contract"
        vendor cordapp_vendor_name
        licence cordapp_license
        versionId cordapp_version_id
    }
    signing {
        enabled = cordapp_signing_enabled
    }
}

dependencies {
    implementation "$kotlin_group:kotlin-stdlib-jdk8:$kotlin_version"
    implementation "$corda_group:corda-core:$corda_release_version"
    cordapp project(":libcordapp-contract")

    testRuntimeOnly "$junit_group:junit-jupiter-engine:$junit_version"
    testImplementation "$junit_group:junit-jupiter-api:$junit_version"
    testImplementation "$kotlin_group:kotlin-test:$kotlin_version"
    testImplementation "$corda_group:corda-node-driver:$corda_release_version"
}

问候状态

package com.example.appcordapp.contract

@BelongsToContract(GreetingContract::class)
class GreetingState(
    participants: List<AbstractParty> = emptyList()
    value: String = "Hello, World!"
) : ExampleState<String>(participants, value)

问候合同

package com.example.appcordapp.contract

class GreetingContract : ExampleContract {
    override fun onVerifyCreate(tx: LedgerTransaction, signers: Set<PublicKey>) = requireThat { ... }
}

请注意GreetingContract 扩展ExampleContractGreetingState 扩展ExampleState 以及将GreetingContract 定义为相关合同,因此在使用ExampleFlow 创建GreetingState 时,它应该选择此合同。

AppCorDapp - 工作流 模块中实际上没有定义任何流,因为我明确想测试我是否可以将ExampleFlow 与派生的状态/合同对一起使用。

测试与问题

对于我的测试,我定义了以下 cordapps:

cordappsForAllNodes = listOf(
    TestCordapp.findCordapp("com.example.libcordapp.contract"),
    TestCordapp.findCordapp("com.example.libcordapp.workflow"),
    TestCordapp.findCordapp("com.example.appcordapp.contract")
)

在尝试使用ExampleFlow 创建GreetingClaim 时...

ExampleFlow(GreetingClaim(participants = listOf(partyA)))

...我得到以下异常:

java.lang.IllegalArgumentException: 在类路径 [/.../libcordapp-contract-0.1.jar, /.../libcordapp-workflow 上有多个包含 com.example.appcordapp.contract 包的 CorDapp -0.1.jar]。指定一个 CorDapp 唯一的包名。

我假设这要么归结为 gradle,要么与我使用派生状态和合同这一事实有关?

【问题讨论】:

  • 我认为派生合约导致了这个问题。

标签: kotlin corda


【解决方案1】:

阅读dependencies的官方文档,阅读this answer;我建议你将build.gradle 中的AppCorDapp - Contract 中的cordapp project(":libcordapp-contract") 更改为cordaCompile project(":libcordapp-contract")

在您当前的依赖设置中(即使用cordapp),libcordapp-contract 被包含在您的 CorDapp 的 jar 文件中,因此当您运行测试时,该包位于两个位置:LibCorDapp - ContractAppCorDapp - ContractCorDapps;如果将依赖改为cordaCompile,则该包将只包含在LibCorDapp - ContractCorDapp的jar文件中。

【讨论】:

  • 遗憾的是,我得到与cordaCompile 相同的结果:(
  • 如果您删除 TestCordapp.findCordapp("com.example.libcordapp.contract") 会发生什么?我的意思是因为libcordapp-contract 已经存在于TestCordapp.findCordapp("com.example.appcordapp.contract") 中;你应该能够只用后者来运行你的测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-06
  • 1970-01-01
  • 2018-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多