mirror of
https://github.com/corda/corda.git
synced 2024-12-30 01:39:04 +00:00
Merge pull request #5987 from corda/chrisr3-44-merge
Merge from OS 4.4 up to 88008b.
This commit is contained in:
commit
28cb9e6cf4
@ -12,6 +12,9 @@ API: Contract Constraints
|
|||||||
|
|
||||||
.. note:: Before reading this page, you should be familiar with the key concepts of :doc:`key-concepts-contracts`.
|
.. note:: Before reading this page, you should be familiar with the key concepts of :doc:`key-concepts-contracts`.
|
||||||
|
|
||||||
|
.. note:: As of Corda |corda_version| the `minimumPlatformVersion` required to use these features is 4
|
||||||
|
(see :ref:`Network Parameters <network-parameters>` and :doc:`features-versions` for more details).
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
Reasons for Contract Constraints
|
Reasons for Contract Constraints
|
||||||
|
80
docs/source/features-versions.rst
Normal file
80
docs/source/features-versions.rst
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
Corda Features to Versions
|
||||||
|
==========================
|
||||||
|
|
||||||
|
New versions of Corda introduce new features. These fall into one of three categories which have subtle but important implications for
|
||||||
|
node owners, application developers and network operators.
|
||||||
|
|
||||||
|
The first set are changes that have no impact on application developers or the Corda network protocol. An example would be support for
|
||||||
|
a new HSM or database system, for example, and which are of interest only to a node's operator.
|
||||||
|
|
||||||
|
The second set are new or changed APIs, which are of interest to CorDapp developers. When a release of Corda ships such features, the
|
||||||
|
Platform Version of that node is incremented so that a CorDapp that relies on such a new or changed feature can detect this (eg to
|
||||||
|
prevent it from running on a node without the feature or to trigger an alternative optimised codepath if the feature is present). The
|
||||||
|
app developer should set the CorDapp's minimumPlatformVersion parameter to signal the minimum Platform Version against which the app
|
||||||
|
can run or has been tested. If the application has also been tested against a greater platform version and can exploit it if present,
|
||||||
|
the node can also set the targetPlatformVersion field.
|
||||||
|
|
||||||
|
The third set of changes are those which could affect the operation of a Corda network. Examples would include a change to the
|
||||||
|
serialisation format or flow/wire protocol, or introduction of a new transaction component. These are changes to the core data model and
|
||||||
|
these features have the property that it is not safe for any node or application to take advantage of until all nodes on the network
|
||||||
|
are capable of understanding them. Such features are thus only enabled in a node if the network to which it is connected has published
|
||||||
|
a minimumPlatformVersion in its network parameters that is greater than or equal to the Corda Platform Version that introduced the
|
||||||
|
feature. For example, Corda 4.0 nodes, which implement Corda Platform Version 4, can only take advantage of the Corda Reference States
|
||||||
|
feature when connected to a network with mPV 4.
|
||||||
|
|
||||||
|
If there is a Platform Version below which your application will not run or is not supported, then signal that with the application's
|
||||||
|
`CorDapp.mPV` field. This will prevent older nodes from running your app. Nodes which support newer Platform Versions may also use this
|
||||||
|
field to trigger code paths that emulate behaviours that were in force on that older Platform Version to maximise compatibility. However,
|
||||||
|
if you have tested your app against newer versions of Corda and know your node can take advantage of the new Platform Version behaviours
|
||||||
|
if present, you can signal this by using `CorDapp.targetPV` to declare the latest Platform Version against which the app has been tested
|
||||||
|
and is known to work. In this way, it is possible to ship CorDapps that can both run on all nodes supporting some minimum Platform Version
|
||||||
|
of Corda as well as opt in to newer features should they happen to be available on any given node.
|
||||||
|
|
||||||
|
.. list-table:: Corda Features
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Feature
|
||||||
|
- Corda Platform Version (PV)
|
||||||
|
- Min Network Platform Version (network mPV)
|
||||||
|
- Introduced in OS version
|
||||||
|
- Introduced in Enterprise version
|
||||||
|
* - Observer Nodes
|
||||||
|
- 2
|
||||||
|
- 2
|
||||||
|
- 2.0
|
||||||
|
- n/a
|
||||||
|
* - Corda Serialization Framework
|
||||||
|
- 3
|
||||||
|
- 3
|
||||||
|
- 3.0
|
||||||
|
- 3.0
|
||||||
|
* - Hash Constraints
|
||||||
|
- 1
|
||||||
|
- 1
|
||||||
|
- 1.0
|
||||||
|
- 1.0
|
||||||
|
* - Whitelist Constraints
|
||||||
|
- 3
|
||||||
|
- 3
|
||||||
|
- 3.0
|
||||||
|
- 3.0
|
||||||
|
* - Inline Finality Flow
|
||||||
|
- 4
|
||||||
|
- 3
|
||||||
|
- 4.0
|
||||||
|
- 4.0
|
||||||
|
* - Reference States
|
||||||
|
- 4
|
||||||
|
- 4
|
||||||
|
- 4.0
|
||||||
|
- 4.0
|
||||||
|
* - Signature Constraints
|
||||||
|
- 4
|
||||||
|
- 4
|
||||||
|
- 4.0
|
||||||
|
- 4.0
|
||||||
|
* - Underlying Support for Accounts
|
||||||
|
- 5
|
||||||
|
- 4
|
||||||
|
- 4.3
|
||||||
|
- 4.3
|
@ -77,6 +77,8 @@ cluster generated like this can be sized for the maximum size you may need, and
|
|||||||
|
|
||||||
More information can be found in :doc:`network-bootstrapper`.
|
More information can be found in :doc:`network-bootstrapper`.
|
||||||
|
|
||||||
|
.. _network-parameters:
|
||||||
|
|
||||||
Network parameters
|
Network parameters
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@ -132,6 +134,8 @@ The current set of network parameters:
|
|||||||
Encountering an owned contract in a JAR that is not signed by the rightful owner is most likely a sign of malicious behaviour, and should be reported.
|
Encountering an owned contract in a JAR that is not signed by the rightful owner is most likely a sign of malicious behaviour, and should be reported.
|
||||||
The transaction verification logic will throw an exception when this happens.
|
The transaction verification logic will throw an exception when this happens.
|
||||||
|
|
||||||
|
.. note:: To determine which `minimumPlatformVersion` a zone must mandate in order to permit all the features of Corda |corda_version| see :doc:`features-versions`
|
||||||
|
|
||||||
More parameters will be added in future releases to regulate things like allowed port numbers, whether or not IPv6
|
More parameters will be added in future releases to regulate things like allowed port numbers, whether or not IPv6
|
||||||
connectivity is required for zone members, required cryptographic algorithms and roll-out schedules (e.g. for moving to post quantum cryptography), parameters related to SGX and so on.
|
connectivity is required for zone members, required cryptographic algorithms and roll-out schedules (e.g. for moving to post quantum cryptography), parameters related to SGX and so on.
|
||||||
|
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package net.corda.contracts.serialization.whitelist
|
||||||
|
|
||||||
|
import net.corda.core.contracts.CommandData
|
||||||
|
import net.corda.core.contracts.Contract
|
||||||
|
import net.corda.core.contracts.ContractState
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
|
|
||||||
|
class WhitelistContract : Contract {
|
||||||
|
companion object {
|
||||||
|
const val MAX_VALUE = 2000L
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun verify(tx: LedgerTransaction) {
|
||||||
|
val states = tx.outputsOfType<State>()
|
||||||
|
require(states.isNotEmpty()) {
|
||||||
|
"Requires at least one data state"
|
||||||
|
}
|
||||||
|
|
||||||
|
states.forEach {
|
||||||
|
require(it.whitelistData in WhitelistData(0)..WhitelistData(MAX_VALUE)) {
|
||||||
|
"WhitelistData $it exceeds maximum value!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("CanBeParameter", "MemberVisibilityCanBePrivate")
|
||||||
|
class State(val owner: AbstractParty, val whitelistData: WhitelistData) : ContractState {
|
||||||
|
override val participants: List<AbstractParty> = listOf(owner)
|
||||||
|
|
||||||
|
@Override
|
||||||
|
override fun toString(): String {
|
||||||
|
return whitelistData.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Operate : CommandData
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package net.corda.contracts.serialization.whitelist
|
||||||
|
|
||||||
|
import net.corda.core.serialization.SerializationWhitelist
|
||||||
|
|
||||||
|
data class WhitelistData(val value: Long) : Comparable<WhitelistData> {
|
||||||
|
override fun compareTo(other: WhitelistData): Int {
|
||||||
|
return value.compareTo(other.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = "$value things"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Whitelist : SerializationWhitelist {
|
||||||
|
override val whitelist = listOf(WhitelistData::class.java)
|
||||||
|
}
|
@ -6,11 +6,9 @@ import net.corda.core.contracts.Command
|
|||||||
import net.corda.core.contracts.CommandData
|
import net.corda.core.contracts.CommandData
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
|
||||||
@InitiatingFlow
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class SandboxAttachmentFlow(private val command: CommandData) : FlowLogic<SecureHash>() {
|
class SandboxAttachmentFlow(private val command: CommandData) : FlowLogic<SecureHash>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@ -6,11 +6,9 @@ import net.corda.core.contracts.Command
|
|||||||
import net.corda.core.contracts.TypeOnlyCommandData
|
import net.corda.core.contracts.TypeOnlyCommandData
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
|
||||||
@InitiatingFlow
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class NonDeterministicFlow(private val trouble: TypeOnlyCommandData) : FlowLogic<SecureHash>() {
|
class NonDeterministicFlow(private val trouble: TypeOnlyCommandData) : FlowLogic<SecureHash>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@ -6,12 +6,10 @@ import net.corda.core.contracts.Command
|
|||||||
import net.corda.core.contracts.CommandData
|
import net.corda.core.contracts.CommandData
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
|
||||||
@InitiatingFlow
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class DeterministicCryptoFlow(
|
class DeterministicCryptoFlow(
|
||||||
private val command: CommandData,
|
private val command: CommandData,
|
||||||
|
@ -7,11 +7,9 @@ import net.corda.contracts.djvm.whitelist.WhitelistData
|
|||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
|
||||||
@InitiatingFlow
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class DeterministicWhitelistFlow(private val data: WhitelistData) : FlowLogic<SecureHash>() {
|
class DeterministicWhitelistFlow(private val data: WhitelistData) : FlowLogic<SecureHash>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@ -7,11 +7,9 @@ import net.corda.contracts.fixup.dependent.DependentData
|
|||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
|
||||||
@InitiatingFlow
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class CordappFixupFlow(private val data: DependentData) : FlowLogic<SecureHash>() {
|
class CordappFixupFlow(private val data: DependentData) : FlowLogic<SecureHash>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@ -7,11 +7,9 @@ import net.corda.contracts.serialization.custom.CustomSerializerContract.Purchas
|
|||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
|
||||||
@InitiatingFlow
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class CustomSerializerFlow(
|
class CustomSerializerFlow(
|
||||||
private val purchase: Currantsy
|
private val purchase: Currantsy
|
||||||
|
@ -7,11 +7,9 @@ import net.corda.contracts.serialization.missing.MissingSerializerContract.Opera
|
|||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
|
||||||
@InitiatingFlow
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class MissingSerializerBuilderFlow(private val value: Long) : FlowLogic<SecureHash>() {
|
class MissingSerializerBuilderFlow(private val value: Long) : FlowLogic<SecureHash>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@ -12,14 +12,12 @@ import net.corda.core.crypto.SecureHash
|
|||||||
import net.corda.core.crypto.SignableData
|
import net.corda.core.crypto.SignableData
|
||||||
import net.corda.core.crypto.SignatureMetadata
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.internal.createComponentGroups
|
import net.corda.core.internal.createComponentGroups
|
||||||
import net.corda.core.internal.requiredContractClassName
|
import net.corda.core.internal.requiredContractClassName
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
|
|
||||||
@InitiatingFlow
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class MissingSerializerFlow(private val value: Long) : FlowLogic<SecureHash>() {
|
class MissingSerializerFlow(private val value: Long) : FlowLogic<SecureHash>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package net.corda.flows.serialization.whitelist
|
||||||
|
|
||||||
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import net.corda.contracts.serialization.whitelist.WhitelistContract
|
||||||
|
import net.corda.contracts.serialization.whitelist.WhitelistContract.State
|
||||||
|
import net.corda.contracts.serialization.whitelist.WhitelistData
|
||||||
|
import net.corda.core.contracts.Command
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.flows.StartableByRPC
|
||||||
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
|
||||||
|
@StartableByRPC
|
||||||
|
class WhitelistFlow(private val data: WhitelistData) : FlowLogic<SecureHash>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call(): SecureHash {
|
||||||
|
val notary = serviceHub.networkMapCache.notaryIdentities[0]
|
||||||
|
val stx = serviceHub.signInitialTransaction(
|
||||||
|
TransactionBuilder(notary)
|
||||||
|
.addOutputState(State(ourIdentity, data))
|
||||||
|
.addCommand(Command(WhitelistContract.Operate(), ourIdentity.owningKey))
|
||||||
|
)
|
||||||
|
stx.verify(serviceHub, checkSufficientSignatures = false)
|
||||||
|
return stx.id
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package net.corda.node
|
||||||
|
|
||||||
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
|
import net.corda.contracts.serialization.whitelist.WhitelistData
|
||||||
|
import net.corda.core.contracts.TransactionVerificationException.ContractRejection
|
||||||
|
import net.corda.core.messaging.startFlow
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import net.corda.flows.serialization.whitelist.WhitelistFlow
|
||||||
|
import net.corda.node.services.Permissions
|
||||||
|
import net.corda.testing.core.ALICE_NAME
|
||||||
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
|
import net.corda.testing.driver.DriverParameters
|
||||||
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.driver.internal.incrementalPortAllocation
|
||||||
|
import net.corda.testing.node.NotarySpec
|
||||||
|
import net.corda.testing.node.User
|
||||||
|
import net.corda.testing.node.internal.cordappWithPackages
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.Parameterized
|
||||||
|
import org.junit.runners.Parameterized.Parameters
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
|
@RunWith(Parameterized::class)
|
||||||
|
@Suppress("FunctionName")
|
||||||
|
class ContractWithSerializationWhitelistTest(private val runInProcess: Boolean) {
|
||||||
|
companion object {
|
||||||
|
const val DATA = 123456L
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val logger = loggerFor<ContractWithSerializationWhitelistTest>()
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val contractCordapp = cordappWithPackages("net.corda.contracts.serialization.whitelist").signed()
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val workflowCordapp = cordappWithPackages("net.corda.flows.serialization.whitelist").signed()
|
||||||
|
|
||||||
|
fun parametersFor(runInProcess: Boolean): DriverParameters {
|
||||||
|
return DriverParameters(
|
||||||
|
portAllocation = incrementalPortAllocation(),
|
||||||
|
startNodesInProcess = runInProcess,
|
||||||
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
||||||
|
cordappsForAllNodes = listOf(contractCordapp, workflowCordapp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameters
|
||||||
|
@JvmStatic
|
||||||
|
fun modes(): List<Array<Boolean>> = listOf(Array(1) { true }, Array(1) { false })
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun checkData() {
|
||||||
|
assertNotCordaSerializable<WhitelistData>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 300_000)
|
||||||
|
fun `test serialization whitelist`() {
|
||||||
|
logger.info("RUN-IN-PROCESS=$runInProcess")
|
||||||
|
|
||||||
|
val user = User("u", "p", setOf(Permissions.all()))
|
||||||
|
driver(parametersFor(runInProcess = runInProcess)) {
|
||||||
|
val badData = WhitelistData(DATA)
|
||||||
|
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||||
|
val ex = assertFailsWith<ContractRejection> {
|
||||||
|
CordaRPCClient(hostAndPort = alice.rpcAddress)
|
||||||
|
.start(user.username, user.password)
|
||||||
|
.use { client ->
|
||||||
|
client.proxy.startFlow(::WhitelistFlow, badData)
|
||||||
|
.returnValue
|
||||||
|
.getOrThrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertThat(ex)
|
||||||
|
.hasMessageContaining("WhitelistData $badData exceeds maximum value!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -219,7 +219,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
// present in the CorDapp.
|
// present in the CorDapp.
|
||||||
val result = scanResult.getClassesWithSuperclass(NotaryService::class) +
|
val result = scanResult.getClassesWithSuperclass(NotaryService::class) +
|
||||||
scanResult.getClassesWithSuperclass(SinglePartyNotaryService::class)
|
scanResult.getClassesWithSuperclass(SinglePartyNotaryService::class)
|
||||||
if(!result.isEmpty()) {
|
if (result.isNotEmpty()) {
|
||||||
logger.info("Found notary service CorDapp implementations: " + result.joinToString(", "))
|
logger.info("Found notary service CorDapp implementations: " + result.joinToString(", "))
|
||||||
}
|
}
|
||||||
return result.firstOrNull()
|
return result.firstOrNull()
|
||||||
|
@ -142,11 +142,7 @@ class DriverDSLImpl(
|
|||||||
private lateinit var _notaries: CordaFuture<List<NotaryHandle>>
|
private lateinit var _notaries: CordaFuture<List<NotaryHandle>>
|
||||||
override val notaryHandles: List<NotaryHandle> get() = _notaries.getOrThrow()
|
override val notaryHandles: List<NotaryHandle> get() = _notaries.getOrThrow()
|
||||||
|
|
||||||
override val cordappsClassLoader: ClassLoader? = if (!startNodesInProcess) {
|
override val cordappsClassLoader: URLClassLoader? = createCordappsClassLoader(cordappsForAllNodes)
|
||||||
createCordappsClassLoader(cordappsForAllNodes)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Waitable {
|
interface Waitable {
|
||||||
@Throws(InterruptedException::class)
|
@Throws(InterruptedException::class)
|
||||||
@ -195,14 +191,15 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun shutdown() {
|
override fun shutdown() {
|
||||||
if (waitForAllNodesToFinish) {
|
cordappsClassLoader.use { _ ->
|
||||||
state.locked {
|
if (waitForAllNodesToFinish) {
|
||||||
processes.forEach { it.waitFor() }
|
state.locked {
|
||||||
|
processes.forEach { it.waitFor() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_shutdownManager?.shutdown()
|
||||||
|
_executorService?.shutdownNow()
|
||||||
}
|
}
|
||||||
_shutdownManager?.shutdown()
|
|
||||||
_executorService?.shutdownNow()
|
|
||||||
(cordappsClassLoader as? AutoCloseable)?.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun establishRpc(config: NodeConfig, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> {
|
private fun establishRpc(config: NodeConfig, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> {
|
||||||
@ -992,7 +989,7 @@ class DriverDSLImpl(
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createCordappsClassLoader(cordapps: Collection<TestCordappInternal>?): ClassLoader? {
|
private fun createCordappsClassLoader(cordapps: Collection<TestCordappInternal>?): URLClassLoader? {
|
||||||
if (cordapps == null || cordapps.isEmpty()) {
|
if (cordapps == null || cordapps.isEmpty()) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -634,6 +634,10 @@ object InteractiveShell {
|
|||||||
InputStreamSerializer.invokeContext = null
|
InputStreamSerializer.invokeContext = null
|
||||||
InputStreamDeserializer.closeAll()
|
InputStreamDeserializer.closeAll()
|
||||||
}
|
}
|
||||||
|
if (cmd == "shutdown") {
|
||||||
|
out.println("Called 'shutdown' on the node.\nQuitting the shell now.").also { out.flush() }
|
||||||
|
onExit.invoke()
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user