From 96134c8cfa5b5c371dd81b6c22eb5e9bc5e327d5 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Tue, 19 Feb 2019 19:42:32 +0100 Subject: [PATCH 001/159] Bugfix: register SwapIdentitiesHandler again. (#4789) --- client/rpc/build.gradle | 4 ---- node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt | 2 ++ .../kotlin/net/corda/node/internal/SwapIdentitiesHandler.kt | 2 +- samples/irs-demo/cordapp/build.gradle | 4 +--- samples/simm-valuation-demo/build.gradle | 2 -- samples/trader-demo/workflows-trader/build.gradle | 1 - 6 files changed, 4 insertions(+), 11 deletions(-) diff --git a/client/rpc/build.gradle b/client/rpc/build.gradle index c30049ee08..5d0eb0a662 100644 --- a/client/rpc/build.gradle +++ b/client/rpc/build.gradle @@ -62,9 +62,6 @@ processSmokeTestResources { from(project(':finance:contracts').tasks['jar']) { rename '.*finance-contracts-.*', 'cordapp-finance-contracts.jar' } - from(project(':confidential-identities').tasks['jar']) { - rename '.*confidential-identities-.*', 'cordapp-confidential-identities.jar' - } } // To find potential version conflicts, run "gradle htmlDependencyReport" and then look in @@ -90,7 +87,6 @@ dependencies { smokeTestCompile project(':smoke-test-utils') smokeTestCompile project(':finance:contracts') smokeTestCompile project(':finance:workflows') - smokeTestCompile project(':confidential-identities') smokeTestCompile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" smokeTestCompile "org.apache.logging.log4j:log4j-core:$log4j_version" smokeTestCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index b1e428e69f..27345da35f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -4,6 +4,7 @@ import com.codahale.metrics.MetricRegistry import com.google.common.collect.MutableClassToInstanceMap import com.google.common.util.concurrent.MoreExecutors import com.zaxxer.hikari.pool.HikariPool +import net.corda.confidential.SwapIdentitiesFlow import net.corda.core.CordaException import net.corda.core.concurrent.CordaFuture import net.corda.core.context.InvocationContext @@ -684,6 +685,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, installFinalityHandler() flowManager.registerInitiatedCoreFlowFactory(NotaryChangeFlow::class, NotaryChangeHandler::class, ::NotaryChangeHandler) flowManager.registerInitiatedCoreFlowFactory(ContractUpgradeFlow.Initiate::class, NotaryChangeHandler::class, ::ContractUpgradeHandler) + flowManager.registerInitiatedCoreFlowFactory(SwapIdentitiesFlow::class, SwapIdentitiesHandler::class, ::SwapIdentitiesHandler) } // Ideally we should be disabling the FinalityHandler if it's not needed, to prevent any party from submitting transactions to us without diff --git a/node/src/main/kotlin/net/corda/node/internal/SwapIdentitiesHandler.kt b/node/src/main/kotlin/net/corda/node/internal/SwapIdentitiesHandler.kt index f1c8aff9f3..3b6b08b307 100644 --- a/node/src/main/kotlin/net/corda/node/internal/SwapIdentitiesHandler.kt +++ b/node/src/main/kotlin/net/corda/node/internal/SwapIdentitiesHandler.kt @@ -12,7 +12,7 @@ import net.corda.core.internal.warnOnce * but it is a complex versioning problem because we don't know which peers we might interact with. Disabling it will probably have to be * gated on a minPlatformVersion bump. */ -@InitiatedBy(SwapIdentitiesFlow::class) +//@InitiatedBy(SwapIdentitiesFlow::class) class SwapIdentitiesHandler(private val otherSide: FlowSession) : FlowLogic() { @Suspendable override fun call() { diff --git a/samples/irs-demo/cordapp/build.gradle b/samples/irs-demo/cordapp/build.gradle index 2891083335..3ffb92b4b4 100644 --- a/samples/irs-demo/cordapp/build.gradle +++ b/samples/irs-demo/cordapp/build.gradle @@ -20,7 +20,7 @@ sourceSets { dependencies { cordapp project(':finance:contracts') cordapp project(':finance:workflows') - cordapp project(':confidential-identities') + // Corda integration dependencies cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaCompile project(':core') @@ -52,7 +52,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask]) } cordapp project(':samples:irs-demo:cordapp:contracts-irs') cordapp project(':samples:irs-demo:cordapp:workflows-irs') - cordapp project(':confidential-identities') } node { name "O=Notary Service,L=Zurich,C=CH" @@ -111,7 +110,6 @@ task prepareDockerNodes(type: net.corda.plugins.Dockerform, dependsOn: ['jar', n nodeDefaults{ cordapp project(':samples:irs-demo:cordapp:contracts-irs') cordapp project(':samples:irs-demo:cordapp:workflows-irs') - cordapp project(':confidential-identities') } node { name "O=Notary Service,L=Zurich,C=CH" diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index a9708fdebd..b7db45d7aa 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -33,7 +33,6 @@ dependencies { cordapp project(':finance:workflows') cordapp project(path: ':samples:simm-valuation-demo:contracts-states', configuration: 'shrinkArtifacts') cordapp project(':samples:simm-valuation-demo:flows') - cordapp project(':confidential-identities') // Corda integration dependencies cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts') @@ -72,7 +71,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, cordapp project(':finance:workflows') cordapp project(':samples:simm-valuation-demo:contracts-states') cordapp project(':samples:simm-valuation-demo:flows') - cordapp project(':confidential-identities') rpcUsers = [['username': "default", 'password': "default", 'permissions': [ 'ALL' ]]] } node { diff --git a/samples/trader-demo/workflows-trader/build.gradle b/samples/trader-demo/workflows-trader/build.gradle index 614a817ea1..05f6cf8178 100644 --- a/samples/trader-demo/workflows-trader/build.gradle +++ b/samples/trader-demo/workflows-trader/build.gradle @@ -24,7 +24,6 @@ dependencies { // The trader demo CorDapp depends upon Cash CorDapp features cordapp project(':finance:contracts') cordapp project(':finance:workflows') - cordapp project(':confidential-identities') cordapp project(':samples:bank-of-corda-demo') // Corda integration dependencies From 6c4433d0b5575db9b25e862408ff94cbc59320ac Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Wed, 20 Feb 2019 11:28:32 +0000 Subject: [PATCH 002/159] CORDA-2646 - Database connection pools leaking memory on every checkpoint (#4773) * ENT-3053 Database connection pools leaking memory on every checkpoint. Flip in the thread local from the thread into the fiber. * Back port to OS (needs some gradle changes) and added TODO, ability for it to avoid erroring if not using Hikari. * Review feedback to remove warning. --- build.gradle | 1 + node-api/build.gradle | 3 +++ .../internal/persistence/CordaPersistence.kt | 24 ++++++++++++++++++- node/build.gradle | 2 +- .../node/services/statemachine/FiberUtils.kt | 15 ++++++++++++ .../statemachine/FlowStateMachineImpl.kt | 15 ++++++++++-- 6 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 node/src/main/kotlin/net/corda/node/services/statemachine/FiberUtils.kt diff --git a/build.gradle b/build.gradle index 3fe642542e..1850c8ff6f 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,7 @@ buildscript { ext.jsr305_version = constants.getProperty("jsr305Version") ext.shiro_version = '1.4.0' ext.artifactory_plugin_version = constants.getProperty('artifactoryPluginVersion') + ext.hikari_version = '2.5.1' ext.liquibase_version = '3.5.5' ext.artifactory_contextUrl = 'https://ci-artifactory.corda.r3cev.com/artifactory' ext.snake_yaml_version = constants.getProperty('snakeYamlVersion') diff --git a/node-api/build.gradle b/node-api/build.gradle index 0f2c1a8c3e..5aee94f5b8 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -27,6 +27,9 @@ dependencies { compile "org.apache.qpid:proton-j:$protonj_version" + // SQL connection pooling library + compile "com.zaxxer:HikariCP:$hikari_version" + // ClassGraph: classpath scanning compile "io.github.classgraph:classgraph:$class_graph_version" diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt index 751805d315..ab039bf05f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt @@ -1,6 +1,9 @@ package net.corda.nodeapi.internal.persistence import co.paralleluniverse.strands.Strand +import com.zaxxer.hikari.HikariDataSource +import com.zaxxer.hikari.pool.HikariPool +import com.zaxxer.hikari.util.ConcurrentBag import net.corda.core.internal.NamedCacheFactory import net.corda.core.schemas.MappedSchema import net.corda.core.utilities.contextLogger @@ -9,9 +12,10 @@ import rx.Observable import rx.Subscriber import rx.subjects.UnicastSubject import java.io.Closeable +import java.lang.reflect.Field import java.sql.Connection import java.sql.SQLException -import java.util.UUID +import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.atomic.AtomicInteger @@ -254,6 +258,24 @@ class CordaPersistence( // DataSource doesn't implement AutoCloseable so we just have to hope that the implementation does so that we can close it (_dataSource as? AutoCloseable)?.close() } + + val hikariPoolThreadLocal: ThreadLocal>? by lazy(LazyThreadSafetyMode.PUBLICATION) { + val hikariDataSource = dataSource as? HikariDataSource + if (hikariDataSource == null) { + null + } else { + val poolField: Field = HikariDataSource::class.java.getDeclaredField("pool") + poolField.isAccessible = true + val pool: HikariPool = poolField.get(hikariDataSource) as HikariPool + val connectionBagField: Field = HikariPool::class.java.getDeclaredField("connectionBag") + connectionBagField.isAccessible = true + val connectionBag: ConcurrentBag = connectionBagField.get(pool) as ConcurrentBag + val threadListField: Field = ConcurrentBag::class.java.getDeclaredField("threadList") + threadListField.isAccessible = true + val threadList: ThreadLocal> = threadListField.get(connectionBag) as ThreadLocal> + threadList + } + } } /** diff --git a/node/build.gradle b/node/build.gradle index a4088e80c2..56dc13295a 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -145,7 +145,7 @@ dependencies { compile "org.postgresql:postgresql:$postgresql_version" // SQL connection pooling library - compile "com.zaxxer:HikariCP:2.5.1" + compile "com.zaxxer:HikariCP:${hikari_version}" // Hibernate: an object relational mapper for writing state objects to the database automatically. compile "org.hibernate:hibernate-core:$hibernate_version" diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FiberUtils.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FiberUtils.kt new file mode 100644 index 0000000000..ee50b52bd7 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FiberUtils.kt @@ -0,0 +1,15 @@ +package net.corda.node.services.statemachine + +import co.paralleluniverse.concurrent.util.ThreadAccess +import co.paralleluniverse.fibers.Fiber +import java.lang.reflect.Field + +private val fiberThreadLocalsField: Field = Fiber::class.java.getDeclaredField("fiberLocals").apply { this.isAccessible = true } + +private fun Fiber.swappedOutThreadLocals(): Any = fiberThreadLocalsField.get(this) + +// TODO: This method uses a built-in Quasar function to make a map of all ThreadLocals. This is probably inefficient, but the only API readily available. +fun Fiber.swappedOutThreadLocalValue(threadLocal: ThreadLocal): T? { + val threadLocals = swappedOutThreadLocals() + return ThreadAccess.toMap(threadLocals)[threadLocal] as T? +} diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index 1284b45f7e..254f530df8 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -156,7 +156,10 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, } val continuation = processEvent(transitionExecutor, nextEvent) when (continuation) { - is FlowContinuation.Resume -> return continuation.result + is FlowContinuation.Resume -> { + openThreadLocalWormhole() + return continuation.result + } is FlowContinuation.Throw -> { continuation.throwable.fillInStackTrace() throw continuation.throwable @@ -208,13 +211,21 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, MDC.put("thread-id", Thread.currentThread().id.toString()) } + private fun openThreadLocalWormhole() { + val threadLocal = getTransientField(TransientValues::database).hikariPoolThreadLocal + if (threadLocal != null) { + val valueFromThread = swappedOutThreadLocalValue(threadLocal) + if (valueFromThread != null) threadLocal.set(valueFromThread) + } + } + @Suspendable override fun run() { logic.progressTracker?.currentStep = ProgressTracker.STARTING logic.stateMachine = this + openThreadLocalWormhole() setLoggingContext() - initialiseFlow() logger.debug { "Calling flow: $logic" } From d86ef26e68187c4a5fb5b1879abb4406a291e471 Mon Sep 17 00:00:00 2001 From: carolynequinn <44175553+carolynequinn@users.noreply.github.com> Date: Mon, 18 Feb 2019 10:56:44 +0000 Subject: [PATCH 003/159] Update index.rst Updating the casing from uat.md to UAT.md, as it wasn't linking with the lower case (thanks to @farm !) --- docs/source/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index bbbf636ae6..ea29c53842 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -69,7 +69,7 @@ We look forward to seeing what you can do with Corda! :if_tag: htmlmode corda-network/index.md - corda-network/uat.md + corda-network/UAT.md .. conditional-toctree:: :caption: Contents From 0da07c43719968922530928e49d0b87d8ceb709d Mon Sep 17 00:00:00 2001 From: Richard G Brown Date: Sat, 16 Feb 2019 16:08:47 +0000 Subject: [PATCH 004/159] really minor tweaks to release notes --- docs/source/release-notes.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 28849fe297..84b82a18a1 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -3,7 +3,7 @@ Release notes for Corda 4 .. _release_notes_v4_0: -Here we are, 9 months and 1500 plus commits later... and it's a bouncing baby software release! +Here we are, 11 months and 1500 plus commits later... and it's a bouncing baby software release! We are really proud to release Corda 4 to the open source community today. It's been a long time in the making, but we think you'll agree worth the wait. @@ -29,9 +29,9 @@ Reference states ++++++++++++++++ With Corda 4 we are introducing the concept of "reference input states". These allow smart contracts -to read data from the ledger without simultaneously updating it. They're useful not only for any kind of -reference data such as rates, healthcare codes, geographical information etc, but for anywhere -you might have used a SELECT JOIN in a SQL based app. +to reference data from the ledger in a transaction without simultaneously updating it. They're useful +not only for any kind of reference data such as rates, healthcare codes, geographical information etc, +but for anywhere you might have used a SELECT JOIN in a SQL based app. A reference input state is a ``ContractState`` which can be referred to in a transaction by the contracts of input and output states but, significantly, whose contract is not executed as part of the transaction @@ -297,4 +297,4 @@ Miscellaneous changes To learn more about smaller changes, please read the :doc:`changelog`. -Finally, we have added some new jokes. Thankyou and good night! +Finally, we have added some new jokes. Thank you and good night! From 7979c2dfef9e51abfb9a4c5e0c1a40c3a332f085 Mon Sep 17 00:00:00 2001 From: Richard G Brown Date: Sat, 16 Feb 2019 16:51:59 +0000 Subject: [PATCH 005/159] really minor tweaks to release notes --- docs/source/quickstart-index.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/source/quickstart-index.rst b/docs/source/quickstart-index.rst index c20b4ed10b..354d25fb4f 100644 --- a/docs/source/quickstart-index.rst +++ b/docs/source/quickstart-index.rst @@ -103,5 +103,9 @@ Add a node to an existing production network -------------------------------------------- +---------------------------------------------------------------------------------------------------------+ -| Contact R3 Solutions Engineering at support@r3.com | +| Corda Network is a global production network of Corda nodes, operated by the independent | +| Corda Network Foundation. You can learn more here: https://corda.network/ | ++---------------------------------------------------------------------------------------------------------+ +| Corda Testnet is a test network, operated for the community by R3. You can learn | +| more here: https://testnet.corda.network | +---------------------------------------------------------------------------------------------------------+ From c4c0769763acf3bec3bd5c37180d4e8aae8e6555 Mon Sep 17 00:00:00 2001 From: Richard Gendal Brown Date: Sun, 17 Feb 2019 19:06:42 +0000 Subject: [PATCH 006/159] changed corda network url --- docs/source/quickstart-index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/quickstart-index.rst b/docs/source/quickstart-index.rst index 354d25fb4f..af01a37576 100644 --- a/docs/source/quickstart-index.rst +++ b/docs/source/quickstart-index.rst @@ -104,7 +104,7 @@ Add a node to an existing production network +---------------------------------------------------------------------------------------------------------+ | Corda Network is a global production network of Corda nodes, operated by the independent | -| Corda Network Foundation. You can learn more here: https://corda.network/ | +| Corda Network Foundation. You can learn more here: https://corda.network/participation/index.html | +---------------------------------------------------------------------------------------------------------+ | Corda Testnet is a test network, operated for the community by R3. You can learn | | more here: https://testnet.corda.network | From 4afa28bc7784787ef74bd37b0a5aa75e45796214 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 20 Feb 2019 20:37:08 +0100 Subject: [PATCH 007/159] Docs: fix changelog for C4 --- docs/source/changelog.rst | 73 +++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 81c6ed829b..e77427bddc 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -4,32 +4,6 @@ Changelog Here's a summary of what's changed in each Corda release. For guidance on how to upgrade code from the previous release, see :doc:`app-upgrade-notes`. -Unreleased ----------- - -* Updating postgres dependency to 42.2.5 - -* Test ``CordaService`` s can be installed on mock nodes using ``UnstartedMockNode.installCordaService``. - -* The finance-contracts demo CorDapp has been slimmed down to contain only that which is relevant for contract verification. Everything else - has been moved to the finance-workflows CorDapp: - - * The cash selection logic. ``AbstractCashSelection`` is now in net.corda.finance.contracts.asset so any custom implementations must now be - defined in ``META-INF/services/net.corda.finance.workflows.asset.selection.AbstractCashSelection``. - - * The jackson annotations on ``Expression`` have been removed. You will need to use ``FinanceJSONSupport.registerFinanceJSONMappers`` if - you wish to preserve the JSON format for this class. - - * The various utility methods defined in ``Cash`` for creating cash transactions have been moved to ``net.corda.finance.workflows.asset.CashUtils``. - Similarly with ``CommercialPaperUtils`` and ``ObligationUtils``. - - * Various other utilities such as ``GetBalances` and the test calendar data. - - The only exception to this is ``Interpolator`` and related classes. These are now in the `IRS demo workflows CorDapp `_. - -* Vault states are now correctly migrated when moving from V3 to V4. In particular, this means the relevancy column is correctly filled, and the state party table is populated. - Note: This means Corda can be slow to start up for the first time after upgrading from V3 to V4. - .. _changelog_v4.0: Version 4.0 @@ -43,10 +17,10 @@ Version 4.0 retries have been disabled for single node notaries since in this case they offer no potential benefits, unlike for a notary cluster with several members who might have different availability. -* New configuration property `database.initialiseAppSchema` with values `UPDATE`, `VALIDATE` and `NONE`. - The property controls the behavior of the Hibernate DDL generation. `UPDATE` performs an update of CorDapp schemas, while - `VALID` only verifies their integrity. The property does not affect the node-specific DDL handling and - complements `database.initialiseSchema` to disable DDL handling altogether. +* New configuration property ``database.initialiseAppSchema`` with values ``UPDATE``, ``VALIDATE`` and ``NONE``. + The property controls the behavior of the Hibernate DDL generation. ``UPDATE`` performs an update of CorDapp schemas, while + ``VALID`` only verifies their integrity. The property does not affect the node-specific DDL handling and + complements ``database.initialiseSchema`` to disable DDL handling altogether. * ``JacksonSupport.createInMemoryMapper`` was incorrectly marked as deprecated and is no longer so. @@ -72,11 +46,11 @@ Version 4.0 * Fixed a problem with IRS demo not being able to simulate future dates as expected (https://github.com/corda/corda/issues/3851). -* Fixed a problem that was preventing `Cash.generateSpend` to be used more than once per transaction (https://github.com/corda/corda/issues/4110). +* Fixed a problem that was preventing ``Cash.generateSpend`` to be used more than once per transaction (https://github.com/corda/corda/issues/4110). * Fixed a bug resulting in poor vault query performance and incorrect results when sorting. -* Improved exception thrown by `AttachmentsClassLoader` when an attachment cannot be used because its uploader is not trusted. +* Improved exception thrown by ``AttachmentsClassLoader`` when an attachment cannot be used because its uploader is not trusted. * Fixed deadlocks generated by starting flow from within CordaServices. @@ -98,7 +72,7 @@ Version 4.0 * BFT-Smart and Raft notary implementations have been moved to the ``net.corda.notary.experimental`` package to emphasise their experimental nature. Note that it is not possible to preserve the state for both types of notaries when upgrading from V3 or an earlier Corda version. -* New "validate-configuration" sub-command to `corda.jar`, allowing to validate the actual node configuration without starting the node. +* New "validate-configuration" sub-command to ``corda.jar``, allowing to validate the actual node configuration without starting the node. * CorDapps now have the ability to specify a minimum platform version in their MANIFEST.MF to prevent old nodes from loading them. @@ -292,7 +266,7 @@ Version 4.0 In case your Cordapps use this entity class to persist data in own custom tables as non Primary Key columns refer to :doc:`app-upgrade-notes` for upgrade instructions. -* Adding a public method to check if a public key satisfies Corda recommended algorithm specs, `Crypto.validatePublicKey(java.security.PublicKey)`. +* Adding a public method to check if a public key satisfies Corda recommended algorithm specs, ``Crypto.validatePublicKey(java.security.PublicKey)``. For instance, this method will check if an ECC key lies on a valid curve or if an RSA key is >= 2048bits. This might be required for extra key validation checks, e.g., for Doorman to check that a CSR key meets the minimum security requirements. @@ -316,14 +290,14 @@ Version 4.0 required to filter by object type. Thus, when one wants to filter-in all "reference input states" in a ``FilteredTransaction`` then he/she should check if it is of type ``ReferenceStateRef``. -* Removed type parameter `U` from `tryLockFungibleStatesForSpending` to allow the function to be used with `FungibleState` - as well as `FungibleAsset`. This _might_ cause a compile failure in some obscure cases due to the removal of the type +* Removed type parameter ``U`` from ``tryLockFungibleStatesForSpending`` to allow the function to be used with ``FungibleState`` + as well as ``FungibleAsset``. This _might_ cause a compile failure in some obscure cases due to the removal of the type parameter from the method. If your CorDapp does specify types explicitly when using this method then updating the types will allow your app to compile successfully. However, those using type inference (e.g. using Kotlin) should not experience any changes. Old CorDapp JARs will still work regardless. -* `issuer_ref` column in `FungibleStateSchema` was updated to be nullable to support the introduction of the - `FungibleState` interface. The `vault_fungible_states` table can hold both `FungibleAssets` and `FungibleStates`. +* ``issuer_ref`` column in ``FungibleStateSchema`` was updated to be nullable to support the introduction of the + ``FungibleState`` interface. The ``vault_fungible_states`` table can hold both ``FungibleAssets`` and ``FungibleStates``. * CorDapps built by ``corda-gradle-plugins`` are now signed and sealed JAR files. Signing can be configured or disabled, and it defaults to using the Corda development certificate. @@ -345,6 +319,29 @@ Version 4.0 * Vault Query Criteria have been enhanced to allow filtering by state relevancy. Queries can request all states, just relevant ones, or just non relevant ones. The default is to return all states, to maintain backwards compatibility. Note that this means apps running on nodes using Observer node functionality should update their queries to request only relevant states if they are only expecting to see states in which they participate. +* Postgres dependency was updated to version 42.2.5 + +* Test ``CordaService`` s can be installed on mock nodes using ``UnstartedMockNode.installCordaService``. + +* The finance-contracts demo CorDapp has been slimmed down to contain only that which is relevant for contract verification. Everything else + has been moved to the finance-workflows CorDapp: + + * The cash selection logic. ``AbstractCashSelection`` is now in net.corda.finance.contracts.asset so any custom implementations must now be + defined in ``META-INF/services/net.corda.finance.workflows.asset.selection.AbstractCashSelection``. + + * The jackson annotations on ``Expression`` have been removed. You will need to use ``FinanceJSONSupport.registerFinanceJSONMappers`` if + you wish to preserve the JSON format for this class. + + * The various utility methods defined in ``Cash`` for creating cash transactions have been moved to ``net.corda.finance.workflows.asset.CashUtils``. + Similarly with ``CommercialPaperUtils`` and ``ObligationUtils``. + + * Various other utilities such as ``GetBalances`` and the test calendar data. + + The only exception to this is ``Interpolator`` and related classes. These are now in the `IRS demo workflows CorDapp `_. + +* Vault states are now correctly migrated when moving from V3 to V4. In particular, this means the relevancy column is correctly filled, and the state party table is populated. + Note: This means Corda can be slow to start up for the first time after upgrading from V3 to V4. + Version 3.3 ----------- From f932fa591d1dc05db20a4544a17da155591fd995 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 20 Feb 2019 20:37:58 +0100 Subject: [PATCH 008/159] Docs: introduce a java_version substitution --- docs/source/conf.py | 32 +++++++++++--------------------- docs/source/getting-set-up.rst | 4 ++-- docs/source/release-notes.rst | 4 ++-- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 961bb45bb6..0516f279ed 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,20 +1,19 @@ # -*- coding: utf-8 -*- -# -# R3 prototyping documentation build configuration file, created by -# sphinx-quickstart on Mon Nov 23 21:19:35 2015. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. import sphinx_rtd_theme import sys, os +############################################################################ +# +# TEXT SUBSTITUTIONS + +rst_epilog = """ +.. |java_version| replace:: 8u171 +.. |kotlin_version| replace:: 1.2.71 +""" + +############################################################################ + sys.path.append(os.path.abspath('../ext/')) # If extensions (or modules to document with autodoc) are in another directory, @@ -66,15 +65,6 @@ release = 'Master' # The version for use in the dropdown html. html_context = {'version': 'Master'} -# Properties. -kotlin_version = '1.2.71' - -rst_epilog = """ -.. |kotlin_version| replace:: {kotlin_version} -""".format( - kotlin_version = kotlin_version, -) - # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # diff --git a/docs/source/getting-set-up.rst b/docs/source/getting-set-up.rst index e6c2c27d52..1931cdf1dd 100644 --- a/docs/source/getting-set-up.rst +++ b/docs/source/getting-set-up.rst @@ -5,7 +5,7 @@ Software requirements --------------------- Corda uses industry-standard tools: -* **Oracle JDK 8 JVM** - minimum supported version **8u171** +* **Oracle JDK 8 JVM** - minimum supported version **|java_version|** (please note that * **IntelliJ IDEA** - supported versions **2017.x** and **2018.x** (with Kotlin plugin version |kotlin_version|) * **Git** @@ -26,7 +26,7 @@ Please note: `getting started guide `_, and a series of `Kotlin Koans `_ -* IntelliJ IDEA is recommended due to the strength of its Kotlin integration +* IntelliJ IDEA is recommended due to the strength of its Kotlin integration. Following these software recommendations will minimize the number of errors you encounter, and make it easier for others to provide support. However, if you do use other tools, we'd be interested to hear about any issues that arise. diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 84b82a18a1..d29bc146b2 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -211,9 +211,9 @@ version requirement if they start using new features and APIs. Dependency upgrades +++++++++++++++++++ -We've raised the minimum JDK to 8u171, needed to get fixes for certain ZIP compression bugs. +We've raised the minimum JDK to |java_version|, needed to get fixes for certain ZIP compression bugs. -We've upgraded to Kotlin 1.2.71 so your apps can now benefit from the new features in this language release. +We've upgraded to Kotlin |kotlin_version| so your apps can now benefit from the new features in this language release. We've upgraded to Gradle 4.10.1. From 4dee36adc7d310532010c2bc8c14303c15e8db30 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 20 Feb 2019 20:38:27 +0100 Subject: [PATCH 009/159] Docs: introduce a page about upgrading the node. --- docs/source/index.rst | 1 + docs/source/key-concepts-node.rst | 10 +++-- docs/source/node-administration.rst | 4 +- docs/source/node-upgrade-notes.rst | 63 +++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 docs/source/node-upgrade-notes.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index ea29c53842..ac35a8d50d 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -33,6 +33,7 @@ We look forward to seeing what you can do with Corda! release-notes app-upgrade-notes + node-upgrade-notes .. toctree:: :caption: Development diff --git a/docs/source/key-concepts-node.rst b/docs/source/key-concepts-node.rst index 7e479060cb..72663ddfdc 100644 --- a/docs/source/key-concepts-node.rst +++ b/docs/source/key-concepts-node.rst @@ -81,11 +81,13 @@ The node also has several CorDapps installed by default to handle common tasks s * Upgrading contracts * Broadcasting agreed ledger updates for recording by counterparties +.. _draining-mode: + Draining mode -^^^^^^^^^^^^^ +------------- In order to operate a clean shutdown of a node, it is important than no flows are in-flight, meaning no checkpoints should -be persisted. The node is able to be put in a Flows Draining Mode, during which: +be persisted. The node is able to be put in draining mode, during which: * Commands requiring to start new flows through RPC will be rejected. * Scheduled flows due will be ignored. @@ -93,4 +95,6 @@ be persisted. The node is able to be put in a Flows Draining Mode, during which: * All other activities will proceed as usual, ensuring that the number of in-flight flows will strictly diminish. As their number - which can be monitored through RPC - reaches zero, it is safe to shut the node down. -This property is durable, meaning that restarting the node will not reset it to its default value and that a RPC command is required. \ No newline at end of file +This property is durable, meaning that restarting the node will not reset it to its default value and that a RPC command is required. + +The node can be safely shut down via a drain using the shell. \ No newline at end of file diff --git a/docs/source/node-administration.rst b/docs/source/node-administration.rst index 9a978d1477..3f58bc7dea 100644 --- a/docs/source/node-administration.rst +++ b/docs/source/node-administration.rst @@ -128,7 +128,7 @@ due to expensive run-time costs. They can be turned on and off explicitly regard When starting Corda nodes using Cordformation runner (see :doc:`running-a-node`), you should see a startup message similar to the following: **Jolokia: Agent started with URL http://127.0.0.1:7005/jolokia/** -When starting Corda nodes using the `DriverDSL`, you should see a startup message in the logs similar to the following: +When starting Corda nodes using the 'driver DSL', you should see a startup message in the logs similar to the following: **Starting out-of-process Node USA Bank Corp, debug port is not enabled, jolokia monitoring port is 7005 {}** @@ -203,6 +203,8 @@ For launching on Windows without PowerShell, it is not possible to perform comma .. warning:: If this approach is taken, the passwords will appear in the windows command prompt history. +.. _ref-backup-recommendations: + Backup recommendations ---------------------- diff --git a/docs/source/node-upgrade-notes.rst b/docs/source/node-upgrade-notes.rst new file mode 100644 index 0000000000..24148562d3 --- /dev/null +++ b/docs/source/node-upgrade-notes.rst @@ -0,0 +1,63 @@ +Upgrading your node to Corda 4 +============================== + +Corda releases strive to be backwards compatible, so upgrading a node is fairly straightforward and should not require changes to +applications. It consists of the following steps: + +1. Drain the node. +2. Make a backup of your node directories and/or database. +3. Replace the ``corda.jar`` file with the new version. +4. Start up the node. This step may incur a delay whilst any needed database migrations are applied. +5. Undrain it to re-enable processing of new inbound flows. + +The protocol is designed to tolerate node outages, so during the upgrade process peers on the network will wait for your node to come back. + +Step 1. Drain the node +---------------------- + +Before a node or application on it can be upgraded, the node must be put in :ref:`draining-mode`. This brings the currently running +:doc:`key-concepts-flows` to a smooth halt such that existing work is finished and new work is queuing up rather than being processed. + +Draining flows is a key task for node administrators to perform. It exists to simplify applications by ensuring apps don't have to be +able to migrate workflows from any arbitrary point to other arbitrary points, a task that would rapidly become infeasible as workflow +and protocol complexity increases. + +To drain the node, run the ``gracefulShutdown`` command. This will wait for the node to drain and then shut down the node when the drain +is complete. + +.. warning:: The length of time a node takes to drain depends on both how your applications are designed, and whether any apps are currently + talking to network peers that are offline or slow to respond. It is thus hard to give guidance on how long a drain should take, but in + an environment with well written apps and in which your counterparties are online, drains may need only a few seconds. + +Step 2. Make a backup of your node directories and/or database +-------------------------------------------------------------- + +It's always a good idea to make a backup of your data before upgrading any server. This will make it easy to roll back if there's a problem. +You can simply make a copy of the node's data directory to enable this. If you use an external non-H2 database please consult your database +user guide to learn how to make backups. + +We provide some `backup recommendations `_ if you'd like more detail. + +Step 3. Replace ``corda.jar`` with the new version +-------------------------------------------------- + +Download the latest version of Corda from `our Artifactory site `_. +Make sure it's available on your path, and that you've read the :doc:`release-notes`, in particular to discover what version of Java this +node requires. + +.. important:: Corda 4 requires Java |java_version| or any higher Java 8 patchlevel. Java 9+ is not currently supported. + +Step 4. Start up the node +------------------------- + +Start the node in the usual manner you have selected. The node will perform any automatic data migrations required, which may take some +time. If the migration process is interrupted it can be continued simply by starting the node again, without harm. + +Step 5. Undrain the node +------------------------ + +You may now do any checks that you wish to perform, read the logs, and so on. When you are ready, use this command at the shell: + +``run setFlowsDrainingModeEnabled enabled: false`` + +Your upgrade is complete. \ No newline at end of file From 4ddaad5cefe672f7933cfbb485fa7063cfceeb04 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 20 Feb 2019 20:55:49 +0100 Subject: [PATCH 010/159] Docs: truncate the changelog to remove obsolete releases. --- docs/source/changelog.rst | 1512 +------------------------------------ 1 file changed, 1 insertion(+), 1511 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index e77427bddc..c9f4247900 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -340,1514 +340,4 @@ Version 4.0 The only exception to this is ``Interpolator`` and related classes. These are now in the `IRS demo workflows CorDapp `_. * Vault states are now correctly migrated when moving from V3 to V4. In particular, this means the relevancy column is correctly filled, and the state party table is populated. - Note: This means Corda can be slow to start up for the first time after upgrading from V3 to V4. - -Version 3.3 ------------ - -* Vault query fix: support query by parent classes of Contract State classes (see https://github.com/corda/corda/issues/3714) - -* Fixed an issue preventing Shell from returning control to the user when CTRL+C is pressed in the terminal. - -* Fixed a problem that sometimes prevented nodes from starting in presence of custom state types in the database without a corresponding type from installed CorDapps. - -* Introduced a grace period before the initial node registration fails if the node cannot connect to the Doorman. - It retries 10 times with a 1 minute interval in between each try. At the moment this is not configurable. - -* Fixed an error thrown by NodeVaultService upon recording a transaction with a number of inputs greater than the default page size. - -* Changes to the JSON/YAML serialisation format from ``JacksonSupport``, which also applies to the node shell: - - * ``Instant`` and ``Date`` objects are serialised as ISO-8601 formatted strings rather than timestamps - * ``PublicKey`` objects are serialised and looked up according to their Base58 encoded string - * ``Party`` objects can be deserialised by looking up their public key, in addition to their name - * ``NodeInfo`` objects are serialised as an object and can be looked up using the same mechanism as ``Party`` - * ``NetworkHostAndPort`` serialised according to its ``toString()`` - * ``PartyAndCertificate`` is serialised as the name - * ``SerializedBytes`` is serialised by materialising the bytes into the object it represents, and then serialising that - object into YAML/JSON - * ``X509Certificate`` is serialised as an object with key fields such as ``issuer``, ``publicKey``, ``serialNumber``, etc. - The encoded bytes are also serialised into the ``encoded`` field. This can be used to deserialise an ``X509Certificate`` - back. - * ``CertPath`` objects are serialised as a list of ``X509Certificate`` objects. - -* ``fullParties`` boolean parameter added to ``JacksonSupport.createDefaultMapper`` and ``createNonRpcMapper``. If ``true`` - then ``Party`` objects are serialised as JSON objects with the ``name`` and ``owningKey`` fields. For ``PartyAndCertificate`` - the ``certPath`` is serialised. - -* Several members of ``JacksonSupport`` have been deprecated to highlight that they are internal and not to be used - -* ``ServiceHub`` and ``CordaRPCOps`` can now safely be used from multiple threads without incurring in database transaction problems. - -* Fixed an issue preventing out of process nodes started by the ``Driver`` from logging to file. - -* The Vault Criteria API has been extended to take a more precise specification of which class contains a field. This primarily impacts Java users; Kotlin users need take no action. The old methods have been deprecated but still work - the new methods avoid bugs that can occur when JPA schemas inherit from each other. - -* Removed -xmx VM argument from Explorer's Capsule setup. This helps avoiding out of memory errors. - -* Node will now gracefully fail to start if one of the required ports is already in use. - -* Fixed incorrect exception handling in ``NodeVaultService._query()``. - -* Avoided a memory leak deriving from incorrect MappedSchema caching strategy. - -* Fix CORDA-1403 where a property of a class that implemented a generic interface could not be deserialised in - a factory without a serialiser as the subtype check for the class instance failed. Fix is to compare the raw - type. - -* Fix CORDA-1229. Setter-based serialization was broken with generic types when the property was stored - as the raw type, List for example. - -.. _changelog_v3.2: - -Version 3.2 ------------ - -* Doorman and NetworkMap URLs can now be configured individually rather than being assumed to be - the same server. Current ``compatibilityZoneURL`` configurations remain valid. See both :doc:`corda-configuration-file` - and :doc:`permissioning` for details. - -* Table name with a typo changed from ``NODE_ATTCHMENTS_CONTRACTS`` to ``NODE_ATTACHMENTS_CONTRACTS``. - -.. _changelog_v3.1: - -Version 3.1 ------------ - -* Update the fast-classpath-scanner dependent library version from 2.0.21 to 2.12.3 - - .. note:: Whilst this is not the latest version of this library, that being 2.18.1 at time of writing, versions - later than 2.12.3 (including 2.12.4) exhibit a different issue. - -* Updated the api scanner gradle plugin to work the same way as the version in master. These changes make the api scanner more - accurate and fix a couple of bugs, and change the format of the api-current.txt file slightly. Backporting these changes - to the v3 branch will make it easier for us to ensure that apis are stable for future versions. These changes are - released in gradle plugins version 3.0.10. For more information on the api scanner see - the `documentation `_. - -* Fixed security vulnerability when using the ``HashAttachmentConstraint``. Added strict check that the contract JARs - referenced in a transaction were deployed on the node. - -* Fixed node's behaviour on startup when there is no connectivity to network map. Node continues to work normally if it has - all the needed network data, waiting in the background for network map to become available. - -.. _changelog_v3: - -Version 3.0 ------------ - -* Due to a security risk, the `conflict` property has been removed from `NotaryError.Conflict` error object. It has been replaced - with `consumedStates` instead. The new property no longer specifies the original requesting party and transaction id for - a consumed state. Instead, only the hash of the transaction id is revealed. For more details why this change had to be - made please refer to the release notes. - -* Added ``NetworkMapCache.getNodesByLegalName`` for querying nodes belonging to a distributed service such as a notary cluster - where they all share a common identity. ``NetworkMapCache.getNodeByLegalName`` has been tightened to throw if more than - one node with the legal name is found. - -* Introduced Flow Draining mode, in which a node continues executing existing flows, but does not start new. This is to support graceful node shutdown/restarts. - In particular, when this mode is on, new flows through RPC will be rejected, scheduled flows will be ignored, and initial session messages will not be consumed. - This will ensure that the number of checkpoints will strictly diminish with time, allowing for a clean shutdown. - -* Removed blacklisted word checks in Corda X.500 name to allow "Server" or "Node" to be use as part of the legal name. - -* Separated our pre-existing Artemis broker into an RPC broker and a P2P broker. - -* Refactored ``NodeConfiguration`` to expose ``NodeRpcOptions`` (using top-level "rpcAddress" property still works with warning). - -* Modified ``CordaRPCClient`` constructor to take a ``SSLConfiguration?`` additional parameter, defaulted to ``null``. - -* Introduced ``CertificateChainCheckPolicy.UsernameMustMatchCommonName`` sub-type, allowing customers to optionally enforce - username == CN condition on RPC SSL certificates. - -* Modified ``DriverDSL`` and sub-types to allow specifying RPC settings for the Node. - -* Modified the ``DriverDSL`` to start Cordformation nodes allowing automatic generation of "rpcSettings.adminAddress" in case - "rcpSettings.useSsl" is ``false`` (the default). - -* Introduced ``UnsafeCertificatesFactory`` allowing programmatic generation of X509 certificates for test purposes. - -* JPA Mapping annotations for States extending ``CommonSchemaV1.LinearState`` and ``CommonSchemaV1.FungibleState`` on the - `participants` collection need to be moved to the actual class. This allows to properly specify the unique table name per - a collection. See: DummyDealStateSchemaV1.PersistentDummyDealState -* Database schema changes - an H2 database instance of Corda 1.0 and 2.0 cannot be reused for Corda 3.0, listed changes for Vault and Finance module: - - * ``NODE_TRANSACTIONS``: - column ``"TRANSACTION”`` renamed to ``TRANSACTION_VALUE``, serialization format of BLOB stored in the column has changed to AMQP - * ``VAULT_STATES``: - column ``CONTRACT_STATE`` removed - * ``VAULT_FUNGIBLE_STATES``: - column ``ISSUER_REFERENCE`` renamed to ``ISSUER_REF`` and the field size increased - * ``"VAULTSCHEMAV1$VAULTFUNGIBLESTATES_PARTICIPANTS"``: - table renamed to ``VAULT_FUNGIBLE_STATES_PARTS``, - column ``"VAULTSCHEMAV1$VAULTFUNGIBLESTATES_OUTPUT_INDEX"`` renamed to ``OUTPUT_INDEX``, - column ``"VAULTSCHEMAV1$VAULTFUNGIBLESTATES_TRANSACTION_ID"`` renamed to ``TRANSACTION_ID`` - * ``VAULT_LINEAR_STATES``: - type of column ``"UUID"`` changed from ``VARBINARY`` to ``VARCHAR(255)`` - select varbinary column as ``CAST("UUID" AS UUID)`` to get UUID in varchar format - * ``"VAULTSCHEMAV1$VAULTLINEARSTATES_PARTICIPANTS"``: - table renamed to ``VAULT_LINEAR_STATES_PARTS``, - column ``"VAULTSCHEMAV1$VAULTLINEARSTATES_OUTPUT_INDEX"`` renamed to ``OUTPUT_INDEX``, - column ``"VAULTSCHEMAV1$VAULTLINEARSTATES_TRANSACTION_ID"`` renamed to ``TRANSACTION_ID`` - * ``contract_cash_states``: - columns storing Base58 representation of the serialised public key (e.g. ``issuer_key``) were changed to store Base58 representation of SHA-256 of public key prefixed with `DL` - * ``contract_cp_states``: - table renamed to ``cp_states``, column changes as for ``contract_cash_states`` - -* X.509 certificates now have an extension that specifies the Corda role the certificate is used for, and the role - hierarchy is now enforced in the validation code. See ``net.corda.core.internal.CertRole`` for the current implementation - until final documentation is prepared. Certificates at ``NODE_CA``, ``WELL_KNOWN_SERVICE_IDENTITY`` and above must - only ever by issued by network services and therefore issuance constraints are not relevant to end users. - The ``TLS``, ``WELL_KNOWN_LEGAL_IDENTITY`` roles must be issued by the ``NODE_CA`` certificate issued by the - Doorman, and ``CONFIDENTIAL_IDENTITY`` certificates must be issued from a ``WELL_KNOWN_LEGAL_IDENTITY`` certificate. - For a detailed specification of the extension please see :doc:`permissioning`. - -* The network map service concept has been re-designed. More information can be found in :doc:`network-map`. - - * The previous design was never intended to be final but was rather a quick implementation in the earliest days of the - Corda project to unblock higher priority items. It suffers from numerous disadvantages including lack of scalability, - as one node is expected to hold open and manage connections to every node on the network; not reliable; hard to defend - against DoS attacks; etc. - - * There is no longer a special network map node for distributing the network map to the other nodes. Instead the network - map is now a collection of signed ``NodeInfo`` files distributed via HTTP. - - * The ``certificateSigningService`` config has been replaced by ``compatibilityZoneURL`` which is the base URL for the - doorman registration and for downloading the network map. There is also an end-point for the node to publish its node-info - object, which the node does each time it changes. ``networkMapService`` config has been removed. - - * To support local and test deployments, the node polls the ``additional-node-infos`` directory for these signed ``NodeInfo`` - objects which are stored in its local cache. On startup the node generates its own signed file with the filename format - "nodeInfo-\*". This can be copied to every node's ``additional-node-infos`` directory that is part of the network. - - * Cordform (which is the ``deployNodes`` gradle task) does this copying automatically for the demos. The ``NetworkMap`` - parameter is no longer needed. - - * For test deployments we've introduced a bootstrapping tool (see :doc:`network-bootstrapper`). - - * ``extraAdvertisedServiceIds``, ``notaryNodeAddress``, ``notaryClusterAddresses`` and ``bftSMaRt`` configs have been - removed. The configuration of notaries has been simplified into a single ``notary`` config object. See - :doc:`corda-configuration-file` for more details. - - * Introducing the concept of network parameters which are a set of constants which all nodes on a network must agree on - to correctly interoperate. These can be retrieved from ``ServiceHub.networkParameters``. - - * One of these parameters, ``maxTransactionSize``, limits the size of a transaction, including its attachments, so that - all nodes have sufficient memory to validate transactions. - - * The set of valid notaries has been moved to the network parameters. Notaries are no longer identified by the CN in - their X500 name. - - * Single node notaries no longer have a second separate notary identity. Their main identity *is* their notary identity. - Use ``NetworkMapCache.notaryIdentities`` to get the list of available notaries. - - * Added ``NetworkMapCache.getNodesByLegalName`` for querying nodes belonging to a distributed service such as a notary cluster - where they all share a common identity. ``NetworkMapCache.getNodeByLegalName`` has been tightened to throw if more than - one node with the legal name is found. - - * The common name in the node's X500 legal name is no longer reserved and can be used as part of the node's name. - - * Moved ``NodeInfoSchema`` to internal package as the node info's database schema is not part of the public API. This - was needed to allow changes to the schema. - -* Support for external user credentials data source and password encryption [CORDA-827]. - -* Exporting additional JMX metrics (artemis, hibernate statistics) and loading Jolokia agent at JVM startup when using - DriverDSL and/or cordformation node runner. - -* Removed confusing property database.initDatabase, enabling its guarded behaviour with the dev-mode. - In devMode Hibernate will try to create or update database schemas, otherwise it will expect relevant schemas to be present - in the database (pre configured via DDL scripts or equivalent), and validate these are correct. - -* ``AttachmentStorage`` now allows providing metadata on attachments upload - username and filename, currently as plain - strings. Those can be then used for querying, utilizing ``queryAttachments`` method of the same interface. - -* ``SSH Server`` - The node can now expose shell via SSH server with proper authorization and permissioning built in. - -* ``CordaRPCOps`` implementation now checks permissions for any function invocation, rather than just when starting flows. - -* ``wellKnownPartyFromAnonymous()`` now always resolve the key to a ``Party``, then the party to the well known party. - Previously if it was passed a ``Party`` it would use its name as-is without verifying the key matched that name. - -* ``OpaqueBytes.bytes`` now returns a clone of its underlying ``ByteArray``, and has been redeclared as ``final``. - This is a minor change to the public API, but is required to ensure that classes like ``SecureHash`` are immutable. - -* Experimental support for PostgreSQL: CashSelection done using window functions - -* ``FlowLogic`` now exposes a series of function called ``receiveAll(...)`` allowing to join ``receive(...)`` instructions. - -* Renamed "plugins" directory on nodes to "cordapps" - -* The ``Cordformation`` gradle plugin has been split into ``cordformation`` and ``cordapp``. The former builds and - deploys nodes for development and testing, the latter turns a project into a cordapp project that generates JARs in - the standard CorDapp format. - -* ``Cordapp`` now has a name field for identifying CorDapps and all CorDapp names are printed to console at startup. - -* Enums now respect the whitelist applied to the Serializer factory serializing / deserializing them. If the enum isn't - either annotated with the @CordaSerializable annotation or explicitly whitelisted then a NotSerializableException is - thrown. - -* Gradle task ``deployNodes`` can have an additional parameter ``configFile`` with the path to a properties file - to be appended to node.conf. - -* Cordformation node building DSL can have an additional parameter ``configFile`` with the path to a properties file - to be appended to node.conf. - -* ``FlowLogic`` now has a static method called ``sleep`` which can be used in certain circumstances to help with resolving - contention over states in flows. This should be used in place of any other sleep primitive since these are not compatible - with flows and their use will be prevented at some point in the future. Pay attention to the warnings and limitations - described in the documentation for this method. This helps resolve a bug in ``Cash`` coin selection. - A new static property ``currentTopLevel`` returns the top most ``FlowLogic`` instance, or null if not in a flow. - -* ``CordaService`` annotated classes should be upgraded to take a constructor parameter of type ``AppServiceHub`` which - allows services to start flows marked with the ``StartableByService`` annotation. For backwards compatability - service classes with only ``ServiceHub`` constructors will still work. - -* ``TimeWindow`` now has a ``length`` property that returns the length of the time-window as a ``java.time.Duration`` object, - or ``null`` if the time-window isn't closed. - -* A new ``SIGNERS_GROUP`` with ordinal 6 has been added to ``ComponentGroupEnum`` that corresponds to the ``Command`` - signers. - -* ``PartialMerkleTree`` is equipped with a ``leafIndex`` function that returns the index of a hash (leaf) in the - partial Merkle tree structure. - -* A new function ``checkCommandVisibility(publicKey: PublicKey)`` has been added to ``FilteredTransaction`` to check - if every command that a signer should receive (e.g. an Oracle) is indeed visible. - -* Changed the AMQP serializer to use the officially assigned R3 identifier rather than a placeholder. - -* The ``ReceiveTransactionFlow`` can now be told to record the transaction at the same time as receiving it. Using this - feature, better support for observer/regulator nodes has been added. See :doc:`tutorial-observer-nodes`. - -* Added an overload of ``TransactionWithSignatures.verifySignaturesExcept`` which takes in a collection of ``PublicKey`` s. - -* ``DriverDSLExposedInterface`` has been renamed to ``DriverDSL`` and the ``waitForAllNodesToFinish()`` method has instead - become a parameter on driver creation. - -* Values for the ``database.transactionIsolationLevel`` config now follow the ``java.sql.Connection`` int constants but - without the "TRANSACTION" prefix, i.e. "NONE", "READ_UNCOMMITTED", etc. - -* Peer-to-peer communications is now via AMQP 1.0 as default. - Although the legacy Artemis CORE bridging can still be used by setting the ``useAMQPBridges`` configuration property to false. - -* The Artemis topics used for peer-to-peer communication have been changed to be more consistent with future cryptographic - agility and to open up the future possibility of sharing brokers between nodes. This is a breaking wire level change - as it means that nodes after this change will not be able to communicate correctly with nodes running the previous version. - Also, any pending enqueued messages in the Artemis message store will not be delivered correctly to their original target. - However, assuming a clean reset of the artemis data and that the nodes are consistent versions, - data persisted via the AMQP serializer will be forward compatible. - -* The ability for CordaServices to register callbacks so they can be notified of shutdown and clean up resource such as - open ports. - -* Move to a message based control of peer to peer bridge formation to allow for future out of process bridging components. - This removes the legacy Artemis bridges completely, so the ``useAMQPBridges`` configuration property has been removed. - -* A ``CordaInternal`` attribute has been added to identify properties that are not intended to form part of the - public api and as such are not intended for public use. This is alongside the existing ``DoNotImplement`` attribute for classes which - provide Corda functionality to user applications, but should not be implemented by consumers, and any classes which - are defined in ``.internal`` packages, which are also not for public use. - -* Marked ``stateMachine`` on ``FlowLogic`` as ``CordaInternal`` to make clear that is it not part of the public api and is - only for internal use - -* Provided experimental support for specifying your own webserver to be used instead of the default development - webserver in ``Cordform`` using the ``webserverJar`` argument - -* Created new ``StartedMockNode`` and ``UnstartedMockNode`` classes which are wrappers around our MockNode implementation - that expose relevant methods for testing without exposing internals, create these using a ``MockNetwork``. - -* The test utils in ``Expect.kt``, ``SerializationTestHelpers.kt``, ``TestConstants.kt`` and ``TestUtils.kt`` have moved - from the ``net.corda.testing`` package to the ``net.corda.testing.core`` package, and ``FlowStackSnapshot.kt`` has moved to the - ``net.corda.testing.services`` package. Moving existing classes out of the ``net.corda.testing.*`` package - will help make it clearer which parts of the api are stable. Scripts have been provided to smooth the upgrade - process for existing projects in the ``tools\scripts`` directory of the Corda repo. - -* ``TransactionSignature`` includes a new ``partialMerkleTree`` property, required for future support of signing over - multiple transactions at once. - -* Updating Jolokia dependency to latest version (includes security fixes) - -.. _changelog_v1: - -Release 1.0 ------------ - -* Unification of VaultQuery And VaultService APIs - Developers now only need to work with a single Vault Service API for all needs. - -* Java 8 lambdas now work property with Kryo during check-pointing. - -* Java 8 serializable lambdas now work property with Kryo during check-pointing. - -* String constants have been marked as ``const`` type in Kotlin, eliminating cases where functions of the form - ``get()`` were created for the Java API. These can now be referenced by their name directly. - -* ``FlowLogic`` communication has been extensively rewritten to use functions on ``FlowSession`` as the base for communication - between nodes. - - * Calls to ``send()``, ``receive()`` and ``sendAndReceive()`` on FlowLogic should be replaced with calls - to the function of the same name on ``FlowSession``. Note that the replacement functions do not take in a destination - parameter, as this is defined in the session. - * Initiated flows now take in a ``FlowSession`` instead of ``Party`` in their constructor. If you need to access the - counterparty identity, it is in the ``counterparty`` property of the flow session. - - -* Added X509EdDSAEngine to intercept and rewrite EdDSA public keys wrapped in X509Key instances. This corrects an issue - with verifying certificate paths loaded from a Java Keystore where they contain EdDSA keys. - -* Confidential identities are now complete: - - * The identity negotiation flow is now called ``SwapIdentitiesFlow``, renamed from ``TransactionKeyFlow``. - * generateSpend() now creates a new confidential identity for the change address rather than using the identity of the - input state owner. - * Please see the documentation :doc:`key-concepts-identity` and :doc:`api-identity` for more details. - -* Remove the legacy web front end from the SIMM demo. - -* ``NodeInfo`` and ``NetworkMapCache`` changes: - - * Removed ``NodeInfo::legalIdentity`` in preparation for handling of multiple identities. We left list of ``NodeInfo::legalIdentitiesAndCerts``, - the first identity still plays a special role of main node identity. - * We no longer support advertising services in network map. Removed ``NodeInfo::advertisedServices``, ``serviceIdentities`` - and ``notaryIdentity``. - * Removed service methods from ``NetworkMapCache``: ``partyNodes``, ``networkMapNodes``, ``notaryNodes``, ``regulatorNodes``, - ``getNodesWithService``, ``getPeersWithService``, ``getRecommended``, ``getNodesByAdvertisedServiceIdentityKey``, ``getAnyNotary``, - ``notaryNode``, ``getAnyServiceOfType``. To get all known ``NodeInfo``'s call ``allNodes``. - * In preparation for ``NetworkMapService`` redesign and distributing notaries through ``NetworkParameters`` we added - ``NetworkMapCache::notaryIdentities`` list to enable to lookup for notary parties known to the network. Related ``CordaRPCOps::notaryIdentities`` - was introduced. Other special nodes parties like Oracles or Regulators need to be specified directly in CorDapp or flow. - * Moved ``ServiceType`` and ``ServiceInfo`` to ``net.corda.nodeapi`` package as services are only required on node startup. - -* Adding enum support to the class carpenter - -* ``ContractState::contract`` has been moved ``TransactionState::contract`` and it's type has changed to ``String`` in order to - support dynamic classloading of contract and contract constraints. - -* CorDapps that contain contracts are now automatically loaded into the attachment storage - for CorDapp developers this - now means that contracts should be stored in separate JARs to flows, services and utilities to avoid large JARs being - auto imported to the attachment store. - -* About half of the code in test-utils has been moved to a new module ``node-driver``, - and the test scope modules are now located in a ``testing`` directory. - -* ``CordaPluginRegistry`` has been renamed to ``SerializationWhitelist`` and moved to the ``net.corda.core.serialization`` - package. The API for whitelisting types that can't be annotated was slightly simplified. This class used to contain - many things, but as we switched to annotations and classpath scanning over time it hollowed out until this was - the only functionality left. You also need to rename your services resource file to the new class name. - An associated property on ``MockNode`` was renamed from ``testPluginRegistries`` to ``testSerializationWhitelists``. - -* Contract Upgrades: deprecated RPC authorization / deauthorization API calls in favour of equivalent flows in ContractUpgradeFlow. - Implemented contract upgrade persistence using JDBC backed persistent map. - -* Vault query common attributes (state status and contract state types) are now handled correctly when using composite - criteria specifications. State status is overridable. Contract states types are aggregatable. - -* Cash selection algorithm is now pluggable (with H2 being the default implementation) - -* Removed usage of Requery ORM library (replaced with JPA/Hibernate) - -* Vault Query performance improvement (replaced expensive per query SQL statement to obtain concrete state types - with single query on start-up followed by dynamic updates using vault state observable)) - -* Vault Query fix: filter by multiple issuer names in ``FungibleAssetQueryCriteria`` - -* Following deprecated methods have been removed: - - * In ``DataFeed`` - - * ``first`` and ``current``, replaced by ``snapshot`` - * ``second`` and ``future``, replaced by ``updates`` - - * In ``CordaRPCOps`` - - * ``stateMachinesAndUpdates``, replaced by ``stateMachinesFeed`` - * ``verifiedTransactions``, replaced by ``verifiedTransactionsFeed`` - * ``stateMachineRecordedTransactionMapping``, replaced by ``stateMachineRecordedTransactionMappingFeed`` - * ``networkMapUpdates``, replaced by ``networkMapFeed`` - -* Due to security concerns and the need to remove the concept of state relevancy (which isn't needed in Corda), - ``ResolveTransactionsFlow`` has been made internal. Instead merge the receipt of the ``SignedTransaction`` and the subsequent - sub-flow call to ``ResolveTransactionsFlow`` with a single call to ``ReceiveTransactionFlow``. The flow running on the counterparty - must use ``SendTransactionFlow`` at the correct place. There is also ``ReceiveStateAndRefFlow`` and ``SendStateAndRefFlow`` for - dealing with ``StateAndRef``'s. - -* Vault query soft locking enhancements and deprecations - - * removed original ``VaultService`` ``softLockedStates`` query mechanism. - * introduced improved ``SoftLockingCondition`` filterable attribute in ``VaultQueryCriteria`` to enable specification of - different soft locking retrieval behaviours (exclusive of soft locked states, soft locked states only, specified by set - of lock ids) - -* Trader demo now issues cash and commercial paper directly from the bank node, rather than the seller node self-issuing - commercial paper but labelling it as if issued by the bank. - -* Merged handling of well known and confidential identities in the identity service. Registration now takes in an identity - (either type) plus supporting certificate path, and de-anonymisation simply returns the issuing identity where known. - If you specifically need well known identities, use the network map, which is the authoritative source of current well - known identities. - -* Currency-related API in ``net.corda.core.contracts.ContractsDSL`` has moved to ```net.corda.finance.CurrencyUtils``. - -* Remove `IssuerFlow` as it allowed nodes to request arbitrary amounts of cash to be issued from any remote node. Use - `CashIssueFlow` instead. - -* Some utility/extension functions (``sumOrThrow``, ``sumOrNull``, ``sumOrZero`` on ``Amount`` and ``Commodity``) - have moved to be static methods on the classes themselves. This improves the API for Java users who no longer - have to see or known about file-level FooKt style classes generated by the Kotlin compile, but means that IntelliJ - no longer auto-suggests these extension functions in completion unless you add import lines for them yourself - (this is Kotlin IDE bug KT-15286). - -* ``:finance`` module now acting as a CorDapp with regard to flow registration, schemas and serializable types. - -* ``WebServerPluginRegistry`` now has a ``customizeJSONSerialization`` which can be overridden to extend the REST JSON - serializers. In particular the IRS demos must now register the ``BusinessCalendar`` serializers. - -* Moved ``:finance`` gradle project files into a ``net.corda.finance`` package namespace. - This may require adjusting imports of Cash flow references and also of ``StartFlow`` permission in ``gradle.build`` files. - -* Removed the concept of relevancy from ``LinearState``. The ``ContractState``'s relevancy to the vault can be determined - by the flow context, the vault will process any transaction from a flow which is not derived from transaction resolution - verification. - -* Removed the tolerance attribute from ``TimeWindowChecker`` and thus, there is no extra tolerance on the notary side anymore. - -* The ``FungibleAsset`` interface has been made simpler. The ``Commands`` grouping interface - that included the ``Move``, ``Issue`` and ``Exit`` interfaces have all been removed, while the ``move`` function has - been renamed to ``withNewOwnerAndAmount`` to be consistent with the ``withNewOwner`` function of the ``OwnableState``. - -* The ``IssueCommand`` interface has been removed from ``Structures``, because, due to the introduction of nonces per - transaction component, the issue command does not need a nonce anymore and it does not require any other attributes. - -* As a consequence of the above and the simpler ``FungibleAsset`` format, fungible assets like ``Cash`` now use - ``class Issue : TypeOnlyCommandData()``, because it's only its presence (``Issue``) that matters. - -* A new `PrivacySalt` transaction component is introduced, which is now an attribute in ``TraversableTransaction`` and - inherently in ``WireTransaction``. - -* A new ``nonces: List`` feature has been added to ``FilteredLeaves``. - -* Due to the ``nonces`` and ``PrivacySalt`` introduction, new functions have been added to ``MerkleTransaction``: - ``fun serializedHash(x: T, privacySalt: PrivacySalt?, index: Int): SecureHash`` - ``fun serializedHash(x: T, nonce: SecureHash): SecureHash`` - ``fun computeNonce(privacySalt: PrivacySalt, index: Int)``. - -* A new ``SignatureMetadata`` data class is introduced with two attributes, ``platformVersion: Int`` and - ``schemeNumberID: Int`` (the signature scheme used). - -* As part of the metadata support in signatures, a new ``data class SignableData(val txId: SecureHash, val signatureMetadata: SignatureMetadata)`` - is introduced, which represents the object actually signed. - -* The unused ``MetaData`` and ``SignatureType`` in ``crypto`` package have been removed. - -* The ``class TransactionSignature(bytes: ByteArray, val by: PublicKey, val signatureMetadata:`` - ``SignatureMetadata): DigitalSignature(bytes)`` class is now utilised Vs the old ``DigitalSignature.WithKey`` for - Corda transaction signatures. Practically, it takes the ``signatureMetadata`` as an extra input, in order to support - signing both the transaction and the extra metadata. - -* To reflect changes in the signing process, the ``Crypto`` object is now equipped with the: - ``fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature`` and - ``fun doVerify(txId: SecureHash, transactionSignature: TransactionSignature): Boolean`` functions. - -* ``SerializationCustomization.addToWhitelist()`` now accepts multiple classes via varargs. - -* Two functions to easily sign a ``FilteredTransaction`` have been added to ``ServiceHub``: - ``createSignature(filteredTransaction: FilteredTransaction, publicKey: PublicKey)`` and - ``createSignature(filteredTransaction: FilteredTransaction)`` to sign with the legal identity key. - -* A new helper method ``buildFilteredTransaction(filtering: Predicate)`` is added to ``SignedTransaction`` to - directly build a ``FilteredTransaction`` using provided filtering functions, without first accessing the - ``tx: WireTransaction``. - -* Test type ``NodeHandle`` now has method ``stop(): CordaFuture`` that terminates the referenced node. - -* Fixed some issues in IRS demo: - * Fixed leg and floating leg notional amounts were not displayed for created deals neither in single nor in list view. - * Parties were not displayed for created deals in single view. - * Non-default notional amounts caused the creation of new deals to fail. - -.. warning:: Renamed configuration property key `basedir` to `baseDirectory`. This will require updating existing configuration files. - -* Removed deprecated parts of the API. - -* Removed ``PluginServiceHub``. Replace with ``ServiceHub`` for ``@CordaService`` constructors. - -* ``X509CertificateHolder`` has been removed from the public API, replaced by ``java.security.X509Certificate``. - -* Moved ``CityDatabase`` out of ``core`` and into ``finance`` - -* All of the ``serializedHash`` and ``computeNonce`` functions have been removed from ``MerkleTransaction``. - The ``serializedHash(x: T)`` and ``computeNonce`` were moved to ``CryptoUtils``. - -* Two overloaded methods ``componentHash(opaqueBytes: OpaqueBytes, privacySalt: PrivacySalt,`` - ``componentGroupIndex: Int, internalIndex: Int): SecureHash`` and ``componentHash(nonce: SecureHash, opaqueBytes: OpaqueBytes): SecureHash`` have - been added to ``CryptoUtils``. Similarly to ``computeNonce``, they internally use SHA256d for nonce and leaf hash - computations. - -* The ``verify(node: PartialTree, usedHashes: MutableList): SecureHash`` in ``PartialMerkleTree`` has been - renamed to ``rootAndUsedHashes`` and is now public, as it is required in the verify function of ``FilteredTransaction``. - -* ``TraversableTransaction`` is now an abstract class extending ``CoreTransaction``. ``WireTransaction`` and - ``FilteredTransaction`` now extend ``TraversableTransaction``. - -* Two classes, ``ComponentGroup(open val groupIndex: Int, open val components: List)`` and - ``FilteredComponentGroup(override val groupIndex: Int, override val components:`` - ``List, val nonces: List, val partialMerkleTree:`` - ``PartialMerkleTree): ComponentGroup(groupIndex, components)`` have been added, which are properties - of the ``WireTransaction`` and ``FilteredTransaction``, respectively. - -* ``checkAllComponentsVisible(componentGroupEnum: ComponentGroupEnum)`` is added to ``FilteredTransaction``, a new - function to check if all components are visible in a specific component-group. - -* To allow for backwards compatibility, ``WireTransaction`` and ``FilteredTransaction`` have new fields and - constructors: ``WireTransaction(componentGroups: List, privacySalt: PrivacySalt = PrivacySalt())``, - ``FilteredTransaction private constructor(id: SecureHash,filteredComponentGroups:`` - ``List, groupHashes: List``. ``FilteredTransaction`` is still built via - ``buildFilteredTransaction(wtx: WireTransaction, filtering: Predicate)``. - -* ``FilteredLeaves`` class have been removed and as a result we can directly call the components from - ``FilteredTransaction``, such as ``ftx.inputs`` Vs the old ``ftx.filteredLeaves.inputs``. - -* A new ``ComponentGroupEnum`` is added with the following enum items: ``INPUTS_GROUP``, ``OUTPUTS_GROUP``, - ``COMMANDS_GROUP``, ``ATTACHMENTS_GROUP``, ``NOTARY_GROUP``, ``TIMEWINDOW_GROUP``. - -* ``ContractUpgradeFlow.Initiator`` has been renamed to ``ContractUpgradeFlow.Initiate`` - -* ``@RPCSinceVersion``, ``RPCException`` and ``PermissionException`` have moved to ``net.corda.client.rpc``. - -* Current implementation of SSL in ``CordaRPCClient`` has been removed until we have a better solution which doesn't rely - on the node's keystore. - -.. _changelog_m14: - -Milestone 14 ------------- - -* Changes in ``NodeInfo``: - - * ``PhysicalLocation`` was renamed to ``WorldMapLocation`` to emphasise that it doesn't need to map to a truly physical - location of the node server. - * Slots for multiple IP addresses and ``legalIdentitiesAndCert`` entries were introduced. Addresses are no longer of type - ``SingleMessageRecipient``, but of ``NetworkHostAndPort``. - -* ``ServiceHub.storageService`` has been removed. ``attachments`` and ``validatedTransactions`` are now direct members of - ``ServiceHub``. - -* Mock identity constants used in tests, such as ``ALICE``, ``BOB``, ``DUMMY_NOTARY``, have moved to ``net.corda.testing`` - in the ``test-utils`` module. - -* ``DummyContract``, ``DummyContractV2``, ``DummyLinearContract`` and ``DummyState`` have moved to ``net.corda.testing.contracts`` - in the ``test-utils`` modules. - -* In Java, ``QueryCriteriaUtilsKt`` has moved to ``QueryCriteriaUtils``. Also ``and`` and ``or`` are now instance methods - of ``QueryCriteria``. - -* ``random63BitValue()`` has moved to ``CryptoUtils`` - -* Added additional common Sort attributes (see ``Sort.CommandStateAttribute``) for use in Vault Query criteria - to include STATE_REF, STATE_REF_TXN_ID, STATE_REF_INDEX - -* Moved the core flows previously found in ``net.corda.flows`` into ``net.corda.core.flows``. This is so that all packages - in the ``core`` module begin with ``net.corda.core``. - -* ``FinalityFlow`` can now be subclassed, and the ``broadcastTransaction`` and ``lookupParties`` function can be - overridden in order to handle cases where no single transaction participant is aware of all parties, and therefore - the transaction must be relayed between participants rather than sent from a single node. - -* ``TransactionForContract`` has been removed and all usages of this class have been replaced with usage of - ``LedgerTransaction``. In particular ``Contract.verify`` and the ``Clauses`` API have been changed and now take a - ``LedgerTransaction`` as passed in parameter. The principal consequence of this is that the types of the input and output - collections on the transaction object have changed, so it may be necessary to ``map`` down to the ``ContractState`` - sub-properties in existing code. - -* Added various query methods to ``LedgerTransaction`` to simplify querying of states and commands. In the same vain - ``Command`` is now parameterised on the ``CommandData`` field. - -* Kotlin utilities that we deemed useful enough to keep public have been moved out of ``net.corda.core.Utils`` and into - ``net.corda.core.utilities.KotlinUtils``. The other utilities have been marked as internal. - -* Changes to ``Cordformation``/ cordapp building: - - * ``Cordformation`` modifies the JAR task to make cordapps build as semi fat JARs containing all dependencies - except other cordapps and Corda core dependencies. - * ``Cordformation`` adds a ``corda`` and ``cordaRuntime`` configuration to projects which cordapp developers should - use to exclude core Corda JARs from being built into Cordapp fat JARs. - -* ``database`` field in ``AbstractNode`` class has changed the type from ``org.jetbrains.exposed.sql.Database`` to - ‘net.corda.node.utilities.CordaPersistence’ - no change is needed for the typical use - (i.e. services.database.transaction { code block } ) however a change is required when Database was explicitly declared - -* ``DigitalSignature.LegallyIdentifiable``, previously used to identify a signer (e.g. in Oracles), has been removed. - One can use the public key to derive the corresponding identity. - -* Vault Query improvements and fixes: - - * FIX inconsistent behaviour: Vault Query defaults to UNCONSUMED in all QueryCriteria types - - * FIX serialization error: Vault Query over RPC when using custom attributes using VaultCustomQueryCriteria. - - * Aggregate function support: extended VaultCustomQueryCriteria and associated DSL to enable specification of - aggregate functions (sum, max, min, avg, count) with, optional, group by clauses and sorting (on calculated aggregate). - - * Pagination simplification. Pagination continues to be optional, with following changes: - - - If no PageSpecification provided then a maximum of MAX_PAGE_SIZE (200) results will be returned, otherwise we fail-fast - with a ``VaultQueryException`` to alert the API user to the need to specify a PageSpecification. Internally, we no - longer need to calculate a results count (thus eliminating an expensive SQL query) unless a PageSpecification is - supplied (note: that a value of -1 is returned for total_results in this scenario). Internally, we now use the - AggregateFunction capability to perform the count. - - Paging now starts from 1 (was previously 0). - - * Additional Sort criteria: by StateRef (or constituents: txId, index) - -* Confidential identities API improvements - - * Registering anonymous identities now takes in AnonymousPartyAndPath - * AnonymousParty.toString() now uses toStringShort() to match other toString() functions - * Add verifyAnonymousIdentity() function to verify without storing an identity - * Replace pathForAnonymous() with anonymousFromKey() which matches actual use-cases better - * Add unit test for fetching the anonymous identity from a key - * Update verifyAnonymousIdentity() function signature to match registerAnonymousIdentity() - * Rename AnonymisedIdentity to AnonymousPartyAndPath - * Remove certificate from AnonymousPartyAndPath as it's not actually used. - * Rename registerAnonymousIdentity() to verifyAndRegisterAnonymousIdentity() - -* Added JPA ``AbstractPartyConverter`` to ensure identity schema attributes are persisted securely according to type - (well known party, resolvable anonymous party, completely anonymous party). - -.. _changelog_m13: - -Milestone 13 ------------- - -Special thank you to `Frederic Dalibard `_, for his contribution which adds -support for more currencies to the DemoBench and Explorer tools. - -* A new Vault Query service: - - * Implemented using JPA and Hibernate, this new service provides the ability to specify advanced queries using - criteria specification sets for both vault attributes and custom contract specific attributes. In addition, new - queries provide sorting and pagination capabilities. - The new API provides two function variants which are exposed for usage within Flows and by RPC clients: - - - ``queryBy()`` for point-in-time snapshot queries - (replaces several existing VaultService functions and a number of Kotlin-only extension functions) - - ``trackBy()`` for snapshot and streaming updates - (replaces the VaultService ``track()`` function and the RPC ``vaultAndUpdates()`` function) - - Existing VaultService API methods will be maintained as deprecated until the following milestone release. - - * The NodeSchema service has been enhanced to automatically generate mapped objects for any ContractState objects - that extend FungibleAsset or LinearState, such that common attributes of those parent states are persisted to - two new vault tables: vault_fungible_states and vault_linear_states (and thus queryable using the new Vault Query - service API). - Similarly, two new common JPA superclass schemas (``CommonSchemaV1.FungibleState`` and - ``CommonSchemaV1.LinearState``) mirror the associated FungibleAsset and LinearState interface states to enable - CorDapp developers to create new custom schemas by extension (rather than duplication of common attribute mappings) - - * A new configurable field ``requiredSchemas`` has been added to the CordaPluginRegistry to enable CorDapps to - register custom contract state schemas they wish to query using the new Vault Query service API (using the - ``VaultCustomQueryCriteria``). - - * See :doc:`api-vault-query` for full details and code samples of using the new Vault Query service. - -* Identity and cryptography related changes: - - * Enable certificate validation in most scenarios (will be enforced in all cases in an upcoming milestone). - - * Added DER encoded format for CompositeKey so they can be used in X.509 certificates. - - * Corrected several tests which made assumptions about counterparty keys, which are invalid when confidential - identities are used. - - * A new RPC has been added to support fuzzy matching of X.500 names, for instance, to translate from user input to - an unambiguous identity by searching the network map. - - * A function for deterministic key derivation ``Crypto.deriveKeyPair(privateKey: PrivateKey, seed: ByteArray)`` - has been implemented to support deterministic ``KeyPair`` derivation using an existing private key and a seed - as inputs. This operation is based on the HKDF scheme and it's a variant of the hardened parent-private -> - child-private key derivation function of the BIP32 protocol, but it doesn't utilize extension chain codes. - Currently, this function supports the following schemes: ECDSA secp256r1 (NIST P-256), ECDSA secp256k1 and - EdDSA ed25519. - -* A new ``ClassWhitelist`` implementation, ``AllButBlacklisted`` is used internally to blacklist classes/interfaces, - which are not expected to be serialised during checkpoints, such as ``Thread``, ``Connection`` and ``HashSet``. - This implementation supports inheritance and if a superclass or superinterface of a class is blacklisted, so is - the class itself. An ``IllegalStateException`` informs the user if a class is blacklisted and such an exception is - returned before checking for ``@CordaSerializable``; thus, blacklisting precedes annotation checking. - -* ``TimeWindow`` has a new 5th factory method ``TimeWindow.fromStartAndDuration(fromTime: Instant, duration: Duration)`` - which takes a start-time and a period-of-validity (after this start-time) as inputs. - -* The node driver has moved to net.corda.testing.driver in the test-utils module. - -* Web API related collections ``CordaPluginRegistry.webApis`` and ``CordaPluginRegistry.staticServeDirs`` moved to - ``net.corda.webserver.services.WebServerPluginRegistry`` in ``webserver`` module. - Classes serving Web API should now extend ``WebServerPluginRegistry`` instead of ``CordaPluginRegistry`` - and they should be registered in ``resources/META-INF/services/net.corda.webserver.services.WebServerPluginRegistry``. - -* Added a flag to the driver that allows the running of started nodes in-process, allowing easier debugging. - To enable use `driver(startNodesInProcess = true) { .. }`, or `startNode(startInSameProcess = true, ..)` - to specify for individual nodes. - -* Dependencies changes: - * Upgraded Dokka to v0.9.14. - * Upgraded Gradle Plugins to 0.12.4. - * Upgraded Apache ActiveMQ Artemis to v2.1.0. - * Upgraded Netty to v4.1.9.Final. - * Upgraded BouncyCastle to v1.57. - * Upgraded Requery to v1.3.1. - -.. _changelog_m12: - -Milestone 12 (First Public Beta) --------------------------------- - -* Quite a few changes have been made to the flow API which should make things simpler when writing CorDapps: - - * ``CordaPluginRegistry.requiredFlows`` is no longer needed. Instead annotate any flows you wish to start via RPC with - ``@StartableByRPC`` and any scheduled flows with ``@SchedulableFlow``. - - * ``CordaPluginRegistry.servicePlugins`` is also no longer used, along with ``PluginServiceHub.registerFlowInitiator``. - Instead annotate your initiated flows with ``@InitiatedBy``. This annotation takes a single parameter which is the - initiating flow. This initiating flow further has to be annotated with ``@InitiatingFlow``. For any services you - may have, such as oracles, annotate them with ``@CordaService``. These annotations will be picked up automatically - when the node starts up. - - * Due to these changes, when unit testing flows make sure to use ``AbstractNode.registerInitiatedFlow`` so that the flows - are wired up. Likewise for services use ``AbstractNode.installCordaService``. - - * Related to ``InitiatingFlow``, the ``shareParentSessions`` boolean parameter of ``FlowLogic.subFlow`` has been - removed. This was an unfortunate parameter that unnecessarily exposed the inner workings of flow sessions. Now, if - your sub-flow can be started outside the context of the parent flow then annotate it with ``@InitiatingFlow``. If - it's meant to be used as a continuation of the existing parent flow, such as ``CollectSignaturesFlow``, then it - doesn't need any annotation. - - * The ``InitiatingFlow`` annotation also has an integer ``version`` property which assigns the initiating flow a version - number, defaulting to 1 if it's not specified. This enables versioning of flows with nodes only accepting communication - if the version number matches. At some point we will support the ability for a node to have multiple versions of the - same flow registered, enabling backwards compatibility of flows. - - * ``ContractUpgradeFlow.Instigator`` has been renamed to just ``ContractUpgradeFlow``. - - * ``NotaryChangeFlow.Instigator`` has been renamed to just ``NotaryChangeFlow``. - - * ``FlowLogic.getCounterpartyMarker`` is no longer used and been deprecated for removal. If you were using this to - manage multiple independent message streams with the same party in the same flow then use sub-flows instead. - -* There are major changes to the ``Party`` class as part of confidential identities: - - * ``Party`` has moved to the ``net.corda.core.identity`` package; there is a deprecated class in its place for - backwards compatibility, but it will be removed in a future release and developers should move to the new class as soon - as possible. - * There is a new ``AbstractParty`` superclass to ``Party``, which contains just the public key. This now replaces - use of ``Party`` and ``PublicKey`` in state objects, and allows use of full or anonymised parties depending on - use-case. - * A new ``PartyAndCertificate`` class has been added which aggregates a Party along with an X.509 certificate and - certificate path back to a network trust root. This is used where a Party and its proof of identity are required, - for example in identity registration. - * Names of parties are now stored as a ``X500Name`` rather than a ``String``, to correctly enforce basic structure of the - name. As a result all node legal names must now be structured as X.500 distinguished names. - -* The identity management service takes an optional network trust root which it will validate certificate paths to, if - provided. A later release will make this a required parameter. - -* There are major changes to transaction signing in flows: - - * You should use the new ``CollectSignaturesFlow`` and corresponding ``SignTransactionFlow`` which handle most - of the details of this for you. They may get more complex in future as signing becomes a more featureful - operation. ``ServiceHub.legalIdentityKey`` no longer returns a ``KeyPair``, it instead returns just the - ``PublicKey`` portion of this pair. The ``ServiceHub.notaryIdentityKey`` has changed similarly. The goal of this - change is to keep private keys encapsulated and away from most flow code/Java code, so that the private key - material can be stored in HSMs and other key management devices. - * The ``KeyManagementService`` no longer provides any mechanism to request the node's ``PrivateKey`` objects directly. - Instead signature creation occurs in the ``KeyManagementService.sign``, with the ``PublicKey`` used to indicate - which of the node's keypairs to use. This lookup also works for ``CompositeKey`` scenarios - and the service will search for a leaf key hosted on the node. - * The ``KeyManagementService.freshKey`` method now returns only the ``PublicKey`` portion of the newly generated ``KeyPair`` - with the ``PrivateKey`` kept internally to the service. - * Flows which used to acquire a node's ``KeyPair``, typically via ``ServiceHub.legalIdentityKey``, - should instead use the helper methods on ``ServiceHub``. In particular to freeze a ``TransactionBuilder`` and - generate an initial partially signed ``SignedTransaction`` the flow should use ``ServiceHub.toSignedTransaction``. - Flows generating additional party signatures should use ``ServiceHub.createSignature``. Each of these methods is - provided with two signatures. One version that signs with the default node key, the other which allows key selection - by passing in the ``PublicKey`` partner of the desired signing key. - * The original ``KeyPair`` signing methods have been left on the ``TransactionBuilder`` and ``SignedTransaction``, but - should only be used as part of unit testing. - -* ``Timestamp`` used for validation/notarization time-range has been renamed to ``TimeWindow``. - There are now 4 factory methods ``TimeWindow.fromOnly(fromTime: Instant)``, - ``TimeWindow.untilOnly(untilTime: Instant)``, ``TimeWindow.between(fromTime: Instant, untilTime: Instant)`` and - ``TimeWindow.withTolerance(time: Instant, tolerance: Duration)``. - Previous constructors ``TimeWindow(fromTime: Instant, untilTime: Instant)`` and - ``TimeWindow(time: Instant, tolerance: Duration)`` have been removed. - -* The Bouncy Castle library ``X509CertificateHolder`` class is now used in place of ``X509Certificate`` in order to - have a consistent class used internally. Conversions to/from ``X509Certificate`` are done as required, but should - be avoided where possible. - -* The certificate hierarchy has been changed in order to allow corda node to sign keys with proper certificate chain. - * The corda node will now be issued a restricted client CA for identity/transaction key signing. - * TLS certificate are now stored in `sslkeystore.jks` and identity keys are stored in `nodekeystore.jks` - -.. warning:: The old keystore will need to be removed when upgrading to this version. - -Milestone 11.1 --------------- - -* Fix serialisation error when starting a flow. -* Automatically whitelist subclasses of `InputStream` when serialising. -* Fix exception in DemoBench on Windows when loading CorDapps into the Node Explorer. -* Detect when localhost resolution is broken on MacOSX, and provide instructions on how to fix it. - -Milestone 11.0 --------------- - -* API changes: - * Added extension function ``Database.transaction`` to replace ``databaseTransaction``, which is now deprecated. - - * Starting a flow no longer enables progress tracking by default. To enable it, you must now invoke your flow using - one of the new ``CordaRPCOps.startTrackedFlow`` functions. ``FlowHandle`` is now an interface, and its ``progress: Observable`` - field has been moved to the ``FlowProgressHandle`` child interface. Hence developers no longer need to invoke ``notUsed`` - on their flows' unwanted progress-tracking observables. - - * Moved ``generateSpend`` and ``generateExit`` functions into ``OnLedgerAsset`` from the vault and - ``AbstractConserveAmount`` clauses respectively. - - * Added ``CompositeSignature`` and ``CompositeSignatureData`` as part of enabling ``java.security`` classes to work - with composite keys and signatures. - - * ``CompositeKey`` now implements ``java.security.PublicKey`` interface, so that keys can be used on standard classes - such as ``Certificate``. - - * There is no longer a need to transform single keys into composite - ``composite`` extension was removed, it is - impossible to create ``CompositeKey`` with only one leaf. - - * Constructor of ``CompositeKey`` class is now private. Use ``CompositeKey.Builder`` to create a composite key. - Keys emitted by the builder are normalised so that it's impossible to create a composite key with only one node. - (Long chains of single nodes are shortened.) - - * Use extension function ``PublicKeys.keys`` to access all keys belonging to an instance of ``PublicKey``. For a - ``CompositeKey``, this is equivalent to ``CompositeKey.leafKeys``. - - * Introduced ``containsAny``, ``isFulfilledBy``, ``keys`` extension functions on ``PublicKey`` - ``CompositeKey`` - type checking is done there. - -* Corda now requires JDK 8u131 or above in order to run. Our Kotlin now also compiles to JDK8 bytecode, and so you'll need - to update your CorDapp projects to do the same. E.g. by adding this to ``build.gradle``: - -.. parsed-literal:: - - tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - languageVersion = "1.1" - apiVersion = "1.1" - jvmTarget = "1.8" - } - } - -.. - - or by adjusting ``Settings/Build,Execution,Deployment/Compiler/KotlinCompiler`` in IntelliJ:: - - - Language Version: 1.1 - - API Version: 1.1 - - Target JVM Version: 1.8 - -* DemoBench is now installed as ``Corda DemoBench`` instead of ``DemoBench``. - -* Rewrote standard test identities to have full X.500 distinguished names. As part of this work we standardised on a - smaller set of test identities, to reduce risk of subtle differences (i.e. similar common names varying by whitespace) - in naming making it hard to diagnose issues. - -Milestone 10.0 --------------- - -Special thank you to `Qian Hong `_, `Marek Skocovsky `_, -`Karel Hajek `_, and `Jonny Chiu `_ for their contributions -to Corda in M10. - -.. warning:: Due to incompatibility between older version of IntelliJ and gradle 3.4, you will need to upgrade Intellij -to 2017.1 (with kotlin-plugin v1.1.1) in order to run Corda demos in IntelliJ. You can download the latest IntelliJ - from `JetBrains `_. - -.. warning:: The Kapt-generated models are no longer included in our codebase. If you experience ``unresolved references`` -errors when building in IntelliJ, please rebuild the schema model by running ``gradlew kaptKotlin`` in Windows or - ``./gradlew kaptKotlin`` in other systems. Alternatively, perform a full gradle build or install. - -.. note:: Kapt is used to generate schema model and entity code (from annotations in the codebase) using the Kotlin Annotation -processor. - -* Corda DemoBench: - * DemoBench is a new tool to make it easy to configure and launch local Corda nodes. A very useful tool to demonstrate - to your colleagues the fundamentals of Corda in real-time. It has the following features: - - * Clicking "Add node" creates a new tab that lets you edit the most important configuration properties of the node - before launch, such as its legal name and which CorDapps will be loaded. - * Each tab contains a terminal emulator, attached to the pseudoterminal of the node. This lets you see console output. - * You can launch an Corda Explorer instance for each node via the DemoBench UI. Credentials are handed to the Corda - Explorer so it starts out logged in already. - * Some basic statistics are shown about each node, informed via the RPC connection. - * Another button launches a database viewer in the system browser. - * The configurations of all running nodes can be saved into a single ``.profile`` file that can be reloaded later. - - * Download `Corda DemoBench `_. - -* Vault: - * Soft Locking is a new feature implemented in the vault which prevent a node constructing transactions that attempt - to use the same input(s) simultaneously. - * Such transactions would result in naturally wasted effort when the notary rejects them as double spend attempts. - * Soft locks are automatically applied to coin selection (eg. cash spending) to ensure that no two transactions attempt - to spend the same fungible states. - -* Corda Shell : - * The shell lets developers and node administrators easily command the node by running flows, RPCs and SQL queries. - * It provides a variety of commands to monitor the node. - * The Corda Shell is based on the popular `CRaSH project `_ and new commands can be easily - added to the node by simply dropping Groovy or Java files into the node's ``shell-commands`` directory. - * We have many enhancements planned over time including SSH access, more commands and better tab completion. - -* API changes: - * The new Jackson module provides JSON/YAML serialisers for common Corda datatypes. - If you have previously been using the JSON support in the standalone web server, - please be aware that Amounts are now serialised as strings instead of { quantity, token } pairs as before. - The old format is still accepted, but the new JSON will be produced using strings like "1000.00 USD" when writing. - You can use any format supported by ``Amount.parseCurrency`` as input. - - * We have restructured client package in this milestone. - * ``CordaClientRPC`` is now in the new ``:client:rpc`` module. - * The old ``:client`` module has been split up into ``:client:jfx`` and ``:client:mock``. - * We also have a new ``:node-api`` module (package ``net.corda.nodeapi``) which contains the shared code between - ``node`` and ``client``. - - * The basic Amount API has been upgraded to have support for advanced financial use cases and to better integrate with - currency reference data. - -* Configuration: - * Replace ``artemisPort`` with ``p2pPort`` in Gradle configuration. - * Replace ``artemisAddress`` with ``p2pAddress`` in node configuration. - * Added ``rpcAddress`` in node configuration for non-ssl RPC connection. - -* Object Serialization: - * Pool Kryo instances for efficiency. - -* RPC client changes: - * RPC clients can now connect to the node without the need for SSL. This requires a separate port on the Artemis broker, - SSL must not be used for RPC connection. - * CordaRPCClient now needs to connect to ``rpcAddress`` rather than ``p2pAddress``. - -* Dependencies changes: - * Upgraded Kotlin to v1.1.1. - * Upgraded Gradle to v3.4.1. - * Upgraded requery to v1.2.1. - * Upgraded H2 to v1.4.194. - * Replaced kotlinx-support-jdk8 with kotlin-stdlib-jre8. - -* Improvements: - * Added ``--version`` command line flag to print the version of the node. - * Flows written in Java can now execute a sub-flow inside ``UntrustworthyData.unwrap``. - * Added optional out-of-process transaction verification. Any number of external verifier processes may be attached - to the node which can handle loadbalanced verification requests. - -* Bug fixes: - * ``--logging-level`` command line flag was previously broken, now correctly sets the logging level. - * Fixed bug whereby Cash Exit was not taking into account the issuer reference. - - -Milestone 9.1 -------------- - -* Correct web server ports for IRS demo. -* Correct which corda-webserver JAR is published to Maven. - -Milestone 9 ------------ - -* With thanks to `Thomas Schroeter `_ for the Byzantine fault tolerant (BFT) - notary prototype. -* Web server is a separate JAR. This is a breaking change. The new webserver JAR (``corda-webserver.jar``) - must be invoked separately to node startup, using the command``java -jar corda-webserver.jar`` in the same - directory as the ``node.conf``. Further changes are anticipated in upcoming milestone releases. - -* API: - - * Pseudonymous ``AnonymousParty`` class added as a superclass of ``Party``. - * Split ``CashFlow`` into individual ``CashIssueFlow``, ``CashPaymentFlow`` and ``CashExitFlow`` flows, so that fine - grained permissions can be applied. Added ``CashFlowCommand`` for use-cases where cash flow triggers need to be - captured in an object that can be passed around. - * ``CordaPluginRegistry`` method ``registerRPCKryoTypes`` is renamed ``customizeSerialization`` and the argument - types now hide the presence of Kryo. - * New extension functions for encoding/decoding to base58, base64, etc. See - ``core/src/main/kotlin/net/corda/core/crypto/EncodingUtils.kt`` - * Add ``openAttachment`` function to Corda RPC operations, for downloading an attachment from a node's data storage. - * Add ``getCashBalances`` function to Corda RPC operations, for getting cash balances from a node's vault. - -* Configuration: - * ``extraAdvertisedServiceIds`` config is now a list of strings, rather than a comma separated string. For example - ``[ "corda.interest_rates" ]`` instead of ``"corda.interest_rates"``. - -* Flows: - * Split ``CashFlow`` into separate ``CashIssueFlow``, ``CashPaymentFlow`` and ``CashExitFlow`` so that permissions can - be assigned individually. - * Split single example user into separate "bankUser" and "bigCorpUser" so that permissions for the users make sense - rather than being a combination of both roles. - * ``ProgressTracker`` emits exception thrown by the flow, allowing the ANSI renderer to correctly stop and print the error - -* Object Serialization: - - * Consolidated Kryo implementations across RPC and P2P messaging with whitelisting of classes via plugins or with - ``@CordaSerializable`` for added node security. - -* Privacy: - * Non-validating notary service now takes in a ``FilteredTransaction`` so that no potentially sensitive transaction - details are unnecessarily revealed to the notary - -* General: - * Add vault service persistence using Requery - * Certificate signing utility output is now more verbose - -Milestone 8 ------------ - -* Node memory usage and performance improvements, demo nodes now only require 200 MB heap space to run. - -* The Corda node no longer runs an internal web server, it's now run in a separate process. Driver and Cordformation have - been updated to reflect this change. Existing CorDapps should be updated with additional calls to the new ``startWebserver()`` - interface in their Driver logic (if they use the driver e.g. in integration tests). See the IRS demo for an example. - -* Data model: ``Party`` equality is now based on the owning key, rather than the owning key and name. This is important for - party anonymisation to work, as each key must identify exactly one party. - -* Contracts: created new composite clauses called ``AllOf``, ``AnyOf`` and ``FirstOf`` to replace ``AllComposition``, ``AnyComposition`` - and ``FirstComposition``, as this is significantly clearer in intent. ``AnyOf`` also enforces that at least one subclause - must match, whereas ``AnyComposition`` would accept no matches. - -* Explorer: the user can now configure certificate path and keystore/truststore password on the login screen. - -* Documentation: - - * Key Concepts section revamped with new structure and content. - * Added more details to :doc:`getting-set-up` page. - -* Flow framework: improved exception handling with the introduction of ``FlowException``. If this or a subtype is thrown - inside a flow it will propagate to all counterparty flows and subsequently be thrown by them as well. Existing flows such as - ``NotaryFlow.Client/Service`` and others have been modified to throw a ``FlowException`` (in this particular case a - ``NotaryException``) instead of sending back error responses. - -* Notary flow: provide complete details of underlying error when contract validation fails. - -Milestone 7 ------------ - -* With thanks to `Thomas Schroeter `_ ``NotaryFlow`` is now idempotent. - -* Explorer: - - * The GUI for the explorer now shows other nodes on the network map and the transactions between them. - * Map resolution increased and allows zooming and panning. - * `Video demonstration `_ of the Node Explorer. - -* The CorDapp template now has a Java example that parallels the Kotlin one for developers more comfortable with Java. - ORM support added to the Kotlin example. - -* Demos: - - * Added the Bank of Corda demo - a demo showing a node (Bank of Corda) acting as an issuer of Cash, and a client - driver providing both Web and RPC access to request issuance of cash. - * Demos now use RPC to communicate with the node from the webserver. This brings the demos more in line with how - interaction with nodes is expected to be. The demos now treat their webservers like clients. This will also allow - for the splitting of the webserver from the node for milestone 8. - * Added a SIMM valuation demo integration test to catch regressions. - -* Security: - - * MQ broker of the node now requires authentication which means that third parties cannot connect to and - listen to queues on the Node. RPC and P2P between nodes is now authenticated as a result of this change. - This also means that nodes or RPC users cannot pretend to be other nodes or RPC users. - * The node now does host verification of any node that connects to it and prevents man in the middle attacks. - -* Improvements: - - * Vault updates now contain full ``StateAndRef`` which allows subscribers to check whether the update contains - relevant states. - * Cash balances are calculated using aggregate values to prevent iterating through all states in the vault, which - improves performance. - * Multi-party services, such as notaries, are now load balanced and represented as a single ``Party`` object. - * The Notary Change flow now supports encumbrances. - -Milestone 6 ------------ - -* Added the `Corda technical white paper <_static/corda-technical-whitepaper.pdf>`_. Note that its current version - is 0.5 to reflect the fact that the Corda design is still evolving. Although we expect only relatively small tweaks - at this point, when Corda reaches 1.0 so will the white paper. - -* Major documentation restructuring and new content: - - * More details on Corda node internals. - * New CorDapp tutorial. - * New tutorial on building transactions. - * New tutorials on how to run and use a notary service. - -* An experimental version of the deterministic JVM sandbox has been added. It is not integrated with the node and will - undergo some significant changes in the coming releases before it is integrated, as the code is finished, as bugs are - found and fixed, and as the platform subset we choose to expose is finalised. Treat this as an outline of the basic - approach rather than something usable for production. - -* Developer experience: - - * Samples have been merged back into the main repository. All samples can now be run via command line or IntelliJ. - - * Added a Client RPC python example. - - * Node console output now displays concise startup information, such as startup time or web address. All logging to - the console is suppressed apart from errors and flow progress tracker steps. It can be re-enabled by passing - ``--log-to-console`` command line parameter. Note that the log file remains unchanged and will still contain all - log entries. - - * The ``runnodes`` scripts generated by the Gradle plugins now open each node in separate terminal windows or (on macOS) tabs. - - * A much more complete template app. - - * JARs now available on Maven Central. - -* Data model: A party is now identified by a composite key (formerly known as a "public key tree") instead of a single public key. - Read more in :ref:`composite-keys`. This allows expressing distributed service identities, e.g. a distributed notary. - In the future this will also allow parties to use multiple signing keys for their legal identity. - -* Decentralised consensus: A prototype RAFT based notary composed of multiple nodes has been added. This implementation - is optimised for high performance over robustness against malicious cluster members, which may be appropriate for - some financial situations. - -* Node explorer app: - - * New theme aligned with the Corda branding. - * The New Transaction screen moved to the Cash View (as it is used solely for cash transactions) - * Removed state machine/flow information from Transaction table. A new view for this will be created in a future release. - * Added a new Network View that displays details of all nodes on the network. - * Users can now configure the reporting currency in settings. - * Various layout and performance enhancements. - -* Client RPC: - - * Added a generic ``startFlow`` method that enables starting of any flow, given sufficient permissions. - * Added the ability for plugins to register additional classes or custom serialisers with Kryo for use in RPC. - * ``rpc-users.properties`` file has been removed with RPC user settings moved to the config file. - -* Configuration changes: It is now possible to specify a custom legal name for any of the node's advertised services. - -* Added a load testing framework which allows stress testing of a node cluster, as well as specifying different ways of - disrupting the normal operation of nodes. See :doc:`loadtesting`. - -* Improvements to the experimental contract DSL, by Sofus Mortensen of Nordea Bank (please give Nordea a shoutout too). - -API changes: - -* The top level package has been renamed from ``com.r3corda`` to ``net.corda``. -* Protocols have been renamed to "flows". -* ``OpaqueBytes`` now uses ``bytes`` as the field name rather than ``bits``. - -Milestone 5 ------------ - -* A simple RPC access control mechanism. Users, passwords and permissions can be defined in a configuration file. - This mechanism will be extended in future to support standard authentication systems like LDAP. - -* New features in the explorer app and RPC API for working with cash: - - * Cash can now be sent, issued and exited via RPC. - * Notes can now be associated with transactions. - * Hashes are visually represented using identicons. - * Lots of functional work on the explorer UI. You can try it out by running ``gradle tools:explorer:runDemoNodes`` to run - a local network of nodes that swap cash with each other, and then run ``gradle tools:explorer:run`` to start - the app. - -* A new demo showing shared valuation of derivatives portfolios using the ISDA SIMM has been added. Note that this app - relies on a proprietary implementation of the ISDA SIMM business logic from OpenGamma. A stub library is provided - to ensure it compiles but if you want to use the app for real please contact us. - -* Developer experience (we plan to do lots more here in milestone 6): - - * Demos and samples have been split out of the main repository, and the initial developer experience continues to be - refined. All necessary JARs can now be installed to Maven Local by simply running ``gradle install``. - * It's now easier to define a set of nodes to run locally using the new "CordFormation" gradle plugin, which - defines a simple DSL for creating networks of nodes. - * The template CorDapp has been upgraded with more documentation and showing more features. - -* Privacy: transactions are now structured as Merkle trees, and can have sections "torn off" - presented for - verification and signing without revealing the rest of the transaction. - -* Lots of bug fixes, tweaks and polish starting the run up to the open source release. - -API changes: - -* Plugin service classes now take a ``PluginServiceHub`` rather than a ``ServiceHubInternal``. -* ``UniqueIdentifier`` equality has changed to only take into account the underlying UUID. -* The contracts module has been renamed to finance, to better reflect what it is for. - -Milestone 4 ------------ - -New features in this release: - -* Persistence: - - * States can now be written into a relational database and queried using JDBC. The schemas are defined by the - smart contracts and schema versioning is supported. It is reasonable to write an app that stores data in a mix - of global ledger transactions and local database tables which are joined on demand, using join key slots that - are present in many state definitions. Read more about :doc:`api-persistence`. - * The embedded H2 SQL database is now exposed by default to any tool that can speak JDBC. The database URL is - printed during node startup and can be used to explore the database, which contains both node internal data - and tables generated from ledger states. - * Protocol checkpoints are now stored in the database as well. Message processing is now atomic with protocol - checkpointing and run under the same RDBMS transaction. - * MQ message deduplication is now handled at the app layer and performed under the RDMS transaction, so - ensuring messages are only replayed if the RDMS transaction rolled back. - * "The wallet" has been renamed to "the vault". - -* Client RPC: - - * New RPCs added to subscribe to snapshots and update streams state of the vault, currently executing protocols - and other important node information. - * New tutorial added that shows how to use the RPC API to draw live transaction graphs on screen. - -* Protocol framework: - - * Large simplifications to the API. Session management is now handled automatically. Messages are now routed - based on identities rather than node IP addresses. - -* Decentralised consensus: - - * A standalone one-node notary backed by a JDBC store has been added. - * A prototype RAFT based notary composed of multiple nodes is available on a branch. - -* Data model: - - * Compound keys have been added as preparation for merging a distributed RAFT based notary. Compound keys - are trees of public keys in which interior nodes can have validity thresholds attached, thus allowing - boolean formulas of keys to be created. This is similar to Bitcoin's multi-sig support and the data model - is the same as the InterLedger Crypto-Conditions spec, which should aid interoperate in future. Read more about - key trees in the ":doc:`api-core-types`" article. - * A new tutorial has been added showing how to use transaction attachments in more detail. - -* Testnet - - * Permissioning infrastructure phase one is built out. The node now has a notion of development mode vs normal - mode. In development mode it works like M3 and the SSL certificates used by nodes running on your local - machine all self-sign using a developer key included in the source tree. When development mode is not active, - the node won't start until it has a signed certificate. Such a certificate can be obtained by simply running - an included command line utility which generates a CSR and submits it to a permissioning service, then waits - for the signed certificate to be returned. Note that currently there is no public Corda testnet, so we are - not currently running a permissioning service. - -* Standalone app development: - - * The Corda libraries that app developers need to link against can now be installed into your local Maven - repository, where they can then be used like any other JAR. See :doc:`running-a-node`. - -* User interfaces: - - * Infrastructure work on the node explorer is now complete: it is fully switched to using the MQ based RPC system. - * A library of additional reactive collections has been added. This API builds on top of Rx and the observable - collections API in Java 8 to give "live" data structures in which the state of the node and ledger can be - viewed as an ordinary Java ``List``, ``Map`` and ``Set``, but which also emit callbacks when these views - change, and which can have additional views derived in a functional manner (filtered, mapped, sorted, etc). - Finally, these views can then be bound directly into JavaFX UIs. This makes for a concise and functional - way of building application UIs that render data from the node, and the API is available for third party - app developers to use as well. We believe this will be highly productive and enjoyable for developers who - have the option of building JavaFX apps (vs web apps). - * The visual network simulator tool that was demoed back in April as part of the first Corda live demo has - been merged into the main repository. - -* Documentation - - * New secure coding guidelines. Corda tries to eliminate as many security mistakes as practical via the type - system and other mechanically checkable processes, but there are still things that one must be aware of. - * New attachments tutorial. - * New Client RPC tutorial. - * More tutorials on how to build a standalone CorDapp. - -* Testing - - * More integration testing support - * New micro-DSLs for expressing expected sequences of operations with more or less relaxed ordering constraints. - * QuickCheck generators to create streams of randomised transactions and other basic types. QuickCheck is a way - of writing unit tests that perform randomised fuzz testing of code, originally developed by the Haskell - community and now also available in Java. - -API changes: - -* The transaction types (Signed, Wire, LedgerTransaction) have moved to ``net.corda.core.transactions``. You can - update your code by just deleting the broken import lines and letting your IDE re-import them from the right - location. -* ``AbstractStateReplacementProtocol.verifyProposal`` has changed its prototype in a minor way. -* The ``UntrustworthyData.validate`` method has been renamed to ``unwrap`` - the old name is now deprecated. -* The wallet, wallet service, etc. are now vault, vault service, etc. These better reflect the intent that they - are a generic secure data store, rather than something which holds cash. -* The protocol send/receive APIs have changed to no longer require a session id. Please check the current version - of the protocol framework tutorial for more details. - -Milestone 3 ------------ - -* More work on preparing for the testnet: - - * Corda is now a standalone app server that loads "CorDapps" into itself as plugins. Whilst the existing IRS - and trader demos still exist for now, these will soon be removed and there will only be a single Corda node - program. Note that the node is a single, standalone jar file that is easier to execute than the demos. - * Project Vega (shared SIMM modelling for derivative portfolios) has already been converted to be a CorDapp. - * Significant work done on making the node persist its wallet data to a SQL backend, with more on the way. - * Upgrades and refactorings of the core transaction types in preparation for the incoming sandboxing work. - -* The Clauses API that seeks to make writing smart contracts easier has gone through another design iteration, - with the result that clauses are now cleaner and more composable. -* Improvements to the protocol API for finalising transactions (notarising, transmitting and storing). -* Lots of work done on an MQ based client API. -* Improvements to the developer site: - - * The developer site has been re-read from start to finish and refreshed for M3 so there should be no obsolete - texts or references anywhere. - * The Corda non-technical white paper is now a part of the developer site and git repository. The LaTeX source is - also provided so if you spot any issues with it, you can send us patches. - * There is a new section on how to write CorDapps. - -* Further R&D work by Sofus Mortensen in the experimental module on a new 'universal' contract language. -* SSL for the REST API and webapp server can now be configured. - - -Milestone 2 ------------ - -* Big improvements to the interest rate swap app: - - * A new web app demonstrating the IRS contract has been added. This can be used as an example for how to interact with - the Corda API from the web. - * Simplifications to the way the demo is used from the command line. - * :doc:`Detailed documentation on how the contract works and can be used ` has been written. - * Better integration testing of the app. - -* Smart contracts have been redesigned around reusable components, referred to as "clauses". The cash, commercial paper - and obligation contracts now share a common issue clause. -* New code in the experimental module (note that this module is a place for work-in-progress code which has not yet gone - through code review and which may, in general, not even function correctly): - - * Thanks to the prolific Sofus Mortensen @ Nordea Bank, an experimental generic contract DSL that is based on the famous - 2001 "Composing contracts" paper has been added. We thank Sofus for this great and promising research, which is so - relevant in the wake of the DAO hack. - * The contract code from the recent trade finance demos is now in experimental. This code comes thanks to a - collaboration of the members; all credit to: - - * Mustafa Ozturk @ Natixis - * David Nee @ US Bank - * Johannes Albertsen @ Dankse Bank - * Rui Hu @ Nordea - * Daniele Barreca @ Unicredit - * Sukrit Handa @ Scotiabank - * Giuseppe Cardone @ Banco Intesa - * Robert Santiago @ BBVA - -* The usability of the command line demo programs has been improved. -* All example code and existing contracts have been ported to use the new Java/Kotlin unit testing domain-specific - languages (DSLs) which make it easy to construct chains of transactions and verify them together. This cleans up - and unifies the previous ad-hoc set of similar DSLs. A tutorial on how to use it has been added to the documentation. - We believe this largely completes our testing story for now around smart contracts. Feedback from bank developers - during the Trade Finance project has indicated that the next thing to tackle is docs and usability improvements in - the protocols API. -* Significant work done towards defining the "CorDapp" concept in code, with dynamic loading of API services and more to - come. -* Inter-node communication now uses SSL/TLS and AMQP/1.0, albeit without all nodes self-signing at the moment. A real - PKI for the p2p network will come later. -* Logging is now saved to files with log rotation provided by Log4J. - -API changes: - -* Some utility methods and extension functions that are specific to certain contract types have moved packages: just - delete the import lines that no longer work and let IntelliJ replace them with the correct package paths. -* The ``arg`` method in the test DSL is now called ``command`` to be consistent with the rest of the data model. -* The messaging APIs have changed somewhat to now use a new ``TopicSession`` object. These APIs will continue to change - in the upcoming releases. -* Clauses now have default values provided for ``ifMatched``, ``ifNotMatched`` and ``requiredCommands``. - -New documentation: - -* :doc:`contract-catalogue` -* :doc:`contract-irs` -* :doc:`tutorial-test-dsl` - -Milestone 1 ------------ - -Highlights of this release: - -* Event scheduling. States in the ledger can now request protocols to be invoked at particular times, for states - considered relevant by the wallet. -* Upgrades to the notary/consensus service support: - - * There is now a way to change the notary controlling a state. - * You can pick between validating and non-validating notaries, these let you select your privacy/robustness trade-off. - -* A new obligation contract that supports bilateral and multilateral netting of obligations, default tracking and - more. -* Improvements to the financial type system, with core classes and contracts made more generic. -* Switch to a better digital signature algorithm: ed25519 instead of the previous JDK default of secp256r1. -* A new integration test suite. -* A new Java unit testing DSL for contracts, similar in spirit to the one already developed for Kotlin users (which - depended on Kotlin specific features). -* An experimental module, where developers who want to work with the latest Corda code can check in contracts/cordapp - code before it's been fully reviewed. Code in this module has compiler warnings suppressed but we will still make - sure it compiles across refactorings. -* Persistence improvements: transaction data is now stored to disk and automatic protocol resume is now implemented. -* Many smaller bug fixes, cleanups and improvements. - -We have new documentation on: - -* :doc:`event-scheduling` -* :doc:`api-core-types` -* :doc:`key-concepts-consensus` - -Summary of API changes (not exhaustive): - -* Notary/consensus service: - - * ``NotaryService`` is now extensible. - * Every ``ContractState`` now has to specify a *participants* field, which is a list of parties that are able to - consume this state in a valid transaction. This is used for e.g. making sure all relevant parties obtain the updated - state when changing a notary. - * Introduced ``TransactionState``, which wraps ``ContractState``, and is used when defining a transaction output. - The notary field is moved from ``ContractState`` into ``TransactionState``. - * Every transaction now has a *type* field, which specifies custom build & validation rules for that transaction type. - Currently two types are supported: General (runs the default build and validation logic) and NotaryChange ( - contract code is not run during validation, checks that the notary field is the only difference between the - inputs and outputs). - ``TransactionBuilder()`` is now abstract, you should use ``TransactionType.General.Builder()`` for building transactions. - -* The cash contract has moved from ``net.corda.contracts`` to ``net.corda.contracts.cash`` -* ``Amount`` class is now generic, to support non-currency types such as physical assets. Where you previously had just - ``Amount``, you should now use ``Amount``. -* Refactored the Cash contract to have a new FungibleAsset superclass, to model all countable assets that can be merged - and split (currency, barrels of oil, etc.) -* Messaging: - - * ``addMessageHandler`` now has a different signature as part of error handling changes. - * If you want to return nothing to a protocol, use ``Ack`` instead of ``Unit`` from now on. - -* In the IRS contract, dateOffset is now an integer instead of an enum. -* In contracts, you now use ``tx.getInputs`` and ``tx.getOutputs`` instead of ``getInStates`` and ``getOutStates``. This is - just a renaming. -* A new ``NonEmptySet`` type has been added for cases where you wish to express that you have a collection of unique - objects which cannot be empty. -* Please use the global ``newSecureRandom()`` function rather than instantiating your own SecureRandom's from now on, as - the custom function forces the use of non-blocking random drivers on Linux. - -Milestone 0 ------------ - -This is the first release, which includes: - -* Some initial smart contracts: cash, commercial paper, interest rate swaps -* An interest rate oracle -* The first version of the protocol/orchestration framework -* Some initial support for pluggable consensus mechanisms -* Tutorials and documentation explaining how it works -* Much more ... + Note: This means Corda can be slow to start up for the first time after upgrading from V3 to V4. \ No newline at end of file From 4e98ea3bbf8987530cd52b20fc0bb0167ab965ee Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 20 Feb 2019 20:56:08 +0100 Subject: [PATCH 011/159] Docs: repair a lot of broken docs pages. --- docs/source/api-states.rst | 2 +- docs/source/app-upgrade-notes.rst | 4 +- docs/source/cipher-suites.rst | 6 +- docs/source/docker.rst | 7 - docs/source/event-scheduling.rst | 2 +- docs/source/flow-state-machines.rst | 12 +- docs/source/oracles.rst | 20 +-- docs/source/quickstart-index.rst | 124 +++++++++--------- docs/source/tutorial-custom-notary.rst | 4 +- docs/source/tutorial-observer-nodes.rst | 2 +- docs/source/tutorial-tear-offs.rst | 2 +- .../corda/irs/api/OracleNodeTearOffTests.kt | 4 +- 12 files changed, 91 insertions(+), 98 deletions(-) delete mode 100644 docs/source/docker.rst diff --git a/docs/source/api-states.rst b/docs/source/api-states.rst index c3cdb75416..5f62608e04 100644 --- a/docs/source/api-states.rst +++ b/docs/source/api-states.rst @@ -155,7 +155,7 @@ and methods. For example, here is the relatively complex definition for a state .. container:: codeset - .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt + .. literalinclude:: ../../finance/contracts/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index 879f566e6a..15bee7a3c1 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -401,8 +401,8 @@ Step 12. Possibly update vault state queries In Corda 4 queries made on a node's vault can filter by the relevancy of those states to the node. As this functionality does not exist in Corda 3, apps will continue to receive all states in any vault queries. However, it may make sense to migrate queries expecting just those states relevant -to the node in question to query for only relevant states. See :doc:`api-vault-query.rst` for more details on how to do this. Not doing this -may result in queries returning more states than expected if the node is using observer functionality (see ":doc:`tutorial-observer-nodes.rst`"). +to the node in question to query for only relevant states. See :doc:`api-vault-query` for more details on how to do this. Not doing this +may result in queries returning more states than expected if the node is using observer functionality (see ":doc:`tutorial-observer-nodes`"). Step 13. Explore other new features that may be useful ------------------------------------------------------ diff --git a/docs/source/cipher-suites.rst b/docs/source/cipher-suites.rst index 8f2d313c45..25e4c99664 100644 --- a/docs/source/cipher-suites.rst +++ b/docs/source/cipher-suites.rst @@ -15,9 +15,9 @@ carefully selected based on various factors, such as provided security-level and with various HSM vendors, algorithm standardisation, variety of cryptographic primitives, business demand, option for post-quantum resistance, side channel security, efficiency and rigorous testing. -Before we present the pool of supported schemes it is useful to be familiar with :doc:`key-concepts-identity`, -:doc:`permissioning` and :doc:`api-identity`. An important design decision in Corda is its shared hierarchy -between the TLS and Node Identity certificates. +Before we present the pool of supported schemes it is useful to be familiar with :doc:`permissioning` +and :doc:`api-identity`. An important design decision in Corda is its shared hierarchy between the +TLS and Node Identity certificates. Certificate hierarchy --------------------- diff --git a/docs/source/docker.rst b/docs/source/docker.rst deleted file mode 100644 index 3315b5cb57..0000000000 --- a/docs/source/docker.rst +++ /dev/null @@ -1,7 +0,0 @@ -Docker -===== - -.. toctree:: - :maxdepth: 1 - - docker-image diff --git a/docs/source/event-scheduling.rst b/docs/source/event-scheduling.rst index 34633c61dc..cc356ffb71 100644 --- a/docs/source/event-scheduling.rst +++ b/docs/source/event-scheduling.rst @@ -67,7 +67,7 @@ Let's take an example of the interest rate swap fixings for our scheduled events .. container:: codeset - .. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/contract/IRS.kt + .. literalinclude:: ../../samples/irs-demo/cordapp/contracts-irs/src/main/kotlin/net/corda/irs/contract/IRS.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 diff --git a/docs/source/flow-state-machines.rst b/docs/source/flow-state-machines.rst index 8c8c217153..bd2fd638d7 100644 --- a/docs/source/flow-state-machines.rst +++ b/docs/source/flow-state-machines.rst @@ -93,7 +93,7 @@ Our flow has two parties (B and S for buyer and seller) and will proceed as foll transaction in B's local vault, and then sending it on to S who also checks it and commits the transaction to S's local vault. -You can find the implementation of this flow in the file ``finance/src/main/kotlin/net/corda/finance/TwoPartyTradeFlow.kt``. +You can find the implementation of this flow in the file ``finance/workflows/src/main/kotlin/net/corda/finance/TwoPartyTradeFlow.kt``. Assuming no malicious termination, they both end the flow being in possession of a valid, signed transaction that represents an atomic asset swap. @@ -201,7 +201,7 @@ Let's implement the ``Seller.call`` method that will be run when the flow is inv .. container:: codeset - .. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt + .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt :language: kotlin :start-after: DOCSTART 4 :end-before: DOCEND 4 @@ -237,7 +237,7 @@ OK, let's do the same for the buyer side: .. container:: codeset - .. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt + .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 @@ -367,7 +367,7 @@ override ``checkTransaction()`` to add our own custom validation logic: .. container:: codeset - .. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt + .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt :language: kotlin :start-after: DOCSTART 5 :end-before: DOCEND 5 @@ -454,7 +454,7 @@ A flow might declare some steps with code inside the flow class like this: .. container:: codeset - .. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt + .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 @@ -478,7 +478,7 @@ is a good idea, as that will help the users see what is coming up. You can pre-c .. container:: codeset - .. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt + .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt :language: kotlin :start-after: DOCSTART 3 :end-before: DOCEND 3 diff --git a/docs/source/oracles.rst b/docs/source/oracles.rst index e9c7198c84..117d08cce0 100644 --- a/docs/source/oracles.rst +++ b/docs/source/oracles.rst @@ -101,12 +101,12 @@ class that binds it to the network layer. Here is an extract from the ``NodeInterestRates.Oracle`` class and supporting types: -.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt +.. literalinclude:: ../../finance/contracts/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 -.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt +.. literalinclude:: ../../finance/contracts/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 @@ -166,7 +166,7 @@ parameter and ``CommandData`` classes. Let's see how the ``sign`` method for ``NodeInterestRates.Oracle`` is written: -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/api/NodeInterestRates.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 @@ -192,7 +192,7 @@ Binding to the network The first step is to create the oracle as a service by annotating its class with ``@CordaService``. Let's see how that's done: -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/api/NodeInterestRates.kt :language: kotlin :start-after: DOCSTART 3 :end-before: DOCEND 3 @@ -201,7 +201,7 @@ done: The Corda node scans for any class with this annotation and initialises them. The only requirement is that the class provide a constructor with a single parameter of type ``ServiceHub``. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/api/NodeInterestRates.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 @@ -219,7 +219,7 @@ We mentioned the client sub-flow briefly above. They are the mechanism that cli use to interact with your oracle. Typically there will be one for querying and one for signing. Let's take a look at those for ``NodeInterestRates.Oracle``. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/flows/RatesFixFlow.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/flows/RatesFixFlow.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 @@ -238,7 +238,7 @@ The oracle is invoked through sub-flows to query for values, add them to the tra the transaction signed by the oracle. Following on from the above examples, this is all encapsulated in a sub-flow called ``RatesFixFlow``. Here's the ``call`` method of that flow. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/flows/RatesFixFlow.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/flows/RatesFixFlow.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 @@ -255,7 +255,7 @@ As you can see, this: Here's an example of it in action from ``FixingFlow.Fixer``. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/flows/FixingFlow.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 @@ -276,10 +276,10 @@ containing your oracle service. You can then write tests on your mock network to verify the nodes interact with your Oracle correctly. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 :dedent: 4 -See `here `_ for more examples. +See `here `_ for more examples. diff --git a/docs/source/quickstart-index.rst b/docs/source/quickstart-index.rst index af01a37576..3b3abb4382 100644 --- a/docs/source/quickstart-index.rst +++ b/docs/source/quickstart-index.rst @@ -16,86 +16,86 @@ I want to: Learn about Corda for the first time ------------------------------------ -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| Useful links | Description | -+============================================+=========================================================================================================+ -| :doc:`key-concepts` | The key concepts and features of the Corda Platform | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`getting-set-up` | Set up your machine for running and developing CorDapps | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`tutorial-cordapp` | A guide to running a simple CorDapp | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| Useful links | Description | ++============================================+============================================================================================+ +| :doc:`key-concepts` | The key concepts and features of the Corda Platform | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`getting-set-up` | Set up your machine for running and developing CorDapps | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`tutorial-cordapp` | A guide to running a simple CorDapp | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ .. _quickstart-develop: Develop a CorDapp ----------------- -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| Useful links | Description | -+============================================+=========================================================================================================+ -| :doc:`hello-world-introduction` | A coding walk-through of a basic CorDapp | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`cordapp-overview` | An introduction to CordApps | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`writing-a-cordapp` | How to structure a CorDapp project | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`cordapp-build-systems` | How to build a CorDapp | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`corda-api` | A guide to the CorDapp API | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| Useful links | Description | ++============================================+============================================================================================+ +| :doc:`hello-world-introduction` | A coding walk-through of a basic CorDapp | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`cordapp-overview` | An introduction to CordApps | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`writing-a-cordapp` | How to structure a CorDapp project | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`cordapp-build-systems` | How to build a CorDapp | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`corda-api` | A guide to the CorDapp API | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ .. _quickstart-run: Run and test a CorDapp on local Corda network --------------------------------------------- -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| Useful links | Description | -+============================================+=========================================================================================================+ -| :doc:`generating-a-node` | Guidance on creating Corda nodes for development and testing locally and on Docker | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`node-structure` | The Corda node folder structure and how to name your node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`running-a-node` | Guidance on running Corda nodes locally and on Docker | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`setting-up-a-corda-network` | Considerations for setting up a Corda network | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`node-administration` | How to monitor a Corda node using an RPC interface | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| Useful links | Description | ++================================================+========================================================================================+ +| :doc:`generating-a-node` | Guidance on creating Corda nodes for development and testing locally and on Docker | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`node-structure` | The Corda node folder structure and how to name your node | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`running-a-node` | Guidance on running Corda nodes locally and on Docker | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`setting-up-a-dynamic-compatibility-zone` | Considerations for setting up a Corda network | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`node-administration` | How to monitor a Corda node using an RPC interface | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node | ++------------------------------------------------+----------------------------------------------------------------------------------------+ .. _quickstart-add: Add a node to an existing test Corda network -------------------------------------------- -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| Useful links | Description | -+============================================+=========================================================================================================+ -| :doc:`node-structure` | The Corda node folder structure and how to name your node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`deploying-a-node` | A step-by-step guide on deploying a Corda node to your own server | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`azure-vm` | A step-by-step guide on creating a Corda Network on Azure | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`aws-vm` | A step-by-step guide on creating a Corda Network on AWS | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`node-administration` | How to monitor a Corda node using an RPC interface | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`blob-inspector` | A troubleshooting tool allowing you to read the contents of a binary blob file | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| Useful links | Description | ++============================================+============================================================================================+ +| :doc:`node-structure` | The Corda node folder structure and how to name your node | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`deploying-a-node` | A step-by-step guide on deploying a Corda node to your own server | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`azure-vm` | A step-by-step guide on creating a Corda Network on Azure | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`aws-vm` | A step-by-step guide on creating a Corda Network on AWS | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`node-administration` | How to monitor a Corda node using an RPC interface | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`blob-inspector` | A troubleshooting tool allowing you to read the contents of a binary blob file | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ .. _quickstart-production: diff --git a/docs/source/tutorial-custom-notary.rst b/docs/source/tutorial-custom-notary.rst index cf7b78a0ff..4c96751a3e 100644 --- a/docs/source/tutorial-custom-notary.rst +++ b/docs/source/tutorial-custom-notary.rst @@ -11,7 +11,7 @@ This will ensure that it is recognised as a notary service. The custom notary service class should provide a constructor with two parameters of types ``ServiceHubInternal`` and ``PublicKey``. Note that ``ServiceHubInternal`` does not provide any API stability guarantees. -.. literalinclude:: ../../samples/notary-demo/src/main/kotlin/net/corda/notarydemo/MyCustomNotaryService.kt +.. literalinclude:: ../../samples/notary-demo/workflows/src/main/kotlin/net/corda/notarydemo/MyCustomNotaryService.kt :language: kotlin :start-after: START 1 :end-before: END 1 @@ -20,7 +20,7 @@ The next step is to write a notary service flow. You are free to copy and modify as ``ValidatingNotaryFlow``, ``NonValidatingNotaryFlow``, or implement your own from scratch (following the ``NotaryFlow.Service`` template). Below is an example of a custom flow for a *validating* notary service: -.. literalinclude:: ../../samples/notary-demo/src/main/kotlin/net/corda/notarydemo/MyCustomNotaryService.kt +.. literalinclude:: ../../samples/notary-demo/workflows/src/main/kotlin/net/corda/notarydemo/MyCustomNotaryService.kt :language: kotlin :start-after: START 2 :end-before: END 2 diff --git a/docs/source/tutorial-observer-nodes.rst b/docs/source/tutorial-observer-nodes.rst index 9d3cdcfe75..3e70892897 100644 --- a/docs/source/tutorial-observer-nodes.rst +++ b/docs/source/tutorial-observer-nodes.rst @@ -17,7 +17,7 @@ Just define a new flow that wraps the SendTransactionFlow/ReceiveTransactionFlow .. container:: codeset - .. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt + .. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/flows/AutoOfferFlow.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 diff --git a/docs/source/tutorial-tear-offs.rst b/docs/source/tutorial-tear-offs.rst index 926c552293..b850ccb5f9 100644 --- a/docs/source/tutorial-tear-offs.rst +++ b/docs/source/tutorial-tear-offs.rst @@ -49,7 +49,7 @@ transaction components is exactly the same. Note that unlike ``WireTransaction`` The following code snippet is taken from ``NodeInterestRates.kt`` and implements a signing part of an Oracle. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/api/NodeInterestRates.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 diff --git a/samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt b/samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt index 93ccb99ff6..e77a092913 100644 --- a/samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt +++ b/samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt @@ -68,7 +68,7 @@ class OracleNodeTearOffTests { mockNet.stopNodes() } - // DOCSTART 1 + // DOCSTART 2 @Test fun `verify that the oracle signs the transaction if the interest rate within allowed limit`() { // Create a partial transaction @@ -93,7 +93,7 @@ class OracleNodeTearOffTests { // Check that the transaction has been signed by the oracle assertContains(fix.signers, oracle.owningKey) } - // DOCEND 1 + // DOCEND 2 @Test fun `verify that the oracle rejects the transaction if the interest rate is outside the allowed limit`() { From cac9b1cfc7f132834ff18cfe341c52fbb60ee53b Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 20 Feb 2019 21:06:14 +0100 Subject: [PATCH 012/159] Docs: note that Corda 3.3 needs a bugfix to be usable with Corda 4 --- docs/source/release-notes.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index d29bc146b2..0ea2fa703d 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -11,15 +11,19 @@ the making, but we think you'll agree worth the wait. Just as prior releases have brought with them commitments to wire and API stability, Corda 4 comes with those same guarantees. States and apps valid in Corda 3 are transparently usable in Corda 4. -.. important:: We strongly recommend reading ":doc:`app-upgrade-notes`". This covers the upgrade procedure, - along with how you can adjust your app to opt-in to new features making your app more secure and - easier to upgrade. +We strongly recommend reading ":doc:`app-upgrade-notes`". This covers the upgrade procedure, +along with how you can adjust your app to opt-in to new features making your app more secure and +easier to upgrade. Additionally, be aware that the data model upgrades are changes to the Corda consensus rules. To use apps that benefit from them, *all* nodes in a compatibility zone must be upgraded and the zone must be enforcing that upgrade. This may take time in large zones like the testnet. Please take this into account for your own schedule planning. +.. warning:: There is a bug in Corda 3.3 that causes problems when receiving a ``FungibleState`` created + by Corda 4. There will shortly be a followup Corda 3.4 release that corrects this error. Interop between + Corda 3 and Corda 4 will require that Corda 3 users are on the latest patchlevel release. + .. contents:: Changes for developers in Corda 4 From 01f0c4b3d280f03e62c2c908881e2eb894a6c6ec Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 20 Feb 2019 21:08:18 +0100 Subject: [PATCH 013/159] Docs: minor change to the release notes. Babies don't typically get delayed by a few months due to backwards compatibility fixes so the opening no longer made sense :) --- docs/source/release-notes.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 0ea2fa703d..5b5192ee2c 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -3,19 +3,19 @@ Release notes for Corda 4 .. _release_notes_v4_0: -Here we are, 11 months and 1500 plus commits later... and it's a bouncing baby software release! +Welcome to the Corda 4 release notes. Please read these carefully to understand what's new in this +release and how the changes can help you. Just as prior releases have brought with them commitments +to wire and API stability, Corda 4 comes with those same guarantees. States and apps valid in +Corda 3 are transparently usable in Corda 4. -We are really proud to release Corda 4 to the open source community today. It's been a long time in -the making, but we think you'll agree worth the wait. +For app developers, we strongly recommend reading ":doc:`app-upgrade-notes`". This covers the upgrade +procedure, along with how you can adjust your app to opt-in to new features making your app more secure and +easier to upgrade in future. -Just as prior releases have brought with them commitments to wire and API stability, Corda 4 -comes with those same guarantees. States and apps valid in Corda 3 are transparently usable in Corda 4. +For node operators, we recommend reading ":doc:`node-upgrade-notes`". The upgrade procedure is simple but +it can't hurt to read the instructions anyway. -We strongly recommend reading ":doc:`app-upgrade-notes`". This covers the upgrade procedure, -along with how you can adjust your app to opt-in to new features making your app more secure and -easier to upgrade. - -Additionally, be aware that the data model upgrades are changes to the Corda consensus rules. To use +Additionally, be aware that the data model improvements are changes to the Corda consensus rules. To use apps that benefit from them, *all* nodes in a compatibility zone must be upgraded and the zone must be enforcing that upgrade. This may take time in large zones like the testnet. Please take this into account for your own schedule planning. From c5c4df303739f6cc5801c24db4694d3aa97cfc98 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Thu, 21 Feb 2019 11:07:23 +0100 Subject: [PATCH 014/159] Docs: address review comments --- docs/source/changelog.rst | 8 +++++--- docs/source/getting-set-up.rst | 15 ++++++--------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index c9f4247900..6c3f946af0 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -19,7 +19,7 @@ Version 4.0 * New configuration property ``database.initialiseAppSchema`` with values ``UPDATE``, ``VALIDATE`` and ``NONE``. The property controls the behavior of the Hibernate DDL generation. ``UPDATE`` performs an update of CorDapp schemas, while - ``VALID`` only verifies their integrity. The property does not affect the node-specific DDL handling and + ``VALIDATE`` only verifies their integrity. The property does not affect the node-specific DDL handling and complements ``database.initialiseSchema`` to disable DDL handling altogether. * ``JacksonSupport.createInMemoryMapper`` was incorrectly marked as deprecated and is no longer so. @@ -305,7 +305,9 @@ Version 4.0 * Finance CorDapps are now built as sealed and signed JAR files. Custom classes can no longer be placed in the packages defined in either finance Cordapp or access it's non-public members. -* Finance CorDapp was split into two separate apps: ``corda-finance-contracts`` and ``corda-finance-workflows``. There is no longer a single cordapp which provides both. +* Finance CorDapp was split into two separate apps: ``corda-finance-contracts`` and ``corda-finance-workflows``. There is + no longer a single cordapp which provides both. You need to have both JARs installed in the node simultaneously for the + app to work however. * All sample CorDapps were split into separate apps: workflows and contracts to reflect new convention. It is recommended to structure your CorDapps this way, see :doc:`app-upgrade-notes` on upgrading your CorDapp. @@ -339,5 +341,5 @@ Version 4.0 The only exception to this is ``Interpolator`` and related classes. These are now in the `IRS demo workflows CorDapp `_. -* Vault states are now correctly migrated when moving from V3 to V4. In particular, this means the relevancy column is correctly filled, and the state party table is populated. +* Vault states are migrated when moving from V3 to V4: the relevancy column is correctly filled, and the state party table is populated. Note: This means Corda can be slow to start up for the first time after upgrading from V3 to V4. \ No newline at end of file diff --git a/docs/source/getting-set-up.rst b/docs/source/getting-set-up.rst index 1931cdf1dd..7e727046fb 100644 --- a/docs/source/getting-set-up.rst +++ b/docs/source/getting-set-up.rst @@ -3,20 +3,17 @@ Getting set up for CorDapp development Software requirements --------------------- + Corda uses industry-standard tools: -* **Oracle JDK 8 JVM** - minimum supported version **|java_version|** (please note that +* **Java 8 JVM** - we require at least version **|java_version|**, but do not currently support Java 9 or higher. + We have tested with Oracle JDK, Amazon Corretto, and Red Hat's OpenJDK builds. Please note that OpenJDK builds + usually exclude JavaFX, which our GUI tools require. * **IntelliJ IDEA** - supported versions **2017.x** and **2018.x** (with Kotlin plugin version |kotlin_version|) -* **Git** - -We also use Gradle and Kotlin, but you do not need to install them. A standalone Gradle wrapper is provided, and it -will download the correct version of Kotlin. +* **Gradle** - we use 4.10 and the ``gradlew`` script in the project / samples directories will download it for you. Please note: -* Corda runs in a JVM. JVM implementations other than Oracle JDK 8 are not actively supported. However, if you do - choose to use OpenJDK, you will also need to install OpenJFX - * Applications on Corda (CorDapps) can be written in any language targeting the JVM. However, Corda itself and most of the samples are written in Kotlin. Kotlin is an `official Android language `_, and you can read more about why @@ -131,7 +128,7 @@ Jetbrains offers a pre-built snap package that allows for easy, one-step install .. _fedora-label: Fedora -------------- +------ .. warning:: If you are using a Mac, Windows or Debian/Ubuntu machine, please follow the :ref:`mac-label`, :ref:`windows-label` or :ref:`deb-ubuntu-label` instructions instead. From 800697a907f7bbaa0d2b73158aa791dc425b1e51 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Thu, 21 Feb 2019 15:27:08 +0000 Subject: [PATCH 015/159] CORDA-2654: Repair the DJVM CLI tool. (#4796) * Replace SandboxedRunnable with Function interface. Remove DJVM from "Key Concepts" release notes. Update installation of shell tool. Fix broken sandbox package names. Make sure we only resolve each class once when loading. Also remove some unused default parameter values. Don't discard "bootstrap" sandbox.* classes because SourceClassLoader may need them. * Restore DJVM to the "Key Concepts" docs. * Remove all mention of "whitelisting" from the DJVM CLI. --- djvm/build.gradle | 16 ---- .../net/corda/djvm/tools/cli/ClassCommand.kt | 13 +-- .../net/corda/djvm/tools/cli/Commands.kt | 3 +- .../net/corda/djvm/tools/cli/NewCommand.kt | 8 +- .../net/corda/djvm/tools/cli/Utilities.kt | 4 +- .../corda/djvm/tools/cli/WhitelistCommand.kt | 14 --- .../tools/cli/WhitelistGenerateCommand.kt | 92 ------------------- .../djvm/tools/cli/WhitelistShowCommand.kt | 33 ------- djvm/shell/djvm | 2 +- djvm/shell/install | 2 +- .../corda/djvm/rewiring/SandboxClassLoader.kt | 2 +- .../net/corda/djvm/source/ClassSource.kt | 4 +- docs/source/key-concepts-djvm.rst | 34 ++----- 13 files changed, 21 insertions(+), 206 deletions(-) delete mode 100644 djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistCommand.kt delete mode 100644 djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistGenerateCommand.kt delete mode 100644 djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistShowCommand.kt diff --git a/djvm/build.gradle b/djvm/build.gradle index 64bd5bbd53..fd207b04a5 100644 --- a/djvm/build.gradle +++ b/djvm/build.gradle @@ -51,22 +51,6 @@ shadowJar { baseName 'corda-djvm' classifier '' relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm' - - // These particular classes are only needed to "bootstrap" - // the compilation of the other sandbox classes. At runtime, - // we will generate better versions from deterministic-rt.jar. - exclude 'sandbox/java/lang/Appendable.class' - exclude 'sandbox/java/lang/CharSequence.class' - exclude 'sandbox/java/lang/Character\$Subset.class' - exclude 'sandbox/java/lang/Character\$Unicode*.class' - exclude 'sandbox/java/lang/Comparable.class' - exclude 'sandbox/java/lang/Enum.class' - exclude 'sandbox/java/lang/Iterable.class' - exclude 'sandbox/java/lang/StackTraceElement.class' - exclude 'sandbox/java/lang/StringBuffer.class' - exclude 'sandbox/java/lang/StringBuilder.class' - exclude 'sandbox/java/nio/**' - exclude 'sandbox/java/util/**' } assemble.dependsOn shadowJar diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt index e0fdb7f745..db6460a2a8 100644 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt +++ b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt @@ -32,14 +32,6 @@ abstract class ClassCommand : CommandBase() { @Option(names = ["--ignore-definition-providers"], description = ["Disable all definition providers."]) var ignoreDefinitionProviders: Boolean = false - @Option( - names = ["-w", "--whitelist"], - description = ["Override the default whitelist. Use provided whitelist instead. If NONE is provided, the " + - "whitelist will be ignored. If ALL is provided, all references will be whitelisted. LANG can be " + - "used to only whitelist select classes and their members from the java.lang package."] - ) - var whitelist: Path? = null - @Option(names = ["-c", "--classpath"], description = ["Additions to the default class path."], split = ":") var classPath: Array = emptyArray() @@ -65,8 +57,6 @@ abstract class ClassCommand : CommandBase() { protected var executor = SandboxExecutor(SandboxConfiguration.DEFAULT) - private var derivedWhitelist: Whitelist = Whitelist.MINIMAL - abstract fun processClasses(classes: List>) open fun printSuccess(classes: List>) {} @@ -74,8 +64,7 @@ abstract class ClassCommand : CommandBase() { override fun validateArguments() = filters.isNotEmpty() override fun handleCommand(): Boolean { - derivedWhitelist = whitelistFromPath(whitelist) - val configuration = getConfiguration(derivedWhitelist) + val configuration = getConfiguration(Whitelist.MINIMAL) classLoader = SourceClassLoader(getClasspath(), configuration.analysisConfiguration.classResolver) createExecutor(configuration) diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt index 65dd4c9ed4..b76fc3b6db 100644 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt +++ b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt @@ -15,8 +15,7 @@ import picocli.CommandLine.Command NewCommand::class, RunCommand::class, ShowCommand::class, - TreeCommand::class, - WhitelistCommand::class + TreeCommand::class ] ) @Suppress("KDocMissingDocumentation") diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt index 8e6302de4e..22c6e96aee 100644 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt +++ b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt @@ -55,13 +55,13 @@ class NewCommand : CommandBase() { companion object { val TEMPLATE = """ - |package net.corda.djvm; + |package net.corda.sandbox; | - |import net.corda.djvm.execution.SandboxedRunnable; + |import java.util.function.Function; | - |public class [NAME] implements SandboxedRunnable<[FROM], [TO]> { + |public class [NAME] implements Function<[FROM], [TO]> { | @Override - | public [TO] run([FROM] input) { + | public [TO] apply([FROM] input) { | return [RETURN]; | } |} diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt index adc88a1423..d488aa7253 100644 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt +++ b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt @@ -60,7 +60,7 @@ fun openOptions(force: Boolean) = if (force) { * Get the path of where any generated code will be placed. Create the directory if it does not exist. */ fun createCodePath(): Path { - return Paths.get("tmp", "net", "corda", "djvm").let { + return Paths.get("tmp", "net", "corda", "sandbox").let { Files.createDirectories(it) } } @@ -92,7 +92,7 @@ val userClassPath: String = System.getProperty("java.class.path") /** * Get a reference of each concrete class that implements interface or class [T]. */ -inline fun find(scanSpec: String = "net/corda/djvm"): List> { +inline fun find(scanSpec: String = "net/corda/sandbox"): List> { return ClassGraph() .whitelistPaths(scanSpec) .enableAllInfo() diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistCommand.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistCommand.kt deleted file mode 100644 index 7671f9c246..0000000000 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistCommand.kt +++ /dev/null @@ -1,14 +0,0 @@ -package net.corda.djvm.tools.cli - -import picocli.CommandLine.Command - -@Command( - name = "whitelist", - description = ["Utilities and commands related to the whitelist for the deterministic sandbox."], - subcommands = [ - WhitelistGenerateCommand::class, - WhitelistShowCommand::class - ] -) -@Suppress("KDocMissingDocumentation") -class WhitelistCommand : CommandBase() diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistGenerateCommand.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistGenerateCommand.kt deleted file mode 100644 index 85a8084654..0000000000 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistGenerateCommand.kt +++ /dev/null @@ -1,92 +0,0 @@ -package net.corda.djvm.tools.cli - -import net.corda.djvm.analysis.AnalysisConfiguration -import net.corda.djvm.analysis.AnalysisContext -import net.corda.djvm.analysis.ClassAndMemberVisitor -import net.corda.djvm.references.ClassRepresentation -import net.corda.djvm.references.Member -import net.corda.djvm.source.ClassSource -import picocli.CommandLine.* -import java.io.PrintStream -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.StandardOpenOption -import java.util.zip.GZIPOutputStream - -@Command( - name = "generate", - description = ["Generate and export whitelist from the class and member declarations provided in one or more " + - "JARs."] -) -@Suppress("KDocMissingDocumentation") -class WhitelistGenerateCommand : CommandBase() { - - @Parameters(description = ["The paths of the JARs that the whitelist is to be generated from."]) - var paths: Array = emptyArray() - - @Option( - names = ["-o", "--output"], - description = ["The file to which the whitelist will be written. If not provided, STDOUT will be used."] - ) - var output: Path? = null - - override fun validateArguments() = paths.isNotEmpty() - - override fun handleCommand(): Boolean { - val entries = AnalysisConfiguration.createRoot().use { configuration -> - val entries = mutableListOf() - val visitor = object : ClassAndMemberVisitor(configuration, null) { - override fun visitClass(clazz: ClassRepresentation): ClassRepresentation { - entries.add(clazz.name) - return super.visitClass(clazz) - } - - override fun visitMethod(clazz: ClassRepresentation, method: Member): Member { - visitMember(clazz, method) - return super.visitMethod(clazz, method) - } - - override fun visitField(clazz: ClassRepresentation, field: Member): Member { - visitMember(clazz, field) - return super.visitField(clazz, field) - } - - private fun visitMember(clazz: ClassRepresentation, member: Member) { - entries.add("${clazz.name}.${member.memberName}:${member.signature}") - } - } - val context = AnalysisContext.fromConfiguration(configuration) - for (path in paths) { - ClassSource.fromPath(path).getStreamIterator().forEach { - visitor.analyze(it, context) - } - } - entries - } - output?.also { - Files.newOutputStream(it, StandardOpenOption.CREATE).use { out -> - GZIPOutputStream(out).use { gzip -> - PrintStream(gzip).use { pout -> - pout.println(""" - |java/.* - |javax/.* - |jdk/.* - |com/sun/.* - |sun/.* - |--- - """.trimMargin().trim()) - printEntries(pout, entries) - } - } - } - } ?: printEntries(System.out, entries) - return true - } - - private fun printEntries(stream: PrintStream, entries: List) { - for (entry in entries.sorted().distinct()) { - stream.println(entry) - } - } - -} diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistShowCommand.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistShowCommand.kt deleted file mode 100644 index 85ec05fdc1..0000000000 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistShowCommand.kt +++ /dev/null @@ -1,33 +0,0 @@ -package net.corda.djvm.tools.cli - -import picocli.CommandLine.* -import java.nio.file.Path - -@Command( - name = "show", - description = ["Print the whitelist used for the deterministic sandbox."] -) -@Suppress("KDocMissingDocumentation") -class WhitelistShowCommand : CommandBase() { - - @Option( - names = ["-w", "--whitelist"], - description = ["Override the default whitelist. Use provided whitelist instead."] - ) - var whitelist: Path? = null - - @Parameters(description = ["Words or phrases to use to filter down the result."]) - var filters: Array = emptyArray() - - override fun validateArguments() = true - - override fun handleCommand(): Boolean { - val whitelist = whitelistFromPath(whitelist) - val filters = filters.map(String::toLowerCase) - whitelist.items - .filter { item -> filters.all { it in item.toLowerCase() } } - .forEach { println(it) } - return true - } - -} diff --git a/djvm/shell/djvm b/djvm/shell/djvm index 5388332d2f..ec3c19e5a7 100755 --- a/djvm/shell/djvm +++ b/djvm/shell/djvm @@ -4,7 +4,7 @@ file="${BASH_SOURCE[0]}" linked_file="$(test -L "$file" && readlink "$file" || echo "$file")" base_dir="$(cd "$(dirname "$linked_file")/../" && pwd)" version="$(cat $base_dir/../build.gradle | sed -n 's/^[ ]*ext\.corda_release_version[ =]*"\([^"]*\)".*$/\1/p')" -jar_file="$base_dir/cli/build/libs/corda-djvm-$version-all.jar" +jar_file="$base_dir/cli/build/libs/corda-djvm-$version-cli.jar" CLASSPATH="${CLASSPATH:-}" diff --git a/djvm/shell/install b/djvm/shell/install index 4874c00583..7e458e0bb4 100755 --- a/djvm/shell/install +++ b/djvm/shell/install @@ -10,7 +10,7 @@ cd "$base_dir/.." # Generate auto-completion file for Bash and ZSH cd "$base_dir" -java -cp "$base_dir/../cli/build/libs/corda-djvm-$version-all.jar" \ +java -cp "$base_dir/../cli/build/libs/corda-djvm-$version-cli.jar" \ picocli.AutoComplete -n djvm net.corda.djvm.tools.cli.Commands -f # Create a symbolic link to the `djvm` utility diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt b/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt index c1b3475534..16ed666419 100644 --- a/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt +++ b/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt @@ -131,7 +131,7 @@ class SandboxClassLoader private constructor( if (!isSandboxClass || parent is SandboxClassLoader) { try { - clazz = super.loadClass(name, resolve) + clazz = super.loadClass(name, false) } catch (e: ClassNotFoundException) { } catch (e: SandboxClassLoadingException) { e.messages.clearProvisional() diff --git a/djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt b/djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt index 4cb15b9194..232519c9d5 100644 --- a/djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt +++ b/djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt @@ -11,8 +11,8 @@ import java.nio.file.Path * @property origin The origin of the class source, if any. */ class ClassSource private constructor( - val qualifiedClassName: String = "", - val origin: String? = null + val qualifiedClassName: String, + val origin: String? ) { val internalClassName: String = qualifiedClassName.asResourcePath diff --git a/docs/source/key-concepts-djvm.rst b/docs/source/key-concepts-djvm.rst index fec8be7483..a3c5755f1f 100644 --- a/docs/source/key-concepts-djvm.rst +++ b/docs/source/key-concepts-djvm.rst @@ -59,8 +59,8 @@ Abstraction ~~~~~~~~~~~ The sandbox is abstracted away as an executor which takes as input an implementation of the interface -``SandboxedRunnable``, dereferenced by a ``ClassSource``. This interface has a single method that -needs implementing, namely ``run(Input): Output``. +``Function``, dereferenced by a ``ClassSource``. This interface has a single method that +needs implementing, namely ``apply(Input): Output``. A ``ClassSource`` object referencing such an implementation can be passed into the ``SandboxExecutor`` together with an input of type ``Input``. The executor has operations for both execution and static @@ -68,7 +68,7 @@ validation, namely ``run()`` and ``validate()``. These methods both return a sum * In the case of execution, this summary object has information about: * Whether or not the runnable was successfully executed. - * If successful, the return value of ``SandboxedRunnable.run()``. + * If successful, the return value of ``Function.apply()``. * If failed, the exception that was raised. * And in both cases, a summary of all accrued costs during execution. @@ -80,7 +80,7 @@ validation, namely ``run()`` and ``validate()``. These methods both return a sum severity ``ERROR`` will prevent execution. The sandbox has a configuration that applies to the execution of a specific runnable. This configuration, on a higher -level, contains a set of rules, definition providers, emitters and a whitelist. +level, contains a set of rules, definition providers and emitters. .. image:: resources/djvm-overview.png @@ -107,7 +107,7 @@ work may well introduce additional constraints that we would want to place on th .. note:: It is worth noting that not only smart contract code is instrumented by the sandbox, but all code that it can - transitively reach. In particular this means that the Java runtime classes (that have not been whitelisted) and any + transitively reach. In particular this means that the Java runtime classes and any other library code used in the program are also instrumented and persisted ahead of time. @@ -129,15 +129,6 @@ tries to catch such exceptions, as doing so would allow the user to bypass the t profile. -Only Allow Explicitly Whitelisted Runtime API -............................................. - -Ensures that constant pool references are mapped against a verified subset of the Java runtime libraries. Said subset -excludes functionality that contract code should not have access to, such as native code. This whitelist has been -trimmed down to the bare minimum needed, a few classes in ``java.lang``, so that also the Java runtime libraries -themselves are subjected to the same amount of scrutiny that the rest of the code is. - - Disallow Dynamic Invocation ........................... @@ -154,9 +145,6 @@ network requests, general hardware interaction, threading, *etc.* These all cons allowing such code to be called arbitrarily from the JVM would require deterministic guarantees on the native machine code level. This falls out of scope for the DJVM. -Java runtime classes that call into native code and that are needed from within the sandbox environment, can be -whitelisted explicitly. - Disallow Finalizer Methods .......................... @@ -278,9 +266,8 @@ The loaded classes are further rewritten in two ways: Disable Synchronised Methods and Blocks ....................................... -Since Java's multi-threading API has been excluded from the whitelist, synchronised methods and code blocks have little -use in sandboxed code. Consequently, we log informational messages about occurrences of this in your sandboxed code and -automatically transform them into ordinary methods and code blocks instead. +The DJVM doesn't support multi-threading and so synchronised methods and code blocks have little +use in sandboxed code. Consequently, we automatically transform them into ordinary methods and code blocks instead. Future Work @@ -290,9 +277,6 @@ Further work is planned: * To enable controlled use of reflection APIs. - * Strip out the dependency on the extensive whitelist of underlying Java - runtime classes. - * Currently, dynamic invocation is disallowed. Allow specific lambda and string concatenation meta-factories used by Java code itself. @@ -351,7 +335,7 @@ This run will produce some output similar to this: The output should be pretty self-explanatory, but just to summarise: - * It prints out the return value from the ``SandboxedRunnable.run()`` method implemented in + * It prints out the return value from the ``Function.apply()`` method implemented in ``net.corda.sandbox.Hello``. * It also prints out the aggregated costs for allocations, invocations, jumps and throws. @@ -363,5 +347,3 @@ Other commands to be aware of are: * ``djvm inspect`` which allows you to inspect what byte code modifications will be applied to a class. * ``djvm show`` which displays the transformed byte code of a class, *i.e.*, the end result and not the difference. - - * ``djvm whitelist`` which displays the content of the whitelist in use. From 26c626f9893cd0b37de132aecfe673ad7c999e86 Mon Sep 17 00:00:00 2001 From: Tommy Lillehagen Date: Thu, 21 Feb 2019 18:22:49 +0000 Subject: [PATCH 016/159] CORDA-2664 - Update Docker docs (#4800) --- docs/source/docker-image.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/docker-image.rst b/docs/source/docker-image.rst index 19c4e8b9c4..3fd725eec7 100644 --- a/docs/source/docker-image.rst +++ b/docs/source/docker-image.rst @@ -20,7 +20,7 @@ In this example, the certificates are stored at ``/home/user/cordaBase/certifica -v /path/to/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-4.0:RELEASE + corda/corda-zulu-5.0-snapshot:latest As the node runs within a container, several mount points are required: @@ -55,7 +55,7 @@ In this example, we have previously generated a network-parameters file using th -v /home/user/sharedFolder/network-parameters:/opt/corda/network-parameters \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-4.0-snapshot:latest + corda/corda-zulu-5.0-snapshot:latest There is a new mount ``/home/user/sharedFolder/node-infos:/opt/corda/additional-node-infos`` which is used to hold the ``nodeInfo`` of all the nodes within the network. As the node within the container starts up, it will place it's own nodeInfo into this directory. This will allow other nodes also using this folder to see this new node. @@ -79,7 +79,7 @@ Joining TestNet -e LOCALITY="London" -e COUNTRY="GB" \ -v /home/user/docker/config:/etc/corda \ -v /home/user/docker/certificates:/opt/corda/certificates \ - corda/corda-4.0-snapshot:latest config-generator --testnet + corda/corda-zulu-5.0-snapshot:latest config-generator --testnet ``$MY_PUBLIC_ADDRESS`` will be the public address that this node will be advertised on. ``$ONE_TIME_DOWNLOAD_KEY`` is the one-time code provided for joining TestNet. @@ -104,7 +104,7 @@ It is now possible to start the node using the generated config and certificates -v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-4.0-snapshot:latest + corda/corda-zulu-5.0-snapshot:latest Joining an existing Compatibility Zone @@ -128,7 +128,7 @@ It is possible to configure the name of the Trust Root file by setting the ``TRU -e MY_EMAIL_ADDRESS="cordauser@r3.com" \ -v /home/user/docker/config:/etc/corda \ -v /home/user/docker/certificates:/opt/corda/certificates \ - corda/corda-4.0-snapshot:latest config-generator --generic + corda/corda-zulu-5.0-snapshot:latest config-generator --generic Several environment variables must also be passed to the container to allow it to register: @@ -159,5 +159,5 @@ Once the container has finished performing the initial registration, the node ca -v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-4.0-snapshot:latest + corda/corda-zulu-5.0-snapshot:latest From 08ce12f068f12e3ed14d4baeb366c80dfbe74da5 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Tue, 19 Feb 2019 12:09:08 +0000 Subject: [PATCH 017/159] CORDA-2610: Internal APIs made much clearer in API doc page --- docs/source/corda-api.rst | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/docs/source/corda-api.rst b/docs/source/corda-api.rst index 211afbb484..4321741dd4 100644 --- a/docs/source/corda-api.rst +++ b/docs/source/corda-api.rst @@ -42,10 +42,10 @@ The following modules form part of Corda's public API and we commit to API/ABI b * **Core (net.corda.core)**: core Corda libraries such as crypto functions, types for Corda's building blocks: states, contracts, transactions, attachments, etc. and some interfaces for nodes and protocols * **Client RPC (net.corda.client.rpc)**: client RPC * **Client Jackson (net.corda.client.jackson)**: JSON support for client applications -* **Test Utils (net.corda.testing.core)**: generic test utilities -* **Test Node Driver (net.corda.testing.node, net.corda.testing.driver)**: test utilities to run nodes programmatically -* **Http Test Utils (net.corda.testing.http)**: a small set of utilities for making HttpCalls, aimed at demos and tests. * **DSL Test Utils (net.corda.testing.dsl)**: a simple DSL for building pseudo-transactions (not the same as the wire protocol) for testing purposes. +* **Test Node Driver (net.corda.testing.node, net.corda.testing.driver)**: test utilities to run nodes programmatically +* **Test Utils (net.corda.testing.core)**: generic test utilities +* **Http Test Utils (net.corda.testing.http)**: a small set of utilities for making HttpCalls, aimed at demos and tests. * **Dummy Contracts (net.corda.testing.contracts)**: dummy state and contracts for testing purposes * **Mock Services (net.corda.testing.services)**: mock service implementations for testing purposes @@ -57,7 +57,7 @@ Non-public API (experimental) The following modules are not part of the Corda's public API and no backwards compatibility guarantees are provided. They are further categorized in 2 classes: * the incubating modules, for which we will do our best to minimise disruption to developers using them until we are able to graduate them into the public API -* the unstable modules, which are available but we do not commit to their stability or continuation in any sense +* the internal modules, which are not to be used, and will change without notice Corda incubating modules ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -68,31 +68,14 @@ Corda incubating modules * **net.corda.client.mock**: client mock utilities * **Cordformation**: Gradle integration plugins -Corda unstable modules +Corda internal modules ~~~~~~~~~~~~~~~~~~~~~~ -* **net.corda.buildSrc**: necessary gradle plugins to build Corda -* **net.corda.node**: core code of the Corda node (eg: node driver, node services, messaging, persistence) -* **net.corda.node.api**: data structures shared between the node and the client module, e.g. types sent via RPC -* **net.corda.samples.network.visualiser**: a network visualiser that uses a simulation to visualise the interaction and messages between nodes on the Corda network -* **net.corda.samples.demos.attachment**: demonstrates sending a transaction with an attachment from one to node to another, and the receiving node accessing the attachment -* **net.corda.samples.demos.bankofcorda**: simulates the role of an asset issuing authority (eg. central bank for cash) -* **net.corda.samples.demos.irs**: demonstrates an Interest Rate Swap agreement between two banks -* **net.corda.samples.demos.notary**: a simple demonstration of a node getting multiple transactions notarised by a distributed (Raft or BFT SMaRt) notary -* **net.corda.samples.demos.simmvaluation**: A demo of SIMM valuation and agreement on a distributed ledger -* **net.corda.samples.demos.trader**: demonstrates four nodes, a notary, an issuer of cash (Bank of Corda), and two parties trading with each other, exchanging cash for a commercial paper -* **net.corda.node.smoke.test.utils**: test utilities for smoke testing -* **net.corda.node.test.common**: common test functionality -* **net.corda.tools.demobench**: a GUI tool that allows to run Corda nodes locally for demonstrations -* **net.corda.tools.explorer**: a GUI front-end for Corda -* **net.corda.tools.graphs**: utilities to infer project dependencies -* **net.corda.tools.loadtest**: Corda load tests -* **net.corda.webserver**: is a servlet container for CorDapps that export HTTP endpoints. This server is an RPC client of the node -* **net.corda.sandbox-creator**: sandbox utilities -* **net.corda.quasar.hook**: agent to hook into Quasar and provide types exclusion lists +Everything else is internal and will change without notice, even deleted, and should not be used. This also includes any package that has +``.internal`` in it. So for example, ``net.corda.core.internal`` and sub-packages should not be used. -.. warning:: Code inside any package in the ``net.corda`` namespace which contains ``.internal`` or in ``net.corda.node`` for internal use only. - Future releases will reject any CorDapps that use types from these packages. +Some of the public modules may depend on internal modules, so be careful to not rely on these transitive dependencies. In particular, the +testing modules depend on the node module and so you may end having the node in your test classpath. .. warning:: The web server module will be removed in future. You should call Corda nodes through RPC from your web server of choice e.g., Spring Boot, Vertx, Undertow. From b8ff7aa073f6ed1d16b08b14ff08828ac1d04b00 Mon Sep 17 00:00:00 2001 From: James Brown Date: Sat, 16 Feb 2019 17:40:35 +0000 Subject: [PATCH 018/159] CORDA-2604 update certificate structure diagram --- .../resources/certificate_structure.png | Bin 373728 -> 168740 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/source/resources/certificate_structure.png b/docs/source/resources/certificate_structure.png index 5b98df08591c684fce80278f2b24c97742321a4e..fb71dc7847dd469046a48dd2d3af52be5ca4b7d4 100644 GIT binary patch literal 168740 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

$&6 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

#8`+qu{^!`v&d z>UMzGHB6t51&r}hTL43u{4gIh9!d^5Ytn(p@Iylhf7KAMT@G@d&vk7JG*I8Lv9!H$ z?OGg=wDD!Vfq{YkZtA$bXy&~uiS(8T zl8^lhZAo`Ab?YnO4%u&d$H1j?DZ%~Fx73KLpc5#1R8=Ut+>?fkZU7Mlvq$9J#z|APYXN@8Lc7i0?11=UKvf}iaGjRY~ zPz)uX2!NG+nx32D#H+{-iU)4Fd}eRD*2l(MC|ADI^X0XMaaGq?25?(x*0W%4%p%&I;zCSr*oUgh{d z;7%bFGivj}_JPo*1Qh%e*S2kOKyPL6Ld`{9UTO6lm@M!C)lX#L2WR^Cj}NqH(Z(P| z+i1zQVZ(WfJf-K53kv9%OYsB%XDoH)dJHOR%|7lmT^At&RSkmU2YXap%SA3FNbPG& z%RA+9fnGG}mFzXNpf^2Y+55rLorCl7D`nY+_>%boi~bIVW5pyb%I7#>y;I@S^1Ow! z&&w)B&%W}OZ&<-3_zqo@{AD@Ps%Ovg{=r=yA(+5tx55XiQ)3SL1zP^ zAiWn4hwD00yamCo$N)i@PQ;v5^BUdB*$czE9;$(C|cdy`nk|>$uy(y(%?d# z_(-aV$J{u*;*viCD1?a+O0+==jFefoU_dDe9{~Ui$_3OkgDx;-kTkUN?D^=PgY&6V z=$=FA?TwkQ`?J&Xq$hbB1Qpd)=el1#Ld;0+6bdF;?S%*rvI_MOy+Vp3ZoAy|?-)ju zRO+eEFraVRy7kj{*Uib7oT`8gD~ns4?!ec>MZz|%*{gEzlHweN)CK4mS}Iuf$mQo~ z#6oNQhSg3#XAF~`B-7)jKk<1?54~O4UwiD4mzVPX=gJneZ9B~*JKSe{5;IvnoTjBb z&Lx?v?}(gh;L%_3wd9@)WjCV_o^Z+??D;E)?wH+o^$THBPXify5cTT(ebA|TQOKm=vGt-p~K9| zY#4o|{~t_$p7Pfr-%a*L;>CA3;t)t1-uNO4StTGW4CLL;ihBujBr57V2H~pVSd;t- zFQQ5y-jj=hL5kTWDcRhmdI&(iCd?K&_5j)&b{&IVIe`6y?W23H9x)Z}T1A&k9w|PakY(3Wu-ihdZIiQ6X>TcRP95DZ^LX}Y{M`e= zwU5H4AEwAzHxwS63oCpP-uQZF%-GK@hP@%S9BVS?E-08fwW{20s~Cw-{W0 zeaolhw#Ssg*Mf}0Fo%flE9^=8J1`!!K@2@0zH9(k3(2D3pSG>QPdo<=2VB)d;%$zB z!Vd=@FT$Mj&y6ua0Fc9?z91DQ>2w0+#FcB;PP%X^r}4tJB%>HD&;1=iU_I=c!=P2Zash=Ldvdh!Y>PXWNQpOnVwXAD$C&dEZvsaITQ~Ykqd$Qj|FfZ`m$crF)}i$k~)PlBg)WJQejJn z3>7@31#}5)2EeP?%!&GzdZyIp{u+2^px@LxIY`%Z#&TMO{tN?`q0 zzD$HBoi8sxMb(5#U05&Z;iW(udnMz%YiQv7ChMfr-$HNQ_Hw^h8~!s-_4&a!XWNf; zKaPAc^y=0!Q{HS7%D;|9r^2K%0T5a@%=eXhQ4>)71nBU6t=E*CkK!f(0Ug@%r4||d zU}j+IK{m!{L?hoN!6%a8UxbXRh!kvD*C#aI7mSp!fiA~G>8Ujr78YhjY$UAq2Cy5l z`jS*+FBpU(Y@&%vLo#*wB0xK}m<`M*)a?$ zfXh@59rDIL-@kV+5`;YvvEo4(f#~LGJdVEv(hqIhOE)P9V9kK|$iF!cB!+N4(8a$4 z^$iqZL3n&`W~ifM5$XVqz>>m;4DYep z7wKW;%J?ley7iLHv3~J!aprq7f#wWf5>S*MOJ$ zL2v4D%Cr3bFbQ+|@e2`_Uz=rIts4aAx(eoO%C1#%v@n><$EU~jWyapiW%b>=F>U@S zAf$e$z@adypBXp1j|>?^{vwTtv>cGU)E|4ycQfL-S6i);%S9EpgCYR7gZjC1k*IF@ z|7(STKd^=ApiTo)Ai)~;T+(Iu3C5-*;gCFQ^zS|?h!~TAIyOB>g_YAm3WGH>gJ1_? zKoNvaay>ZLswYm|CP-akq5$xH0`-g(qAi~m1>`vnhyj6JC-5dvc>+95NM!XOCiz*K zzlRMTtCSiduZT>^wI|HkZ?wNHk3ima;4+I~N|{o)^ch+@0K={#Q=ToA7sz79#-0YC zjZlj4j66VFphY3YWq>?I@FRTx7yHh-@`mNak^Z-n{6O$l&GC~!%&fB+YCDb7l7BN5 z_VBONbDQ3Y0ABv=0Hx?);his6krRYTU;YtVz7+fhr@s2$mzRHVDt+(Ht5v>qH_u+n zMsv2S(zvo$NNm4v)i%p@pH!L!*fnTbJG5D+@c+v3)(Zg81L@*ml5`Z<&f(57-a)U7_ld@DetoTe zx3?5V_I|V*?p8f>qzs5U0vB=T1oiL!`ZAf@aA{OcgtW#})o^WCGqwr_?2a35iVSR? z8!{T7a9*1$Uz8i?o9mpHdF-RYa8S-+ zD$u4l1nR2ba90CRFcu2|gUh<#F!lU3RPw)Hx{F_{wNH+*IZk@TPd`+GC6i&;AAa=+FiRvdon@0k+AluS6uw)b8V-)%?ZUJa zu|kf_NsnR6RViJH%sE=Ed|p@Bg%WNo)8 z&jBD3%@%2SL~~&u-gHNGC0dtdq~D ziVJ`SLEyJnTsg*|E7Kgd0aLMISq7Q-z0FY2Prwz3@S4v_t;3iDUofgqmZh9S97V|N@WzCd2n3gH#wO?M zW~wTR3JB^5TM=;B%;2c!$5Wf2D6omg{=hyPbxXIv2L#Ur;i^iW(EB!XgchK zAlNat&vX@<@#_0bNfmBG1-w*Nm*dJ=sDDi7y|M99UCIb66A!ff+;Z_v$e?aC7rSg< z^*y1COLax#&GGvD?JCh54JDT!mU6c8YU0_{$NH)0?$z973N!=vkzx}p!=lS&Irl5! zzA_NF5t2D5&%X>8D~?S7m~ps(#O?e62jS$kU@M?E`X4wr4wDDOgCgs+@f9 z{a~z2Qc_a#hxqY7RLS9y9a*fa9l~|v(q)UH;{v^smTp@yxZJ=gu>QK~-+hw0`qw73 zLL7A?Y~bp86kk4n^OLY%NA5}nx52?{Ajnhxf<6a-NoS)L(I~L}jDEb*IFw5O^TaK7 z$y)_QN^*rg7Um{b0!}A3_ zco1jXQCP^_n+TKyx#joNf1L2VFs7wlmC_}hcHbc;397RujO_)n5H|KH`%U|q*Q^F)I?qsQxe)1=srzl;_ySJILxD2);Q+?4~XpijZ`@`^snUHDgH*fn|N@sXug~Ux0 z66|h#b-mVBtr8|BTH)OnZ2}|x01TD*yGcW-#dp z&X<_@vFm4L>7&x{ZMOJpClFzB!$z@)YYd=#`%3jRkhpOFvr?!{u;)G zPinaSY-lKC?l7&?)anf09yb1Y*swP%)%p9KLV8v+mSc7VCHQs*#z^7w zB6cxHUyaS&96$z@gF{3o0MYGi2lt4era)T9&;M}r041hm9xP5c_6Dg+Zm>cn z1bKO53?fV|x!Blrhd9@AN^3)@!*#6Yxc}7`DA{9VhYFqn*S02ca^MJ>iF8XRQtGdo zZe+Ep%Q0G>HEDGGz^Vp8FkcOCc&UhHCck;HU7RnLA)5Q;wpbx^&athixZQilA!>0y z^6ry~+k0Zj)`!mgwgr__FZl zj#j-?kW{nh=otmvtNNz`|H{*A`94wLdAVSu*+Qn5F&xrB0JZ5)bpG~>3QfBY*2uQE zr0i@kRJ3o@xVd_5X z|DnHdw06Byoe%3X8P_Je&+vk>V+WM)Cu2s_=n^2t& zns$QY%u1&+`C!-F~ThTMc!ph2z;t|9ok9f_G zMhNwdlU8-w7+7)JTBP+mfH}0@!Bh_Np$PMqrNqVtfaGZ$!f^K=Wt)pcRACq$R>Tc4 zi+tiCpQs?!vfxV<&2ch7mqHC`pQ)w?*BawP<^Q!<%6zm$i$X;Wdm4h(9%Xxi(S8MB zC*@3X~cdE4pC5FvUDl=JAcV!Sl3H=1?>~a;1iHi-*8UtMdI|{ zN=%U^HeY1@-=-sg0d040=V7Xc2qud&Si>SouWTpv#LYZL{`6MlI~I3S zPSD0I)rF4W%A#mgt8Wg{>4kwqJ zq%}yHwn}Q5lK#s})uqFc0fO%E=jq2x-zLL%xeDyzh-;Uw^1NHDfu z&H=W=OYox*J=#;HMFKxtNn z{kv0h@3cQ=TVVb0o}dWd4+|S;kg=Cwe+yCMJYz9Rg%CXcm*w7!V(H-l`6@CcmD~ zRkRXuOkS0fj6yxS6I2O@t>CM_w7y}Ro85EJwz0TV*#7l|VP~v3UAJ~2beF{2*YRB+ z-14+H&D;qsc-tJ2FS)Sx4p+v4x6*Ftsr2lvCE4Rzw9%2i*KlDyaH1dsMseFoLf#hlaW1AdFrZ)k^uv@ ze7+3R*)}2i&E5ajxE1~D23A(r%1hycR{%%gO62F7uGtgkm>eC%y78G%&N5Y>T!WZx z%KkPl+VU1=6=*C6K5Ll{fK$A_ZJoPM%VcR_pOGOWO^!EXrlYhq6P2$sbVIKKjOt9P zDw>L~p^sqy<5Rs-SZ5|BJ!_MN5=EzHeN$LZYNi)8M;TssY+TcptdV7?_x9!SJ^$hv z`s+*Y?;BrVF{Ku?xp4S8R+``;)XJFJQdE1uivw$VAhctDFfdglm4t&&8!KV6-8w@W z$I(p$BBUaX({t(^;L+SYL!+OJ?xK@`@1Iu(o(&-UmNyLV|J3Pj(cWN!Zbx`6T6ZWn z!XT*T3Sf!LY_crT8Tl(YtUZgLz$WDmWVTSKptxL-ziTs9WoBph0t?BD>I_a*m~^jz zjXO+ES97Ha3eN<51mtL}*OTkS5W&1?m@e3TaxKBhy5D9aKBxvZ6EglFLH&5(T)@3Z zg$GuNG>ieMNDHw|se0_4ZRz)%r77kZ z4q3Li{EFCKG3z2KGh z)8jPV9q%&!t%dSPI+|>Z+<+G9s=jqPX%x5%=>lpdsvx)GRh_=PTaj|U618xY&voxv z&`;imU!?o)hX(}0fSx}s1qH~t-}ciHL~o7;rppm}6&wYk4ombV%7cRy>n6w;352CZ zLSOzjs$IdM?@CMhu+F5}8RagTq2ueOI~t$8zLZ*YOC5DX{+6^3g}t+%r&Nv?P3dqANe9sQ>UJMMePOTPsr#N6b9*}3N4+qj)bqE5=4P*_bFpS4!@Dt(#IX@i;YK1#kn1bPR5zN)u$V1pPxR7KzWxagE@XNCOc1 z!lXF^5k=qt!|9+MlI>ce5>Fr~8)JY*cERou}=dYJsFaJ^bjA-L+ZD$|QD3Q{!@|ye;`<*2Y6; z|x@?&>_DQfZiGo z$@n>-)ilEpNGV#40e+ZYw>{eBR8+{g=1k&)C&j-O_5OTmN1R#t zjcLc@cWl1T1whiJA?QAV$4v5hJb)kBb3sg!?2k415WcwAubzH5G|DI zg9*R#<6%KTNSG-XjEzZ)Ny$Vhr`t(W)g{Ue&0b6)j$d{MIVNsQ1YK?N)*n<6*i zk(8We7>#rq6ni(u5f1!;;GYMy3YLUae7hLr^R}}ZJupL>>{CY$uvbPGu*N_8jJ8Wa z3p`0m)z#zr-oXi48X?%fI9hw*<9JVm*LfU_bk2_i z8OZ&@zjT=+&4MF-{tamzLvSmkDzi`4B;(;3}E&g0jdbxOlmnr1&>Lv%6YWV8=-rsgb_48GN>vF-=l<=;Bxo4ZD;tVq} zJ+osHG@qx>9f{_e)zEiS)xiIejRaU37Kaw~ps9{kUpX>0Je+ms zBT4h`0aDU~wwAf7hxJobPSY(Nk9RP}3B%`oO^W02d%kClZ;1DD(Z%H$NK*PRdmML%)*&V&@TcPQWSsUVTXyiXf@8d2?Uk%;5f@ zH%vN@_Xn;Dp+C1Ae&s+{`}8{zI^7g=gViZKTUd@o{L;Uq5k1~xCZQ$%xVHE8H96EG z{{lH9`PM`!0eXT%X~f+HFvJQAJ+d*&Z4*Cr8sp78AaHjtAb`{-VNhozS?+?(G4lB9 zJv@?k`6}X$(#w<&J|ie{R>$1w5W3_lm(6%8ocu!OHN)fvH0x{f#Eq0KrRvOnSoXC* zKhdw~tGJt=mR^lKirT%v1}#6%W$ilGYEA0ij^hmk7Cr6xq z0l$y8lBLY&u0mUG<@s1?$B#Lw6H>j5wd!X^7V43jTO=EJ7 zmx$}oWF=~Jo4q+~O)CjbiXjG(IL3CaULBaJg%o>%II*euqN2Ar{H-utjakj!6^MUv zDtvujPI$|f?rU?`doc{DJa zxRa}w;~4bv-ZL*OE0yFGhTr`u(ZmS*{Nu?axk?yAP3ETns0=i%D;Ef zssYQ%L?Iu_G|Oj_(H&_!MqE%aB9w^ggJQn;O|1dVcOQ5q6fsC)fjK;aVw@g<1hrz! zsdqq_%Oz=^BVSbLEl)AAJ2yL{qK%)jiNWd0ciLiRdt1$^k0Dd@07SWv7z>rrQ(Oyr zHOKSnAeN zKRjpVZcg0A`$Z7lbF}|(I)gwqm5?p2quYK-=+1(lj)8xN5lBvhFa#!zc{oDse@QCT z(Ks`gMaV7-GWUEt)%^jE!Xs&X<8{_?fcb{gJ#B%zw}HYj-@p&YFg_%54BS^BDnbe{ zAi7LI60!Ic3i(riBZnh}IDSnA$Vzr+=2#KbSJ zMfoZRwv1x^*9Ip_I>8@|!e|x0x9X|jfaOw=uvaG4_~b8jx{#m%CfP3PAYsYQMaXUy zOSht?_;@`neDghv_p{hJm=_ap6 zneJ2Uphp)sFFatNtTxbZ;BKVcEvnX}=3R0khcB337(BXZa&WsD^UK5mgFA=xqhnd# zMWW1&N5_eYb&RH_T`|nt`IPOrvFIk}nva1QoVs)G7KSIU7hXSgQ-<@f88GSSBklDy zccCNI&%Nq^8^mr5t2(lp=|;!Nrv=$Qtc9M8aK~|Wzm~?;M}~^X zSai*hZH1#c=Z9`fzN3;}b`&{0!t)~B9_*+;wRfvWL$M&~#+Vjq?3PMou_*lw zz|m&QlF6yQ*UehuCuAxt)1&aK||TH6)gF$l%N8)HgsPVg}7KBUnSa z^UmRA4pN{P;jgp_i#w;cyUcYeuYLy>E871Fm=JN!)Jp>5HQWWPBV|kGN~lgk5P^3* zpn&Yku6tnO6S9?_V9y%xKE#9)y^5y-JUoWT^P&$-?9#C<%Q0%SAq-(RKKQXuMtV(+ z`on|G$z+R}UO^NfXUqe1cnwU>AfPBAAcD-M3PHrvZqH`uoIkqY#594m6wKj+*3eO^ z$g0`_m`1u}BdCOuVMc;@t0SKCS$$sfhqcv@!eafy3Rdv_tE}V0Ev#JdHqb;SipM_d z*Nw%%PRnLbN0b1U;*ddVxYrmDI397YkK?dcw>N5e=mEbL3)CnjGnELxM;w=sGHk)! z29(HUQ%JTT0zmD2>>07wC)8lugv?h)GXmXx?;w``&WkOl9_!(ZKIml>gTp``6C-yw z@909FscM4=B*tUq3gE?&L$8?5)xR%oq#+T)Vxz+hV<9p^4QXq zb1%SG3{;>+SRh~w6dUDJAtFW$T%heliZ}pCi}W3^M~eHdpDyWChZ1~H-Oe0T1FGvb z(!=M|@@I<%Sz_>vAB`-&ZHmZlE@`-km^>8Z18R7csy29^8VkpAI}elBp*C_l@|fp6 zp;@b3GKxM*SFyFNov!Q>lllCOO3HpM)`Y^q!tq%_M3!=GfQa>_Rw`&JxZ;Y+c?^<- zl;6u@C5fq~7i&_OBkM1M$9p>lp12k2K;`!L_urI6HZZ7*2KJTya$U$7L6ItG$`7XZ zIdNc4p}~u5m-56}XJAR9h1c+tIIyN+ED_Q`^?pwmXb?fuWda-y0#eI>e+yRIdP=Uw zPK2lG92uDwil=)V+%dfx+R6q52lL5X*^r#0WP+AS4%6P#SZUn?V9udpMvjm@lPlLT zMn4TOPEj9e2QS=R$|P;xQ&&gLmdCTxG#&O1-{Kq;TdcHppAS%99^l3Yy>#stNePJ@ z;2MDLBXyD;K?en5%u0Dlr)XMINLyvG< z_I{k(2b#`Uwj^G>rnk9Ry!Ci|LxRd}}T8LGd3xF95YMb;Df3Oz%MqS}Fzk z7?6jrK>>rBSchYLFD6%wC^{&?${@q*q3xBf8OqCrS_#;!EED}cq6 z7c~9Li=}5eH*S(R(dXl(meL}na_TY?&fIUIiRc{#fY?!tEr$NA)V<>ak7b;0u95Wf zIPql3MEH*51VrZp6gh=L9Daf*jUn-g@so6zwHC>Qk`BB2%8k1(jdkQoWaLd@<V@-Go5$1|tVprz(Ybg4O=dvj@B za`N+D&fPor$G_)sVAn->Sfrc+PNjO;JUIGiNw8dL$rW zH3;SqHzVl0Lh+s`Ed~mX(u7UXfKY9&D9{r5pU?Bu!CZ>rc@&@ngKEOur&b$0uaFq} z9Q%RvlNE z*)ORwi-HeELTk#lSG>bn|7y=bz<9)y4R#}q=y?M|$<#&oL?91A`^cW37IWz-27E{?Np{Bc$!6#dO9>diV87Xtag8Q%;0!rErzX6Yx|FT~1dG!%|c zQQ!ZV6AB^UG~adp{RhU0s&#AKwbf33dUSL6;9p&X3b!ckqFz<0S^_S_3gu_cfnG@| zK5a!}=@+|`s{BgWeW6+BSJAt?jJIOQ->xm8wXAEN#*RhHEAB}-^c_9NmJb7g@Mm2y zGZlgqtZHEO04sXUV~W$Fus$p7@G*)2_ea`0aBK}5#0q-+6SEa6)o$iiTNBzUQ2P42 z#I#ma-2Ov1Guoy;cKnWDaOIi^pH7#7a;iM7#+MjL?@3J2P0yWp7y@u^mpv)VB_+OJ z@x1whovikwuVSnY&!b*_^^)s)3?OWtZJf3T&9xD+_ypd+e`^64>Jt)ZFWRpFEsbct z!Kh#EW;_R)X(Uwzwl4J~eJ~KHCAqas2Okmsn7V|vptlXrrL7dE52N11pf*N8=vH;W zZ`ssKa4vhof=PVwI~;WI0LWFi5K(6XoDCU8K1u8@bN?U<>-bqHA9?;=Ue=`SLx>9> zlcFTA|8pRxoQO{~#`cJJnew(5WXs`YM2}G=#%;k!+SRsh zcc`ih3cp@B+7~SpMiD6UGSpp7{cFRU7RQuubP)n<8V5KqLi2hoA z!%DE`z+yoI$c`LXw^CT&yU>qnpEK9E$tawCvL#)wjQWQ=d6@4E`yd=8wGdavVK7asV&B>4Jjs<^cj|F_3}@i|>Es z#g(|w*Y=J&Y;uhQn^=FZCF16(-nuxZ$Mns703w9ak>Z7%?D~2@fC8qk5Ha1I)91U- zkMxyw-q8Oa4|_!o$}}SjVQ*jtZ|02=CiDPogdA;krz?QM+rQ=VimHh>u$Mvk6OKb8d~cs_nUTQP%UfOGBu(i$$6W8OamkZ)&9Bg zN-B*?9Wf3Sh(<14JvX&-xdQuqNFPW|CRmlYmf^u34mQW&%gZr@tt)0MLHr(z11J)* z-~2s(;K=L09?`mmJrBN({TXyNtx3P$dVZiU*!xgamB-YpzB;$dykRSCLvB(B;K;&rG7mb~6=F-$@x?$%o0Ok{Q9 zq@ZnK{cUOqv(4vX^0^lITwCv=s^&ozEhSUCo_iUrGI}Vf9=8IgXf+Z`ibvVVvJai) zZV&RZ3prKzUHj5d$c^x^%3$wPa#Hy{ISa1UaVhf~h6;l-zC74bo=4)uO3n|lEK_in z(g_gPT2F`T=B@~n$!WOeh@eKG+nfMA!_dLoZ(S+y-~d%mw;hrv^16ED%FGrCvRs)B zS>eYm9AJzj<{SoNX=v}0=N$h`2{sDd+41n+Sj#H21r9r3xVwE^0ri9kftsLCfaeA+ zkB9yS+@aKKEmdEDWo>;mrMIf`S&Dzo!iR1_9j*0zng%*$3v^2S+to-~(`n;U2k{3p z`~tar=`h=VUDXio{Q5bNziKSLnvz0>(Bjpv0sGI=^~#gKCGlTHQ20}2@h`>vMlb&C z0`j6?*S|OT)e{2$cG0h{`2RMF{`P879+^sGuR=Q`ngtf#0-x!j%U{(OTVCxp9RRF_ zaJo8DI1jG=HT(&>_~PI3vDgn#HUIzSDpR%~cX@n1iY%#}1}%~IH?a3VbJE{c`?Q}c zd=pSrR?*s{thz^KueFM%wyK7HB;~%_z&YX1%`rrS-aPv*%4gweh Lv%^J)PDK4TX0m)n literal 373728 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 ~NzjlGkR0L_NQ{$+sexQQ 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

    f(bGT~#mo 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

    sTBIJUlm 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 From 3a89bc774e6df574558de4cd6b80c920d961b07d Mon Sep 17 00:00:00 2001 From: josecoll Date: Fri, 22 Feb 2019 10:14:59 +0000 Subject: [PATCH 019/159] CORDA-2656 Docs: building a CorDapp for both OS and Enterprise (#4797) * CORDA-2656 Explanation and steps for building a CorDapp for both OS and Enterprise. * Added details and links to Jar Signing and CorDapp dependencies management. --- docs/source/cordapp-build-systems.rst | 2 ++ docs/source/cordapp-overview.rst | 33 ++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/source/cordapp-build-systems.rst b/docs/source/cordapp-build-systems.rst index 5ddfe945d2..07da3debc7 100644 --- a/docs/source/cordapp-build-systems.rst +++ b/docs/source/cordapp-build-systems.rst @@ -38,6 +38,8 @@ the following folder and files from the `Kotlin CorDapp Template ` and annotated according to :ref:`CorDapp separation `. + In particular, it is critical to separate the consensus-critical parts of your application (contracts, states and their dependencies) from + the rest of the business logic (flows, APIs, etc). + The former - the **CorDapp kernel** - is the Jar that will be attached to transactions creating/consuming your states and is the Jar + that any node on the network verifying the transaction must execute. + +.. note:: It is also important to understand how to manage any dependencies a CorDapp may have on 3rd party libraries and other CorDapps. + Please read :ref:`Setting your dependencies ` to understand the options and recommendations with regards to correctly Jar'ing CorDapp dependencies. + +2. Compile this **CorDapp kernel** Jar once, and then depend on it from your workflows Jar (or Jars - see below). Importantly, if + you want your app to work on both Corda and Corda Enterprise, you must compile this Jar against Corda, not Corda Enterprise. + This is because, in future, we may add additional functionality to Corda Enterprise that is not in Corda and you may inadvertently create a + CorDapp kernel that does not work on Corda open source. Compiling against Corda open source as a matter of course prevents this risk, as well + as preventing the risk that you inadvertently create two different versions of the Jar, which will have different hashes and hence break compatibility + and interoperability. + +.. note:: As of Corda 4 it is recommended to use :ref:`CorDapp Jar signing ` to leverage the new signature constraints functionality. + +3. Your workflow Jar(s) should depend on the **CorDapp kernel** (contract, states and dependencies). Importantly, you can create different workflow + Jars for Corda and Corda Enterprise, because the workflows Jar is not consensus critical. For example, you may wish to add additional features + to your CorDapp for when it is run on Corda Enterprise (perhaps it uses advanced features of one of the supported enterprise databases or includes + advanced database migration scripts, or some other Enterprise-only feature). + +In summary, structure your app as kernel (contracts, states, dependencies) and workflow (the rest) and be sure to compile the kernel +against Corda open source. You can compile your workflow (Jars) against the distribution of Corda that they target. From 136f6a6be6f2e5cc4edd793ef13a4a423e056992 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Fri, 22 Feb 2019 14:03:54 +0000 Subject: [PATCH 020/159] CORDA-2667: Fix DJVM SourceClassLoader so that ASM will use it only to load classes from outside the sandbox. (#4804) --- djvm/build.gradle | 16 +++++++ .../net/corda/djvm/analysis/ClassResolver.kt | 46 ++++++++++++++----- .../net/corda/djvm/rewiring/ClassRewriter.kt | 5 +- .../corda/djvm/rewiring/SandboxClassWriter.kt | 11 +++-- .../corda/djvm/source/SourceClassLoader.kt | 24 ++++------ 5 files changed, 68 insertions(+), 34 deletions(-) diff --git a/djvm/build.gradle b/djvm/build.gradle index fd207b04a5..32dd8ae7a4 100644 --- a/djvm/build.gradle +++ b/djvm/build.gradle @@ -51,6 +51,22 @@ shadowJar { baseName 'corda-djvm' classifier '' relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm' + + // These particular classes are only needed to "bootstrap" + // the compilation of the other sandbox classes. At runtime, + // we will generate better versions from deterministic-rt.jar. + exclude 'sandbox/java/lang/Appendable.class' + exclude 'sandbox/java/lang/CharSequence.class' + exclude 'sandbox/java/lang/Character\$Subset.class' + exclude 'sandbox/java/lang/Character\$Unicode*.class' + exclude 'sandbox/java/lang/Comparable.class' + exclude 'sandbox/java/lang/Enum.class' + exclude 'sandbox/java/lang/Iterable.class' + exclude 'sandbox/java/lang/StackTraceElement.class' + exclude 'sandbox/java/lang/StringBuffer.class' + exclude 'sandbox/java/lang/StringBuilder.class' + exclude 'sandbox/java/nio/**' + exclude 'sandbox/java/util/**' } assemble.dependsOn shadowJar diff --git a/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt b/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt index a05b4ec7ec..0609506b66 100644 --- a/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt +++ b/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt @@ -84,23 +84,25 @@ class ClassResolver( * Reverse the resolution of a class name. */ fun reverse(resolvedClassName: String): String { - if (resolvedClassName in pinnedClasses || resolvedClassName in templateClasses) { - return resolvedClassName + return if (resolvedClassName in pinnedClasses || resolvedClassName in templateClasses) { + resolvedClassName + } else { + removeSandboxPrefix(resolvedClassName) } - if (resolvedClassName.startsWith(sandboxPrefix)) { - val nameWithoutPrefix = resolvedClassName.drop(sandboxPrefix.length) - if (resolve(nameWithoutPrefix) == resolvedClassName) { - return nameWithoutPrefix - } - } - return resolvedClassName } /** * Reverse the resolution of a class name from a fully qualified normalized name. */ - fun reverseNormalized(name: String): String { - return reverse(name.asResourcePath).asPackagePath + fun reverseNormalized(className: String): String { + return reverse(className.asResourcePath).asPackagePath + } + + /** + * Generates the equivalent class name outside the sandbox from a fully qualified normalized name. + */ + fun toSourceNormalized(className: String): String { + return toSource(className.asResourcePath).asPackagePath } /** @@ -114,6 +116,28 @@ class ClassResolver( } } + /** + * Maps a class name to its equivalent class outside the sandbox. + * Needed by [net.corda.djvm.source.AbstractSourceClassLoader]. + */ + private fun toSource(className: String): String { + return if (className in pinnedClasses) { + className + } else { + removeSandboxPrefix(className) + } + } + + private fun removeSandboxPrefix(className: String): String { + if (className.startsWith(sandboxPrefix)) { + val nameWithoutPrefix = className.drop(sandboxPrefix.length) + if (resolve(nameWithoutPrefix) == className) { + return nameWithoutPrefix + } + } + return className + } + /** * Check if class is whitelisted or pinned. */ diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt b/djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt index 4804074457..7c732616d8 100644 --- a/djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt +++ b/djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt @@ -7,6 +7,7 @@ import net.corda.djvm.code.ClassMutator import net.corda.djvm.code.EmitterModule import net.corda.djvm.code.emptyAsNull import net.corda.djvm.references.Member +import net.corda.djvm.source.AbstractSourceClassLoader import net.corda.djvm.utilities.loggerFor import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassVisitor @@ -17,11 +18,11 @@ import org.objectweb.asm.MethodVisitor * Functionality for rewriting parts of a class as it is being loaded. * * @property configuration The configuration of the sandbox. - * @property classLoader The class loader used to load the classes that are to be rewritten. + * @property classLoader The class loader used to load the source classes that are to be rewritten. */ open class ClassRewriter( private val configuration: SandboxConfiguration, - private val classLoader: ClassLoader + private val classLoader: AbstractSourceClassLoader ) { private val analysisConfig = configuration.analysisConfiguration diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt b/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt index fc0ad559f6..edc95d4a48 100644 --- a/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt +++ b/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt @@ -1,6 +1,7 @@ package net.corda.djvm.rewiring import net.corda.djvm.code.asPackagePath +import net.corda.djvm.source.AbstractSourceClassLoader import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter import org.objectweb.asm.ClassWriter.COMPUTE_FRAMES @@ -22,28 +23,28 @@ import org.objectweb.asm.Type */ open class SandboxClassWriter( classReader: ClassReader, - private val cloader: ClassLoader, + private val cloader: AbstractSourceClassLoader, flags: Int = COMPUTE_FRAMES or COMPUTE_MAXS ) : ClassWriter(classReader, flags) { - override fun getClassLoader(): ClassLoader = cloader + override fun getClassLoader(): AbstractSourceClassLoader = cloader /** * Get the common super type of [type1] and [type2]. */ override fun getCommonSuperClass(type1: String, type2: String): String { - // Need to override [getCommonSuperClass] to ensure that we use ClassLoader.loadClass(). + // Need to override [getCommonSuperClass] to ensure that we use SourceClassLoader.loadSourceClass(). when { type1 == OBJECT_NAME -> return type1 type2 == OBJECT_NAME -> return type2 } val class1 = try { - classLoader.loadClass(type1.asPackagePath) + classLoader.loadSourceClass(type1.asPackagePath) } catch (exception: Exception) { throw TypeNotPresentException(type1, exception) } val class2 = try { - classLoader.loadClass(type2.asPackagePath) + classLoader.loadSourceClass(type2.asPackagePath) } catch (exception: Exception) { throw TypeNotPresentException(type2, exception) } diff --git a/djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt b/djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt index a644d550ac..f4c1bb108c 100644 --- a/djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt +++ b/djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt @@ -47,29 +47,21 @@ abstract class AbstractSourceClassLoader( return try { logger.trace("Opening ClassReader for class {}...", originalName) - getResourceAsStream("$originalName.class")?.use { - ClassReader(it) - } ?: run(::throwClassLoadingError) + getResourceAsStream("$originalName.class")?.use(::ClassReader) ?: run(::throwClassLoadingError) } catch (exception: IOException) { throwClassLoadingError() } } - /** - * Find and load the class with the specified name from the search path. - */ - override fun findClass(name: String): Class<*> { - logger.trace("Finding class {}...", name) - val originalName = classResolver.reverseNormalized(name) - return super.findClass(originalName) - } - /** * Load the class with the specified binary name. */ - override fun loadClass(name: String, resolve: Boolean): Class<*> { - logger.trace("Loading class {}, resolve={}...", name, resolve) - val originalName = classResolver.reverseNormalized(name).let { n -> + @Throws(ClassNotFoundException::class) + fun loadSourceClass(name: String): Class<*> { + logger.trace("Loading source class {}...", name) + // We need the name of the equivalent class outside of the sandbox. + // This class is expected to belong to the application classloader. + val originalName = classResolver.toSourceNormalized(name).let { n -> // A synthetic exception should be mapped back to its // corresponding exception in the original hierarchy. if (isDJVMException(n)) { @@ -78,7 +70,7 @@ abstract class AbstractSourceClassLoader( n } } - return super.loadClass(originalName, resolve) + return loadClass(originalName) } protected companion object { From b52c7a09a3c0255b12372deb67aa7c4f63235966 Mon Sep 17 00:00:00 2001 From: Andrius Dagys Date: Fri, 22 Feb 2019 16:48:28 +0000 Subject: [PATCH 021/159] =?UTF-8?q?CORDA-2668=20-=20=20TestCordapp=20?= =?UTF-8?q?=E2=80=93=20use=20Gradle=20tooling=20API=20for=20builds=20(#479?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TestCordapp – disable daemon on internal gradle process The TestCordappImpl runs gradle to build cordapp jars required for tests. The started gradle process reuses a Gradle daemon that's potentially already used for running the tests causing the JVM to die with SIGBUS. --- build.gradle | 1 + testing/node-driver/build.gradle | 2 ++ .../testing/node/internal/TestCordappImpl.kt | 30 ++++++++++++------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index 1850c8ff6f..29dc5d0533 100644 --- a/build.gradle +++ b/build.gradle @@ -233,6 +233,7 @@ allprojects { jcenter() maven { url "$artifactory_contextUrl/corda-dependencies" } maven { url 'https://jitpack.io' } + maven { url 'https://repo.gradle.org/gradle/libs-releases' } } configurations { diff --git a/testing/node-driver/build.gradle b/testing/node-driver/build.gradle index 01de45b827..d85baabac1 100644 --- a/testing/node-driver/build.gradle +++ b/testing/node-driver/build.gradle @@ -37,6 +37,8 @@ dependencies { compile "org.eclipse.jetty:jetty-webapp:${jetty_version}" compile "javax.servlet:javax.servlet-api:3.1.0" + compile "org.gradle:gradle-tooling-api:4.10.1" + // Jersey for JAX-RS implementation for use in Jetty compile "org.glassfish.jersey.core:jersey-server:${jersey_version}" compile "org.glassfish.jersey.containers:jersey-container-servlet-core:${jersey_version}" diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt index 0190bb2bf4..2c4dd6cda5 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt @@ -4,7 +4,8 @@ import io.github.classgraph.ClassGraph import net.corda.core.internal.* import net.corda.core.utilities.contextLogger import net.corda.testing.node.TestCordapp -import org.apache.commons.lang.SystemUtils +import org.gradle.tooling.GradleConnector +import org.gradle.tooling.ProgressEvent import java.nio.file.Path import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -77,24 +78,31 @@ data class TestCordappImpl(val scanPackage: String, override val config: Map + log.info(event.description) + } + } + // Blocks until the build is complete + build.run() + projectConnection.close() } } } From 9d04eccc8a113a65fcebeb3563295a0bbc52e204 Mon Sep 17 00:00:00 2001 From: Andrius Dagys Date: Fri, 22 Feb 2019 18:26:40 +0000 Subject: [PATCH 022/159] =?UTF-8?q?CORDA-2668=20-=20TestCordapp=20?= =?UTF-8?q?=E2=80=93=20minor=20refactoring=20in=20jar=20building=20(#4805)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- testing/node-driver/build.gradle | 2 +- .../testing/node/internal/TestCordappImpl.kt | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/testing/node-driver/build.gradle b/testing/node-driver/build.gradle index d85baabac1..83853a85a7 100644 --- a/testing/node-driver/build.gradle +++ b/testing/node-driver/build.gradle @@ -37,7 +37,7 @@ dependencies { compile "org.eclipse.jetty:jetty-webapp:${jetty_version}" compile "javax.servlet:javax.servlet-api:3.1.0" - compile "org.gradle:gradle-tooling-api:4.10.1" + compile "org.gradle:gradle-tooling-api:${gradle.gradleVersion}" // Jersey for JAX-RS implementation for use in Jetty compile "org.glassfish.jersey.core:jersey-server:${jersey_version}" diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt index 2c4dd6cda5..8c924cd047 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt @@ -94,15 +94,16 @@ data class TestCordappImpl(val scanPackage: String, override val config: Map - log.info(event.description) + projectConnection.use { + val build = projectConnection.newBuild().apply { + forTasks("jar") + addProgressListener { event: ProgressEvent -> + log.info(event.description) + } } + // Blocks until the build is complete + build.run() } - // Blocks until the build is complete - build.run() - projectConnection.close() } } } From 8fb3d4dc014d83d79c59b03ab319dda60563ba1a Mon Sep 17 00:00:00 2001 From: Tommy Lillehagen Date: Sat, 23 Feb 2019 17:10:32 +0000 Subject: [PATCH 023/159] CORDA-2669 - Reintroduce pendingFlowsCount (#4806) * CORDA-2669 - pendingFlowsCount not in public API Reintroduce `pendingFlowsCount` to public API (as deprecated). Advise to use the `gracefulShutdown` command in the shell instead. * CORDA-2669 - Add pendingFlowsCount to api-current.txt --- .ci/api-current.txt | 2 ++ .../net/corda/core/messaging/CordaRPCOps.kt | 34 ++++++++++++++++++ .../net/corda/nodeapi/internal/RpcHelpers.kt | 36 ------------------- .../corda/node/internal/CordaRPCOpsImpl.kt | 1 - .../net/corda/tools/shell/InteractiveShell.kt | 6 +--- 5 files changed, 37 insertions(+), 42 deletions(-) diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 8505053a97..87186bf097 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -3147,6 +3147,8 @@ public interface net.corda.core.messaging.CordaRPCOps extends net.corda.core.mes public abstract net.corda.core.identity.Party wellKnownPartyFromX500Name(net.corda.core.identity.CordaX500Name) ## public final class net.corda.core.messaging.CordaRPCOpsKt extends java.lang.Object + @NotNull + public static final net.corda.core.messaging.DataFeed> pendingFlowsCount(net.corda.core.messaging.CordaRPCOps) ## @CordaSerializable public final class net.corda.core.messaging.DataFeed extends java.lang.Object diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index 1b924610d7..ecc03e5763 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -22,6 +22,8 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.Try import rx.Observable +import rx.schedulers.Schedulers +import rx.subjects.PublishSubject import java.io.IOException import java.io.InputStream import java.security.PublicKey @@ -420,6 +422,38 @@ interface CordaRPCOps : RPCOps { fun isWaitingForShutdown(): Boolean } +/** + * Returns a [DataFeed] of the number of pending flows. The [Observable] for the updates will complete the moment all pending flows will have terminated. + */ +@Deprecated("For automated upgrades, consider using the `gracefulShutdown` command in an SSH session instead.") +fun CordaRPCOps.pendingFlowsCount(): DataFeed> { + val updates = PublishSubject.create>() + val initialPendingFlowsCount = stateMachinesFeed().let { + var completedFlowsCount = 0 + var pendingFlowsCount = it.snapshot.size + it.updates.observeOn(Schedulers.io()).subscribe({ update -> + when (update) { + is StateMachineUpdate.Added -> { + pendingFlowsCount++ + updates.onNext(completedFlowsCount to pendingFlowsCount) + } + is StateMachineUpdate.Removed -> { + completedFlowsCount++ + updates.onNext(completedFlowsCount to pendingFlowsCount) + if (completedFlowsCount == pendingFlowsCount) { + updates.onCompleted() + } + } + } + }, updates::onError) + if (pendingFlowsCount == 0) { + updates.onCompleted() + } + pendingFlowsCount + } + return DataFeed(initialPendingFlowsCount, updates) +} + inline fun CordaRPCOps.vaultQueryBy(criteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(), paging: PageSpecification = PageSpecification(), sorting: Sort = Sort(emptySet())): Vault.Page { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/RpcHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/RpcHelpers.kt index e09112af4d..34567596da 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/RpcHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/RpcHelpers.kt @@ -1,45 +1,9 @@ package net.corda.nodeapi.internal import net.corda.core.messaging.CordaRPCOps -import net.corda.core.messaging.DataFeed -import net.corda.core.messaging.StateMachineUpdate import rx.Observable -import rx.schedulers.Schedulers -import rx.subjects.PublishSubject import java.util.concurrent.TimeUnit -/** - * Returns a [DataFeed] of the number of pending flows. The [Observable] for the updates will complete the moment all pending flows will have terminated. - */ -fun CordaRPCOps.pendingFlowsCount(): DataFeed> { - - val updates = PublishSubject.create>() - val initialPendingFlowsCount = stateMachinesFeed().let { - var completedFlowsCount = 0 - var pendingFlowsCount = it.snapshot.size - it.updates.observeOn(Schedulers.io()).subscribe({ update -> - when (update) { - is StateMachineUpdate.Added -> { - pendingFlowsCount++ - updates.onNext(completedFlowsCount to pendingFlowsCount) - } - is StateMachineUpdate.Removed -> { - completedFlowsCount++ - updates.onNext(completedFlowsCount to pendingFlowsCount) - if (completedFlowsCount == pendingFlowsCount) { - updates.onCompleted() - } - } - } - }, updates::onError) - if (pendingFlowsCount == 0) { - updates.onCompleted() - } - pendingFlowsCount - } - return DataFeed(initialPendingFlowsCount, updates) -} - /** * Returns an [Observable] that will complete when the node will have cancelled the draining shutdown hook. * diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index 80909e8fc4..ec15ef7b26 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -35,7 +35,6 @@ import net.corda.node.services.rpc.context import net.corda.node.services.statemachine.StateMachineManager import net.corda.nodeapi.exceptions.NonRpcFlowException import net.corda.nodeapi.exceptions.RejectedCommandException -import net.corda.nodeapi.internal.pendingFlowsCount import rx.Observable import rx.Subscription import java.io.InputStream diff --git a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt index 94868f2a68..bff6a27ece 100644 --- a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt +++ b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt @@ -19,11 +19,7 @@ import net.corda.core.flows.FlowLogic import net.corda.core.internal.* import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.openFuture -import net.corda.core.messaging.CordaRPCOps -import net.corda.core.messaging.DataFeed -import net.corda.core.messaging.FlowProgressHandle -import net.corda.core.messaging.StateMachineUpdate -import net.corda.nodeapi.internal.pendingFlowsCount +import net.corda.core.messaging.* import net.corda.tools.shell.utlities.ANSIProgressRenderer import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer import org.crsh.command.InvocationContext From 254d1b16318bc2699a3445a726d0b2ce874f109e Mon Sep 17 00:00:00 2001 From: Bernd Stoeger Date: Sun, 24 Feb 2019 11:15:08 +0100 Subject: [PATCH 024/159] Include ch package in quasar instrumentation --- node/capsule/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle index b68678c9ee..1c4601043b 100644 --- a/node/capsule/build.gradle +++ b/node/capsule/build.gradle @@ -50,7 +50,7 @@ task buildCordaJAR(type: FatCapsule, dependsOn: project(':node').tasks.jar) { applicationVersion = corda_release_version applicationId = "net.corda.node.Corda" // See experimental/quasar-hook/README.md for how to generate. - def quasarExcludeExpression = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**)" + def quasarExcludeExpression = "x(antlr**;bftsmart**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**)" javaAgents = ["quasar-core-${quasar_version}-jdk8.jar=${quasarExcludeExpression}"] systemProperties['visualvm.display.name'] = 'Corda' minJavaVersion = '1.8.0' From 46909feef93f824c5328a6881314f906944028f6 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 25 Feb 2019 13:15:32 +0000 Subject: [PATCH 025/159] Remove some unused classes from the DJVM. (#4809) Simplify DJVM Gradle files. --- djvm/build.gradle | 12 ++--- djvm/cli/build.gradle | 32 +++---------- .../net/corda/djvm/analysis/PrefixTree.kt | 38 --------------- .../net/corda/djvm/execution/IsolatedTask.kt | 7 +-- .../net/corda/djvm/validation/Reason.kt | 46 ------------------- .../djvm/annotations/NonDeterministic.kt | 0 6 files changed, 13 insertions(+), 122 deletions(-) delete mode 100644 djvm/src/main/kotlin/net/corda/djvm/analysis/PrefixTree.kt delete mode 100644 djvm/src/main/kotlin/net/corda/djvm/validation/Reason.kt rename djvm/src/{main => test}/kotlin/net/corda/djvm/annotations/NonDeterministic.kt (100%) diff --git a/djvm/build.gradle b/djvm/build.gradle index 32dd8ae7a4..3a50ad7e6b 100644 --- a/djvm/build.gradle +++ b/djvm/build.gradle @@ -19,7 +19,7 @@ repositories { } configurations { - testCompile.extendsFrom shadow + testImplementation.extendsFrom shadow jdkRt.resolutionStrategy { // Always check the repository for a newer SNAPSHOT. cacheChangingModulesFor 0, 'seconds' @@ -32,16 +32,16 @@ dependencies { shadow "org.slf4j:slf4j-api:$slf4j_version" // ASM: byte code manipulation library - compile "org.ow2.asm:asm:$asm_version" - compile "org.ow2.asm:asm-commons:$asm_version" + implementation "org.ow2.asm:asm:$asm_version" + implementation "org.ow2.asm:asm-commons:$asm_version" // ClassGraph: classpath scanning shadow "io.github.classgraph:classgraph:$class_graph_version" // Test utilities - testCompile "junit:junit:$junit_version" - testCompile "org.assertj:assertj-core:$assertj_version" - testCompile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" + testImplementation "junit:junit:$junit_version" + testImplementation "org.assertj:assertj-core:$assertj_version" + testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" jdkRt "net.corda:deterministic-rt:latest.integration" } diff --git a/djvm/cli/build.gradle b/djvm/cli/build.gradle index d72a4a74c0..e6399e3e69 100644 --- a/djvm/cli/build.gradle +++ b/djvm/cli/build.gradle @@ -2,27 +2,14 @@ plugins { id 'com.github.johnrengelman.shadow' } -repositories { - maven { - url "$artifactory_contextUrl/corda-dev" - } -} - -configurations { - deterministicRt -} - dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" - compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + implementation "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" + implementation "com.jcabi:jcabi-manifests:$jcabi_manifests_version" - compile "info.picocli:picocli:$picocli_version" - compile project(path: ":djvm", configuration: "shadow") - - // Deterministic runtime - used in whitelist generation - deterministicRt project(path: ':jdk8u-deterministic', configuration: 'jdk') + implementation "info.picocli:picocli:$picocli_version" + implementation project(path: ':djvm', configuration: 'shadow') } jar.enabled = false @@ -39,10 +26,3 @@ shadowJar { } } assemble.dependsOn shadowJar - -task generateWhitelist(type: JavaExec, dependsOn: shadowJar) { - // This is an example of how a whitelist can be generated from a JAR. In most applications though, it is recommended - // that the minimal set whitelist is used. - main = '-jar' - args = [shadowJar.outputs.files.singleFile, 'whitelist', 'generate', '-o', "$buildDir/jdk8-deterministic.dat.gz", configurations.deterministicRt.files[0] ] -} diff --git a/djvm/src/main/kotlin/net/corda/djvm/analysis/PrefixTree.kt b/djvm/src/main/kotlin/net/corda/djvm/analysis/PrefixTree.kt deleted file mode 100644 index 26679cc133..0000000000 --- a/djvm/src/main/kotlin/net/corda/djvm/analysis/PrefixTree.kt +++ /dev/null @@ -1,38 +0,0 @@ -package net.corda.djvm.analysis - -/** - * Trie data structure to make prefix matching more efficient. - */ -class PrefixTree { - - private class Node(val children: MutableMap = mutableMapOf()) - - private val root = Node() - - /** - * Add a new prefix to the set. - */ - fun add(prefix: String) { - var node = root - for (char in prefix) { - val nextNode = node.children.computeIfAbsent(char) { Node() } - node = nextNode - } - } - - /** - * Check if any of the registered prefixes matches the provided string. - */ - fun contains(string: String): Boolean { - var node = root - for (char in string) { - val nextNode = node.children[char] ?: return false - if (nextNode.children.isEmpty()) { - return true - } - node = nextNode - } - return false - } - -} diff --git a/djvm/src/main/kotlin/net/corda/djvm/execution/IsolatedTask.kt b/djvm/src/main/kotlin/net/corda/djvm/execution/IsolatedTask.kt index 7d2ae05153..00d6656c1f 100644 --- a/djvm/src/main/kotlin/net/corda/djvm/execution/IsolatedTask.kt +++ b/djvm/src/main/kotlin/net/corda/djvm/execution/IsolatedTask.kt @@ -38,12 +38,7 @@ class IsolatedTask( exception = (ex as? LinkageError)?.cause ?: ex null } - costs = CostSummary( - runtimeCosts.allocationCost.value, - runtimeCosts.invocationCost.value, - runtimeCosts.jumpCost.value, - runtimeCosts.throwCost.value - ) + costs = CostSummary(runtimeCosts) } logger.trace("Exiting isolated runtime environment...") completionLatch.countDown() diff --git a/djvm/src/main/kotlin/net/corda/djvm/validation/Reason.kt b/djvm/src/main/kotlin/net/corda/djvm/validation/Reason.kt deleted file mode 100644 index 506d333f81..0000000000 --- a/djvm/src/main/kotlin/net/corda/djvm/validation/Reason.kt +++ /dev/null @@ -1,46 +0,0 @@ -package net.corda.djvm.validation - -/** - * Representation of the reason for why a reference has been marked as invalid. - * - * @property code The code used to label the error. - * @property classes A set of invalid class references, where applicable. - */ -data class Reason( - val code: Code, - val classes: List = emptyList() -) { - - /** - * The derived description of the error. - */ - val description = classes.joinToString(", ").let { - when { - classes.size == 1 -> "${code.singularDescription}; $it" - classes.size > 1 -> "${code.pluralDescription}; $it" - else -> code.singularDescription - } - } - - /** - * Error codes used to label invalid references. - * - * @property singularDescription The description to use when [classes] is empty or has one element. - * @property pluralDescription The description to use when [classes] has more than one element. - */ - @Suppress("KDocMissingDocumentation") - enum class Code( - val singularDescription: String, - val pluralDescription: String = singularDescription - ) { - INVALID_CLASS( - singularDescription = "entity signature contains an invalid reference", - pluralDescription = "entity signature contains invalid references" - ), - NOT_WHITELISTED("entity is not whitelisted"), - ANNOTATED("entity is annotated with @NonDeterministic"), - NON_EXISTENT_CLASS("class does not exist"), - NON_EXISTENT_MEMBER("member does not exist") - } - -} \ No newline at end of file diff --git a/djvm/src/main/kotlin/net/corda/djvm/annotations/NonDeterministic.kt b/djvm/src/test/kotlin/net/corda/djvm/annotations/NonDeterministic.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/annotations/NonDeterministic.kt rename to djvm/src/test/kotlin/net/corda/djvm/annotations/NonDeterministic.kt From 7b93cc84773c1e72387be34fdc0bbc372ad9db95 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Mon, 25 Feb 2019 16:48:07 +0100 Subject: [PATCH 026/159] Docs: sync static versions list in master with release/4 branch --- docs/source/_static/versions | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/_static/versions b/docs/source/_static/versions index abeff3b667..775f9cb83f 100644 --- a/docs/source/_static/versions +++ b/docs/source/_static/versions @@ -11,5 +11,9 @@ "https://docs.corda.net/releases/release-V1.0": "V1.0", "https://docs.corda.net/releases/release-V2.0": "V2.0", "https://docs.corda.net/releases/release-V3.0": "V3.0", + "https://docs.corda.net/releases/release-V3.1": "V3.1", + "https://docs.corda.net/releases/release-V3.2": "V3.2", + "https://docs.corda.net/releases/release-V3.3": "V3.3", + "https://docs.corda.net/releases/release-V4.0": "V4.0", "https://docs.corda.net/head/": "Master" } From 111acc5d6e20d040ecfe3a41041e4f3411c38c7a Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 25 Feb 2019 18:11:06 +0000 Subject: [PATCH 027/159] CORDA-2671: Remove broken ExplorerSimulator from Node Explorer. (#4811) * CORDA-2671: Remove broken ExplorerSimulator from Node Explorer. * CORDA-2671: Document that Node Explorer is included with DemoBench. --- docs/source/node-explorer.rst | 69 ++---- tools/explorer/README.md | 35 +-- tools/explorer/build.gradle | 18 +- .../net/corda/explorer/ExplorerSimulation.kt | 212 ------------------ .../main/kotlin/net/corda/explorer/Main.kt | 19 -- .../views/cordapps/cash/NewTransaction.kt | 3 +- 6 files changed, 29 insertions(+), 327 deletions(-) delete mode 100644 tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt diff --git a/docs/source/node-explorer.rst b/docs/source/node-explorer.rst index 1a1dc71b29..15a377fae9 100644 --- a/docs/source/node-explorer.rst +++ b/docs/source/node-explorer.rst @@ -26,68 +26,29 @@ Running the UI Running demo nodes ------------------ -A demonstration Corda network topology is configured with 5 nodes playing the following roles: +Node Explorer is included with the :doc:`demobench` application, which allows +you to create local Corda networks on your desktop. For example: -1. Notary -2. Issuer nodes, representing two fictional central banks (UK Bank Plc issuer of GBP and USA Bank Corp issuer of USD) -3. Participant nodes, representing two users (Alice and Bob) + * Notary + * Bank of Breakfast Tea (*Issuer node* for GBP) + * Bank of Big Apples (*Issuer node* for USD) + * Alice (Participant node, for user Alice) + * Bob (Participant node, for user Bob) + +DemoBench will deploy all nodes with Corda's Finance CorDapp automatically, and +allow you to launch an instance of Node Explorer for each. You will be logged +into the Node Explorer automatically. When connected to an *Issuer* node, a user can execute cash transaction commands to issue and move cash to itself or other parties on the network or to exit cash (for itself only). When connected to a *Participant* node a user can only execute cash transaction commands to move cash to other parties on the network. -The Demo Nodes can be started in one of two modes: +The Node Explorer is also available as a stand-alone JavaFX application. It is +available from the Corda repositories as ``corda-tools-explorer``, and can be +run as -1. Normal - - Fresh clean environment empty of transactions. - Firstly, launch an Explorer instance to login to one of the Issuer nodes and issue some cash to the other participants (Bob and Alice). - Then launch another Explorer instance to login to a participant node and start making payments (eg. move cash). - You will only be able to exit (eg. redeem from the ledger) cash as an issuer node. - -**Windows**:: - - gradlew.bat tools:explorer:runDemoNodes - -**Other**:: - - ./gradlew tools:explorer:runDemoNodes - -2. Simulation - - In this mode Nodes will automatically commence executing commands as part of a random generation process. - The simulation start with pre-allocating chunks of cash to each of the party in 2 currencies (USD, GBP), then it enter a loop to generate random events. - In each iteration, the issuers will execute a Cash Issue or Cash Exit command (at a 9:1 ratio) and a random party will execute a move of cash to another random party. - -**Windows**:: - - gradlew.bat tools:explorer:runSimulationNodes - -**Other**:: - - ./gradlew tools:explorer:runSimulationNodes - - -.. note:: 5 Corda nodes will be created on the following port on localhost by default. - - * Notary -> 20005 (Does not accept logins) - * UK Bank Plc -> 20011 (*Issuer node*) - * USA Bank Corp -> 20008 (*Issuer node*) - * Alice -> 20017 - * Bob -> 20014 - -Explorer login credentials to the Issuer nodes are defaulted to ``manager`` and ``test``. -Explorer login credentials to the Participants nodes are defaulted to ``user1`` and ``test``. -Please note you are not allowed to login to the notary. - -.. note:: When you start the nodes in Windows using the command prompt, they might not be killed when you close the - window or terminate the task. If that happens you need to manually terminate the Java processes running the nodes. - -.. note:: Alternatively, you may start the demo nodes from within IntelliJ using either of the run configurations - ``Explorer - demo nodes`` or ``Explorer - demo nodes (simulation)`` - -.. note:: It is also possible to start the Explorer GUI from IntelliJ via ``Explorer - GUI`` run configuration, provided that the optional TornadoFX plugin has been installed first. + java -jar corda-tools-explorer.jar .. note:: Use the Explorer in conjunction with the Trader Demo and Bank of Corda samples to use other *Issuer* nodes. diff --git a/tools/explorer/README.md b/tools/explorer/README.md index d6c6cbfcd7..a8974402fe 100644 --- a/tools/explorer/README.md +++ b/tools/explorer/README.md @@ -13,36 +13,19 @@ The user can execute cash transaction commands to issue and move cash to other p ./gradlew tools:explorer:run - ## Running Demo Nodes -A demonstration Corda network topology is configured with 5 nodes playing the following roles: -1. Notary -2. Issuer nodes (representing two fictional central banks - UK Bank Plc issuer of GBP and USA Bank Corp issuer of USD) -3. Participant nodes (representing two users - Alice and Bob) +Node Explorer is included with the [DemoBench](https://docs.corda.net/demobench.html) application, +which allows you to create local Corda networks on your desktop. For example: -The Issuer nodes have the ability to issue, move and exit cash amounts. -The Participant nodes are only able to spend cash (eg. move cash). + * Notary + * Bank of Breakfast Tea (*Issuer node* for GBP) + * Bank of Big Apples (*Issuer node* for USD) + * Alice + * Bob -**Windows:** - - gradlew.bat tools:explorer:runDemoNodes - -**Other:** - - ./gradlew tools:explorer:runDemoNodes - -**These Corda nodes will be created on the following port on localhost.** - - * Notary -> 20005 (Does not accept logins) - * UK Bank Plc -> 20011 (*Issuer node*) - * USA Bank Corp -> 20008 (*Issuer node*) - * Alice -> 20017 - * Bob -> 20014 - -Explorer login credentials to the Issuer nodes are defaulted to ``manager`` and ``test``. -Explorer login credentials to the Participants nodes are defaulted to ``user1`` and ``test``. -Please note you are not allowed to login to the notary. +DemoBench will deploy all nodes with Corda's Finance CorDapp automatically, and allow you to launch an +instance of Node Explorer for each. ## TODOs: - Shows more useful information in the dashboard. diff --git a/tools/explorer/build.gradle b/tools/explorer/build.gradle index 612fea229c..0f2951c132 100644 --- a/tools/explorer/build.gradle +++ b/tools/explorer/build.gradle @@ -22,12 +22,13 @@ dependencies { // Corda Core: Data structures and basic types needed to work with Corda. compile project(':core') compile project(':client:jfx') - compile project(':client:mock') - compile project(':node-driver') compile project(':finance:contracts') compile project(':finance:workflows') compile project(':tools:worldmap') + // Log4J: logging framework (with SLF4J bindings) + compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" + // Capsule is a library for building independently executable fat JARs. // We only need this dependency to compile our Caplet against. compileOnly "co.paralleluniverse:capsule:$capsule_version" @@ -60,21 +61,10 @@ tasks.withType(JavaCompile) { options.compilerArgs << '-proc:none' } -task runDemoNodes(dependsOn: 'classes', type: JavaExec) { - main = 'net.corda.explorer.MainKt' - classpath = sourceSets.main.runtimeClasspath -} - -task runSimulationNodes(dependsOn: 'classes', type: JavaExec) { - main = 'net.corda.explorer.MainKt' - classpath = sourceSets.main.runtimeClasspath - args '-S' -} - jar { manifest { attributes( - 'Automatic-Module-Name': 'net.corda.tools.explorer' + 'Automatic-Module-Name': 'net.corda.tools.explorer' ) } } \ No newline at end of file diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt deleted file mode 100644 index 76c4ab0523..0000000000 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ /dev/null @@ -1,212 +0,0 @@ -package net.corda.explorer - -import joptsimple.OptionSet -import net.corda.client.mock.ErrorFlowsEventGenerator -import net.corda.client.mock.EventGenerator -import net.corda.client.mock.Generator -import net.corda.client.rpc.CordaRPCClient -import net.corda.client.rpc.CordaRPCConnection -import net.corda.core.contracts.Amount -import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.Party -import net.corda.core.internal.concurrent.thenMatch -import net.corda.core.messaging.CordaRPCOps -import net.corda.core.messaging.FlowHandle -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.getOrThrow -import net.corda.finance.GBP -import net.corda.finance.USD -import net.corda.finance.contracts.asset.Cash -import net.corda.finance.flows.AbstractCashFlow -import net.corda.finance.flows.CashExitFlow -import net.corda.finance.flows.CashExitFlow.ExitRequest -import net.corda.finance.flows.CashIssueAndPaymentFlow -import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest -import net.corda.finance.flows.CashPaymentFlow -import net.corda.finance.internal.CashConfigDataFlow -import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.BOB_NAME -import net.corda.testing.driver.* -import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.node.User -import net.corda.testing.node.internal.FINANCE_CORDAPPS -import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP -import java.time.Instant -import java.util.* - -class ExplorerSimulation(private val options: OptionSet) { - private val user = User("user1", "test", permissions = setOf( - startFlow(), - startFlow() - )) - private val manager = User("manager", "test", permissions = setOf( - startFlow(), - startFlow(), - startFlow(), - startFlow()) - ) - - private lateinit var notaryNode: NodeHandle - private lateinit var aliceNode: NodeHandle - private lateinit var bobNode: NodeHandle - private lateinit var issuerNodeGBP: NodeHandle - private lateinit var issuerNodeUSD: NodeHandle - private lateinit var notary: Party - - private val RPCConnections = ArrayList() - private val issuers = HashMap() - private val parties = ArrayList>() - - init { - startDemoNodes() - } - - private fun onEnd() { - println("Closing RPC connections") - RPCConnections.forEach { it.close() } - } - - private fun startDemoNodes() { - val portAllocation = incrementalPortAllocation(20000) - driver(DriverParameters( - portAllocation = portAllocation, - cordappsForAllNodes = FINANCE_CORDAPPS, - waitForAllNodesToFinish = true, - jmxPolicy = JmxPolicy.defaultEnabled() - )) { - // TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo. - val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)) - val bob = startNode(providedName = BOB_NAME, rpcUsers = listOf(user)) - val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB") - val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US") - val issuerGBP = startNode(NodeParameters( - providedName = ukBankName, - rpcUsers = listOf(manager), - additionalCordapps = listOf(FINANCE_WORKFLOWS_CORDAPP.copy(config = mapOf("issuableCurrencies" to listOf("GBP")))) - )) - val issuerUSD = startNode(NodeParameters( - providedName = usaBankName, - rpcUsers = listOf(manager), - additionalCordapps = listOf(FINANCE_WORKFLOWS_CORDAPP.copy(config = mapOf("issuableCurrencies" to listOf("USD")))) - )) - - notaryNode = defaultNotaryNode.get() - aliceNode = alice.get() - bobNode = bob.get() - issuerNodeGBP = issuerGBP.get() - issuerNodeUSD = issuerUSD.get() - - arrayOf(notaryNode, aliceNode, bobNode, issuerNodeGBP, issuerNodeUSD).forEach { - println("${it.nodeInfo.legalIdentities.first()} started on ${it.rpcAddress}") - } - - when { - options.has("S") -> startNormalSimulation() - options.has("F") -> startErrorFlowsSimulation() - } - } - } - - private fun setUpRPC() { - // Register with alice to use alice's RPC proxy to create random events. - val aliceClient = CordaRPCClient(aliceNode.rpcAddress) - val aliceConnection = aliceClient.start(user.username, user.password) - val aliceRPC = aliceConnection.proxy - - val bobClient = CordaRPCClient(bobNode.rpcAddress) - val bobConnection = bobClient.start(user.username, user.password) - val bobRPC = bobConnection.proxy - - val issuerClientGBP = CordaRPCClient(issuerNodeGBP.rpcAddress) - val issuerGBPConnection = issuerClientGBP.start(manager.username, manager.password) - val issuerRPCGBP = issuerGBPConnection.proxy - - val issuerClientUSD = CordaRPCClient(issuerNodeUSD.rpcAddress) - val issuerUSDConnection = issuerClientUSD.start(manager.username, manager.password) - val issuerRPCUSD = issuerUSDConnection.proxy - - RPCConnections.addAll(listOf(aliceConnection, bobConnection, issuerGBPConnection, issuerUSDConnection)) - issuers.putAll(mapOf(USD to issuerRPCUSD, GBP to issuerRPCGBP)) - - parties.addAll(listOf(aliceNode.nodeInfo.legalIdentities.first() to aliceRPC, - bobNode.nodeInfo.legalIdentities.first() to bobRPC, - issuerNodeGBP.nodeInfo.legalIdentities.first() to issuerRPCGBP, - issuerNodeUSD.nodeInfo.legalIdentities.first() to issuerRPCUSD)) - } - - private fun startSimulation(eventGenerator: EventGenerator, maxIterations: Int) { - // Log to logger when flow finish. - fun FlowHandle.log(seq: Int, name: String) { - val out = "[$seq] $name $id :" - returnValue.thenMatch({ (stx) -> - Main.log.info("$out ${stx.id} ${(stx.tx.outputs.first().data as Cash.State).amount}") // XXX: Why Main's log? - }, { - Main.log.info("$out ${it.message}") - }) - } - - for (i in 0..maxIterations) { - Thread.sleep(1000) - // Issuer requests. - eventGenerator.issuerGenerator.map { request -> - when (request) { - is IssueAndPaymentRequest -> issuers[request.amount.token]?.let { - println("${Instant.now()} [$i] ISSUING ${request.amount} with ref ${request.issueRef} to ${request.recipient}") - it.startFlow(::CashIssueAndPaymentFlow, request).log(i, "${request.amount.token}Issuer") - } - is ExitRequest -> issuers[request.amount.token]?.let { - println("${Instant.now()} [$i] EXITING ${request.amount} with ref ${request.issuerRef}") - it.startFlow(::CashExitFlow, request).log(i, "${request.amount.token}Exit") - } - else -> throw IllegalArgumentException("Unsupported command: $request") - } - }.generate(SplittableRandom()) - // Party pay requests. - eventGenerator.moveCashGenerator.combine(Generator.pickOne(parties)) { request, (party, rpc) -> - println("${Instant.now()} [$i] SENDING ${request.amount} from $party to ${request.recipient}") - rpc.startFlow(::CashPaymentFlow, request).log(i, party.name.toString()) - }.generate(SplittableRandom()) - } - println("Simulation completed") - } - - private fun startNormalSimulation() { - println("Running simulation mode ...") - setUpRPC() - notary = aliceNode.rpc.notaryIdentities().first() - val eventGenerator = EventGenerator( - parties = parties.map { it.first }, - notary = notary, - currencies = listOf(GBP, USD) - ) - val maxIterations = 100_000 - val anonymous = true - // Pre allocate some money to each party. - eventGenerator.parties.forEach { - for (ref in 0..1) { - for ((currency, issuer) in issuers) { - val amount = Amount(1_000_000, currency) - issuer.startFlow(::CashIssueAndPaymentFlow, amount, OpaqueBytes.of( ref.toByte() ), - it, anonymous, notary).returnValue.getOrThrow() - } - } - } - startSimulation(eventGenerator, maxIterations) - onEnd() - } - - private fun startErrorFlowsSimulation() { - println("Running flows with errors simulation mode ...") - setUpRPC() - val eventGenerator = ErrorFlowsEventGenerator( - parties = parties.map { it.first }, - notary = notary, - currencies = listOf(GBP, USD) - ) - val maxIterations = 10_000 - startSimulation(eventGenerator, maxIterations) - onEnd() - } -} diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/Main.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/Main.kt index 9fbc3199fc..00494b8827 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/Main.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/Main.kt @@ -8,11 +8,9 @@ import javafx.scene.control.ButtonType import javafx.scene.image.Image import javafx.stage.Stage import jfxtras.resources.JFXtrasFontRoboto -import joptsimple.OptionParser import net.corda.client.jfx.model.Models import net.corda.client.jfx.model.NodeMonitorModel import net.corda.client.jfx.model.observableValue -import net.corda.core.utilities.contextLogger import net.corda.explorer.model.CordaViewModel import net.corda.explorer.model.SettingsModel import net.corda.explorer.views.* @@ -31,10 +29,6 @@ class Main : App(MainView::class) { private val loginView by inject() private val fullscreen by observableValue(SettingsModel::fullscreenProperty) - companion object { - internal val log = contextLogger() - } - override fun start(stage: Stage) { var nodeModel: NodeMonitorModel? = null @@ -122,16 +116,3 @@ class Main : App(MainView::class) { FontAwesomeIconFactory.get() // Force initialisation. } } - -/** - * This main method will start 5 nodes (Notary, USA Bank, UK Bank, Bob and Alice) locally for UI testing, - * which will bind to ports 20005, 20008, 20011, 20014 and 20017 locally. - * - * The simulation starts by pre-allocating chunks of cash to each of the parties in 2 currencies (USD, GBP), then it enters a loop which generates random events. - * On each iteration, the issuers will execute a Cash Issue or Cash Exit flow (at a 9:1 ratio) and a random party will execute a move of cash to another random party. - */ -fun main(args: Array) { - val parser = OptionParser("SF") - val options = parser.parse(*args) - ExplorerSimulation(options) -} diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt index f980b0fceb..62a261af27 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt @@ -39,7 +39,6 @@ import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow.PaymentRequest -import net.corda.testing.core.singleIdentityAndCert import org.controlsfx.dialog.ExceptionDialog import tornadofx.* import java.math.BigDecimal @@ -183,7 +182,7 @@ class NewTransaction : Fragment() { partyBLabel.textProperty().bind(transactionTypeCB.valueProperty().map { it?.partyNameB?.let { "$it : " } }) partyBChoiceBox.apply { visibleProperty().bind(transactionTypeCB.valueProperty().map { it?.partyNameB }.isNotNull()) - items = FXCollections.observableList(parties.map { it.singleIdentityAndCert() }).sorted() + items = FXCollections.observableList(parties.map { it.legalIdentitiesAndCerts.single() }).sorted() converter = stringConverter { it?.let { PartyNameFormatter.short.format(it.name) } ?: "" } } // Issuer From 04ba77732b63095c1c7ea9f09e424f6b6a020478 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 25 Feb 2019 18:11:21 +0000 Subject: [PATCH 028/159] ENT-3128: Tidy up DemoBench's "built-in CorDapp" management. (#4814) --- .../demobench/plugin/CordappController.kt | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/plugin/CordappController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/plugin/CordappController.kt index 5748eb9e27..69b4fbdfc0 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/plugin/CordappController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/plugin/CordappController.kt @@ -8,10 +8,10 @@ import net.corda.demobench.model.NodeConfigWrapper import tornadofx.* import java.io.IOException import java.nio.file.Path -import java.nio.file.StandardCopyOption +import java.nio.file.StandardCopyOption.* import kotlin.streams.toList -// TODO This class needs to be revisted. It seems to operate on outdated concepts. +// TODO This class needs to be revisited. It seems to operate on outdated concepts. class CordappController : Controller() { companion object { const val FINANCE_CONTRACTS_CORDAPP_FILENAME = "corda-finance-contracts" @@ -20,10 +20,8 @@ class CordappController : Controller() { private val jvm by inject() private val cordappDir: Path = jvm.applicationDir / NodeConfig.CORDAPP_DIR_NAME - private val cordappJars = setOf( - cordappDir / "$FINANCE_CONTRACTS_CORDAPP_FILENAME.jar", - cordappDir / "$FINANCE_WORKFLOWS_CORDAPP_FILENAME.jar" - ) + private val cordappJars = setOf(FINANCE_CONTRACTS_CORDAPP_FILENAME, FINANCE_WORKFLOWS_CORDAPP_FILENAME) + .map { cordappDir / "$it.jar" } /** * Install any built-in cordapps that this node requires. @@ -33,10 +31,10 @@ class CordappController : Controller() { if (!config.cordappsDir.exists()) { config.cordappsDir.createDirectories() } - cordappJars.forEach { financeCordappJar -> - if (financeCordappJar.exists()) { - financeCordappJar.copyToDirectory(config.cordappsDir, StandardCopyOption.REPLACE_EXISTING) - log.info("Installed 'Finance' cordapp: $financeCordappJar") + cordappJars.forEach { cordappJar -> + if (cordappJar.exists()) { + cordappJar.copyToDirectory(config.cordappsDir, REPLACE_EXISTING) + log.info("Installed cordapp: $cordappJar") } } } @@ -55,5 +53,5 @@ class CordappController : Controller() { } } -val Path.isCordapp: Boolean get() = this.isReadable && this.fileName.toString().endsWith(".jar") -val Path.inCordappsDir: Boolean get() = (this.parent != null) && this.parent.endsWith("cordapps/") +val Path.isCordapp: Boolean get() = isReadable && fileName.toString().endsWith(".jar") +val Path.inCordappsDir: Boolean get() = (parent != null) && parent.endsWith("cordapps/") From d102a8ac11aa0c98461762aca6d6d026b314395b Mon Sep 17 00:00:00 2001 From: Jonathan Locke Date: Tue, 26 Feb 2019 09:23:27 +0000 Subject: [PATCH 029/159] ENT-3059: RPC disconnect log entry changed to info When an RPC client disconnects from the RPC server, the log entry created now has its log level set to info rather than warning. It is perfectly normal for an RPC client to disconnect - only the RPC client knows if it was intentional. --- node/src/main/kotlin/net/corda/node/services/rpc/RPCServer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/main/kotlin/net/corda/node/services/rpc/RPCServer.kt b/node/src/main/kotlin/net/corda/node/services/rpc/RPCServer.kt index 583a3e9705..1023e5a1ab 100644 --- a/node/src/main/kotlin/net/corda/node/services/rpc/RPCServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/rpc/RPCServer.kt @@ -255,7 +255,7 @@ class RPCServer( val notificationType = artemisMessage.getStringProperty(ManagementHelper.HDR_NOTIFICATION_TYPE) require(notificationType == CoreNotificationType.BINDING_REMOVED.name){"Message contained notification type of $notificationType instead of expected ${CoreNotificationType.BINDING_REMOVED.name}"} val clientAddress = artemisMessage.getStringProperty(ManagementHelper.HDR_ROUTING_NAME) - log.warn("Detected RPC client disconnect on address $clientAddress, scheduling for reaping") + log.info("Detected RPC client disconnect on address $clientAddress, scheduling for reaping") invalidateClient(SimpleString(clientAddress)) } From c11f42fc05adc2746aba7517f6a8496a724e2d69 Mon Sep 17 00:00:00 2001 From: josecoll Date: Tue, 26 Feb 2019 17:30:10 +0000 Subject: [PATCH 030/159] CORDA-2655 - Document CorDapp upgradeability guarantees in Corda 4. (#4793) * Document CorDapp upgradeability guarantees. * Incorporating feedback from RGB. * Incorporating feedback from MH. * Minor updates following re-review by RGB * Updates following review by MH. * Include new document in index. * Incorporating review feedback from MH. --- docs/source/api-contract-constraints.rst | 2 + docs/source/app-upgrade-notes.rst | 6 +- docs/source/cordapp-upgradeability.rst | 77 ++++++++++++++++++++++++ docs/source/upgrading-cordapps.rst | 2 + 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 docs/source/cordapp-upgradeability.rst diff --git a/docs/source/api-contract-constraints.rst b/docs/source/api-contract-constraints.rst index 6da31d01a6..b92cb94978 100644 --- a/docs/source/api-contract-constraints.rst +++ b/docs/source/api-contract-constraints.rst @@ -192,6 +192,8 @@ will be selected based on a variety of factors so that the above holds true. If possible constraints, the ``TransactionBuilder`` will throw an exception. +.. _constraints_whitelist_to_signature_ref: + How to use the ``SignatureAttachmentConstraint`` if states were already created on the network with the ``WhitelistedByZoneAttachmentConstraint`` ------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index 15bee7a3c1..cd0d083913 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -154,6 +154,8 @@ Would become: See :ref:`cordapp_configuration_files_ref` for more information. +.. _cordapp_upgrade_finality_flow_ref: + Step 5. Security: Upgrade your use of FinalityFlow -------------------------------------------------- @@ -411,4 +413,6 @@ Corda 4 adds several new APIs that help you build applications. Why not explore: * The `new withEntityManager API `_ for using JPA inside your flows and services. * :ref:`reference_states`, that let you use an input state without consuming it. -* :ref:`state_pointers`, that make it easier to 'point' to one state from another and follow the latest version of a linear state. \ No newline at end of file +* :ref:`state_pointers`, that make it easier to 'point' to one state from another and follow the latest version of a linear state. + +Please also read the :doc:`CorDapp Upgradeability Guarantees ` associated with CorDapp upgrading. \ No newline at end of file diff --git a/docs/source/cordapp-upgradeability.rst b/docs/source/cordapp-upgradeability.rst new file mode 100644 index 0000000000..1e7dc56eb7 --- /dev/null +++ b/docs/source/cordapp-upgradeability.rst @@ -0,0 +1,77 @@ +CorDapp Upgradeability Guarantees +================================= + +Corda 4.0 +--------- + +Corda 4 introduces a number of advanced features (such as signature constraints), and data security model improvements (such as attachments +trust checking and classloader isolation of contract attachments for transaction building and verification). + +The following guarantees are made for CorDapps running on Corda 4.0 + +- Compliant CorDapps compiled with previous versions of Corda (from 3.0) will execute without change on Corda 4.0 + + .. note:: by "compliant", we mean CorDapps that do not utilise Corda internal, non-stable or other non-committed public Corda APIs. + + Recommendation: security hardening changes in flow processing, specifically the ``FinalityFlow``, recommend upgrading existing CorDapp + receiver flows to use the new APIs and thus opting in to platform version 4. See :ref:`cordapp_upgrade_finality_flow_ref` for more information. + +- All constraint types (hash, CZ whitelisted, signature) are consumable within the same transaction if there is an associated contract attachment that satisfies all of them. + +- CorDapp Contract states generated on ledger using hash constraints are not directly migratable to signature constraints in this release. + Your compatibility zone operator may whitelist a JAR previously used to issue hash constrained states, and then you can follow the manual + process described in the paragraph below to migrate these to signature constraints. + +- CorDapp Contract states generated on ledger using CZ whitelisted constraints are migratable to signature constraints using a manual process + that requires programmatic code changes. See :ref:`constraints_whitelist_to_signature_ref` for more information. + +- Explicit Contract Upgrades are only supported for hash and CZ whitelisted constraint types. See :ref:`explicit_contract_upgrades_ref` for more information. + +- CorDapp contract attachments are not trusted from remote peers over the p2p network for the purpose of transaction verification. + A node operator must locally install *all* versions of a Contract attachment to be able to resolve a chain of contract states from its original version. + The RPC ``uploadAttachment`` mechanism can be used to achieve this (as well as conventional loading of a CorDapp by installing it in the nodes /cordapp directory). + See :ref:`cordapp_install_ref` and :ref:`cordapp_contract_attachments_ref` for more information. + +- CorDapp contract attachment classloader isolation has some important side-effects and edge cases to consider: + + 1. Contract attachments should include all 3rd party library dependencies in the same packaged JAR - we call this a "Fat JAR", + meaning that all dependencies are resolvable by the classloader by only loading a single JAR. + 2. Contract attachments that depend on other Contract attachments from a different packaged JAR are currently supported in so far as the Attachments Classloader + will attempt to resolve any external dependencies from the node's application classloader. It is thus paramount that dependent Contract + Attachments are loaded upon node startup from the respective /cordapps directory. + +- Rolling upgrades are partially supported. + A Node operator may choose to manually upload (via the RPC attachments uploader mechanism) a later version of a Contract Attachment than + the version their node is currently using for the purposes of transaction verification (received from remote peers). However, they will only + be able to build new transactions with the version that is currently loaded (installed from the nodes /cordapps directory) in their node. + +- Finance CorDapp (v4) + Whilst experimental, our test coverage has confirmed that states generated with the Finance CorDapp are interchangeable across Open Source + and Enterprise distributions. This has been made possible by releasing a single 4.0 version of the Finance Contracts CorDapp. + Please note the Finance application will be superseded shortly by the new Tokens SDK (https://github.com/corda/token-sdk) + +Later releases +-------------- + +The following additional capabilities are under consideration for delivery in follow-up releases to Corda 4.0: + +- CorDapp Contract states generated on ledger using hash constraints will be automatically migrated to signature constraints when building new transactions + where the latest installed contract Jar is signed as per :ref:`CorDapp Jar signing `. + +- CorDapp Contract states generated on ledger using CZ whitelisted constraints will be automatically migrated to signature constraints when building new transactions + where the latest installed contract Jar is signed as per :ref:`CorDapp Jar signing `. + +- Explicit Contract Upgrades will be supported for all constraint types: hash, CZ whitelisted and signature. + In practice, it should only be necessary to upgrade from hash or CZ whitelisted to new signature constrained contract types. + signature constrained Contracts are upgradeable seamlessly (through built in serialization and code signing controls) without requiring explicit upgrades. + +- Contract attachments will be able to explicitly declare their dependencies on other Contract attachments such that these are automatically + loaded by the Attachments Classloader (rendering the 4.0 fallback to application classloader mechanism redundant). + This improved modularity removes the need to "Fat JAR" all dependencies together in a single jar. + +- Rolling upgrades will be fully supported. + A Node operator will be able to pre-register (by hash or code signing public key) versions of CorDapps they are not yet ready to install locally, + but wish to use for the purposes of transaction verification with peers running later versions of a CorDapp. + +.. note:: Trusted downloading and execution of contract attachments from remote peers will not be integrated until secure JVM sand-boxing is available. + diff --git a/docs/source/upgrading-cordapps.rst b/docs/source/upgrading-cordapps.rst index 638e12847d..6d885c029b 100644 --- a/docs/source/upgrading-cordapps.rst +++ b/docs/source/upgrading-cordapps.rst @@ -259,6 +259,8 @@ a drain is complete there should be no outstanding checkpoints or running flows. A node can be drained or undrained via RPC using the ``setFlowsDrainingModeEnabled`` method, and via the shell using the standard ``run`` command to invoke the RPC. See :doc:`shell` to learn more. +.. _explicit_contract_upgrades_ref: + Contract and state versioning ----------------------------- From 4721b1d09573f6fcda0334c5ec0fbc844a53b4aa Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Tue, 26 Feb 2019 20:30:47 +0000 Subject: [PATCH 031/159] ENT-3165 Add unit test for reference state consumption. (#4818) --- .../core/flows/ReferencedStatesFlowTests.kt | 58 +++++++++++++++++-- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/core/src/test/kotlin/net/corda/core/flows/ReferencedStatesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/ReferencedStatesFlowTests.kt index a4f84d8fd5..b64b60be49 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ReferencedStatesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ReferencedStatesFlowTests.kt @@ -15,15 +15,9 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.VersionInfo import net.corda.testing.common.internal.testNetworkParameters -import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.internal.vault.DUMMY_LINEAR_CONTRACT_PROGRAM_ID -import net.corda.testing.internal.vault.DummyLinearContract -import net.corda.testing.node.StartedMockNode import net.corda.testing.node.internal.* -import net.corda.testing.node.transaction import org.junit.After import org.junit.Before -import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals @@ -121,6 +115,58 @@ class ReferencedStatesFlowTests { assertEquals(2, allRefStates.states.size) } + @Test + fun `check old ref state is consumed when update used in tx with relevant states`() { + // 1. Create a state to be used as a reference state. Don't share it. + val newRefTx = nodes[0].services.startFlow(CreateRefState()).resultFuture.getOrThrow() + val newRefState = newRefTx.tx.outRefsOfType().single() + + // 2. Use the "newRefState" in a transaction involving another party (nodes[1]) which creates a new state. They should store the new state and the reference state. + val newTx = nodes[0].services.startFlow(UseRefState(nodes[1].info.legalIdentities.first(), newRefState.state.data.linearId)) + .resultFuture.getOrThrow() + // Wait until node 1 stores the new tx. + nodes[1].services.validatedTransactions.updates.filter { it.id == newTx.id }.toFuture().getOrThrow() + // Check that nodes[1] has finished recording the transaction (and updating the vault.. hopefully!). + // nodes[1] should have two states. The newly created output of type "Regular.State" and the reference state created by nodes[0]. + assertEquals(2, nodes[1].services.vaultService.queryBy().states.size) + // Now let's find the specific reference state on nodes[1]. + val refStateLinearId = newRefState.state.data.linearId + val query = QueryCriteria.LinearStateQueryCriteria(linearId = listOf(refStateLinearId)) + val theReferencedState = nodes[1].services.vaultService.queryBy(query) + // There should be one result - the reference state. + assertEquals(newRefState, theReferencedState.states.single()) + // The reference state should not be consumed. + assertEquals(Vault.StateStatus.UNCONSUMED, theReferencedState.statesMetadata.single().status) + // nodes[0] should also have the same state. + val nodeZeroQuery = QueryCriteria.LinearStateQueryCriteria(linearId = listOf(refStateLinearId)) + val theReferencedStateOnNodeZero = nodes[0].services.vaultService.queryBy(nodeZeroQuery) + assertEquals(newRefState, theReferencedStateOnNodeZero.states.single()) + assertEquals(Vault.StateStatus.UNCONSUMED, theReferencedStateOnNodeZero.statesMetadata.single().status) + + // 3. Update the reference state but don't share the update. + val updatedRefTx = nodes[0].services.startFlow(UpdateRefState(newRefState)).resultFuture.getOrThrow() + + // 4. Use the evolved state as a reference state. + val updatedTx = nodes[0].services.startFlow(UseRefState(nodes[1].info.legalIdentities.first(), newRefState.state.data.linearId)) + .resultFuture.getOrThrow() + // Wait until node 1 stores the new tx. + nodes[1].services.validatedTransactions.updates.filter { it.id == updatedTx.id }.toFuture().getOrThrow() + // Check that nodes[1] has finished recording the transaction (and updating the vault.. hopefully!). + // nodes[1] should have four states. The originals, plus the newly created output of type "Regular.State" and the reference state created by nodes[0]. + assertEquals(4, nodes[1].services.vaultService.queryBy(QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)).states.size) + // Now let's find the original reference state on nodes[1]. + val updatedQuery = QueryCriteria.VaultQueryCriteria(stateRefs = listOf(newRefState.ref), status = Vault.StateStatus.ALL) + val theOriginalReferencedState = nodes[1].services.vaultService.queryBy(updatedQuery) + // There should be one result - the original reference state. + assertEquals(newRefState, theOriginalReferencedState.states.single()) + // The reference state should be consumed. + assertEquals(Vault.StateStatus.CONSUMED, theOriginalReferencedState.statesMetadata.single().status) + // nodes[0] should also have the same state. + val theOriginalReferencedStateOnNodeZero = nodes[0].services.vaultService.queryBy(updatedQuery) + assertEquals(newRefState, theOriginalReferencedStateOnNodeZero.states.single()) + assertEquals(Vault.StateStatus.CONSUMED, theOriginalReferencedStateOnNodeZero.statesMetadata.single().status) + } + // A dummy reference state contract. class RefState : Contract { companion object { From 2ff7860e4b89b5a8e451ed2484d3b9bf12f1c71c Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Wed, 27 Feb 2019 11:40:48 +0000 Subject: [PATCH 032/159] ENT-3165 Backport caching changes to OS. (#4821) --- .../utilities/InfrequentlyMutatedCache.kt | 57 +++++++++++---- .../utilities/InfrequentlyMutatedCacheTest.kt | 73 +++++++++++++++++++ 2 files changed, 116 insertions(+), 14 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/utilities/InfrequentlyMutatedCache.kt b/node/src/main/kotlin/net/corda/node/utilities/InfrequentlyMutatedCache.kt index ee34dbce5d..64135fb94f 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/InfrequentlyMutatedCache.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/InfrequentlyMutatedCache.kt @@ -2,16 +2,18 @@ package net.corda.node.utilities import com.github.benmanes.caffeine.cache.Caffeine import net.corda.core.internal.NamedCacheFactory +import net.corda.core.internal.VisibleForTesting import net.corda.nodeapi.internal.persistence.contextTransactionOrNull +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger /** * Wraps a Caffeine cache and provides thread safe and database transaction aware invalidation. * - * All access should be via [get] and [invalidate]. Data to be mutated should be changed at source (presumed to be a database) - * followed by a call to [invalidate] the value associated with a key. During periods of invalidity, the source will always be - * consulted to resolve transaction visibility issues. This is why invalidation should be infrequent, otherwise the pessimism - * of the cache for invalidated values will result in few cache hits. + * All access should be via [get], [getIfPresent] or [invalidate]. Data to be mutated should be changed at source + * (presumed to be a database) followed by a call to [invalidate] the value associated with a key. + * During periods of invalidity, the source will always be consulted to resolve transaction visibility issues. + * This is why invalidation should be infrequent, otherwise the pessimism of the cache for invalidated values will result in few cache hits. */ class InfrequentlyMutatedCache(name: String, cacheFactory: NamedCacheFactory) { /** @@ -21,8 +23,8 @@ class InfrequentlyMutatedCache(name: String, cacheFactory: Nam * @param valueGetter A function to return the value for the key if the cache does not have it. */ fun get(key: K, valueGetter: (K) -> V): V { - val wrapper = backingCache.get(key) { key: K -> - Wrapper.Valid(valueGetter(key)) + val wrapper = backingCache.get(key) { k: K -> + currentlyInvalid[k] ?: Wrapper.Valid(valueGetter(k)) } return when(wrapper) { is Wrapper.Valid -> { wrapper.value } @@ -30,6 +32,23 @@ class InfrequentlyMutatedCache(name: String, cacheFactory: Nam } } + /** + * Retrieve the value associated with the given key in the cache, or null if not cached. + * + * @param key The key to retrieve. + */ + fun getIfPresent(key: K): V? { + val wrapper = backingCache.get(key) { k: K -> + null + } + return when (wrapper) { + is Wrapper.Valid -> { + wrapper.value + } + else -> null + } + } + /** * Inform the cache that the current value for the key may have been updated. Subsequent calls to [get] * will not use the current cached value. The point at which values start to be cached again will be @@ -37,18 +56,27 @@ class InfrequentlyMutatedCache(name: String, cacheFactory: Nam * who do not have transaction visibility of the updated value from re-populating the cache with an incorrect value. */ fun invalidate(key: K) { - backingCache.asMap().compute(key) { key: K, value: Wrapper? -> + backingCache.asMap().compute(key) { k: K, value: Wrapper? -> when(value) { - is Wrapper.Valid -> { invalidate(key, Wrapper.Invalidated()) } - is Wrapper.Invalidated -> { invalidate(key, value) } - else -> { null } + is Wrapper.Invalidated -> { + invalidate(k, value) + } + else -> { + invalidate(k, currentlyInvalid[k] ?: Wrapper.Invalidated()) + } } } } + @VisibleForTesting + internal fun flushCache() { + backingCache.invalidateAll() + } + private fun invalidate(key: K, value: Wrapper.Invalidated): Wrapper.Invalidated { val tx = contextTransactionOrNull value.invalidators.incrementAndGet() + currentlyInvalid[key] = value if (tx != null) { // When we close, we can't start using caching again until all simultaneously open transactions are closed. tx.onClose { tx.database.onAllOpenTransactionsClosed { decrementInvalidators(key, value) } } @@ -63,6 +91,7 @@ class InfrequentlyMutatedCache(name: String, cacheFactory: Nam // Maybe we can replace the invalidated value with nothing, so it gets loaded next time. backingCache.asMap().compute(key) { key: K, currentValue: Wrapper? -> if(currentValue === value && value.invalidators.get() == 0) { + currentlyInvalid.remove(key) null } else currentValue } @@ -71,14 +100,14 @@ class InfrequentlyMutatedCache(name: String, cacheFactory: Nam private val backingCache = cacheFactory.buildNamed>(Caffeine.newBuilder(), name) - private sealed class Wrapper { - abstract val value: V? + // This protects against the cache purging something that is marked as invalid and thus we "forget" it shouldn't be cached. + private val currentlyInvalid = ConcurrentHashMap>() + private sealed class Wrapper { class Invalidated : Wrapper() { val invalidators = AtomicInteger(0) - override val value: V? = null } - class Valid(override val value: V) : Wrapper() + class Valid(val value: V) : Wrapper() } } \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/utilities/InfrequentlyMutatedCacheTest.kt b/node/src/test/kotlin/net/corda/node/utilities/InfrequentlyMutatedCacheTest.kt index e0cc3ab47c..17bd9ceaba 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/InfrequentlyMutatedCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/InfrequentlyMutatedCacheTest.kt @@ -11,6 +11,7 @@ import org.junit.Test import java.util.concurrent.Phaser import kotlin.concurrent.thread import kotlin.test.assertEquals +import kotlin.test.assertNull class InfrequentlyMutatedCacheTest { private val cache = InfrequentlyMutatedCache("foo", TestingNamedCacheFactory()) @@ -35,6 +36,14 @@ class InfrequentlyMutatedCacheTest { } } + @Test + fun `getIfPresent from empty cache returns null`() { + database.transaction { + val result = cache.getIfPresent("foo") + assertNull(result) + } + } + @Test fun `other thread get returns result of local thread loader`() { database.transaction { @@ -63,6 +72,18 @@ class InfrequentlyMutatedCacheTest { } } + @Test + fun `getIfPresent after get from empty cache returns result of first loader`() { + database.transaction { + // This will cache "2" + cache.get("foo") { + 2 + } + val result = cache.getIfPresent("foo") + assertEquals(2, result) + } + } + @Test fun `second get from empty cache with invalidate in the middle returns result of second loader`() { database.transaction { @@ -78,6 +99,38 @@ class InfrequentlyMutatedCacheTest { } } + @Test + fun `getIfPresent after get from empty cache with invalidate in the middle returns null`() { + database.transaction { + // This will cache "2" + cache.get("foo") { + 2 + } + cache.invalidate("foo") + val result = cache.getIfPresent("foo") + assertNull(result) + } + } + + @Test + fun `second get from empty cache with invalidate and flush in the middle returns result of third loader`() { + database.transaction { + // This will cache "2" + cache.get("foo") { + 3 + } + cache.invalidate("foo") + cache.flushCache() + cache.get("foo") { + 2 + } + val result = cache.get("foo") { + 1 + } + assertEquals(1, result) + } + } + @Test fun `other thread get with invalidate in the middle returns result of second loader`() { database.transaction { @@ -118,6 +171,26 @@ class InfrequentlyMutatedCacheTest { } } + @Test + fun `getIfPresent outside first transaction from empty cache with invalidate in the middle returns result of third loader`() { + database.transaction { + // This will cache "2" + cache.get("foo") { + 2 + } + cache.invalidate("foo") + // This should not get cached, as the transaction that invalidated is still in-flight. + val result = cache.get("foo") { + 1 + } + assertEquals(1, result) + } + database.transaction { + val result = cache.getIfPresent("foo") + assertNull(result) + } + } + @Test fun `other thread get outside first transaction with invalidate in the middle returns result of other thread`() { database.transaction { From f52d158f2d71cee40c556d70ce4bb39ba25d26aa Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Wed, 27 Feb 2019 12:05:39 +0000 Subject: [PATCH 033/159] CORDA-2665: Updated OwnableState relevancy check put back to V3 version (#4819) * CORDA-2665: Updated OwnableState relevancy check is now gated on target version 4 https://github.com/corda/corda/pull/3789 changed the relevancy check of OwnableState to include the participants list in addition to the owner. This however breaks existing apps which assume (in their vault query) an OwnableState is recorded to the vault if-and-only-if the owner matches. * CORDA-2665 Don't switch to new behaviour on target version upgrade. --- .../node/services/vault/NodeVaultService.kt | 10 +---- .../services/vault/NodeVaultServiceTest.kt | 41 ++++++++++++------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index a521130cf6..853b3ad319 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -20,11 +20,7 @@ import net.corda.node.services.api.SchemaService import net.corda.node.services.api.VaultServiceInternal import net.corda.node.services.schema.PersistentStateService import net.corda.node.services.statemachine.FlowStateMachineImpl -import net.corda.nodeapi.internal.persistence.CordaPersistence -import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit -import net.corda.nodeapi.internal.persistence.currentDBSession -import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction -import net.corda.nodeapi.internal.persistence.contextTransactionOrNull +import net.corda.nodeapi.internal.persistence.* import org.hibernate.Session import rx.Observable import rx.subjects.PublishSubject @@ -71,9 +67,7 @@ class NodeVaultService( */ fun isRelevant(state: ContractState, myKeys: Set): Boolean { val keysToCheck = when (state) { - // Sometimes developers forget to add the owning key to participants for OwnableStates. - // TODO: This logic should probably be moved to OwnableState so we can just do a simple intersection here. - is OwnableState -> (state.participants.map { it.owningKey } + state.owner.owningKey).toSet() + is OwnableState -> listOf(state.owner.owningKey) else -> state.participants.map { it.owningKey } } return keysToCheck.any { it.containsAny(myKeys) } diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index e3557ebd5c..afa9e23613 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -22,8 +22,8 @@ import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.toNonEmptySet import net.corda.finance.* import net.corda.finance.contracts.asset.Cash -import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.contracts.utils.sumCash +import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.workflows.asset.CashUtils import net.corda.finance.workflows.getCashBalance import net.corda.node.services.api.IdentityServiceInternal @@ -42,6 +42,7 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.* import rx.observers.TestSubscriber import java.math.BigDecimal +import java.security.PublicKey import java.util.* import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors @@ -98,8 +99,8 @@ class NodeVaultServiceTest { vaultFiller = VaultFiller(services, dummyNotary) // This is safe because MockServices only ever have a single identity identity = services.myInfo.singleIdentityAndCert() - issuerServices = MockServices(cordappPackages, dummyCashIssuer, mock(), parameters) - bocServices = MockServices(cordappPackages, bankOfCorda, mock(), parameters) + issuerServices = MockServices(cordappPackages, dummyCashIssuer, mock(), parameters) + bocServices = MockServices(cordappPackages, bankOfCorda, mock(), parameters) services.identityService.verifyAndRegisterIdentity(DUMMY_CASH_ISSUER_IDENTITY) services.identityService.verifyAndRegisterIdentity(BOC_IDENTITY) } @@ -129,6 +130,7 @@ class NodeVaultServiceTest { } class FungibleFoo(override val amount: Amount, override val participants: List) : FungibleState + @Test fun `fungible state selection test`() { val issuerParty = services.myInfo.legalIdentities.first() @@ -552,20 +554,29 @@ class NodeVaultServiceTest { @Test fun `is ownable state relevant`() { - val amount = Amount(1000, Issued(BOC.ref(1), GBP)) - val wellKnownCash = Cash.State(amount, identity.party) - val myKeys = services.keyManagementService.filterMyKeys(listOf(wellKnownCash.owner.owningKey)) - assertTrue { NodeVaultService.isRelevant(wellKnownCash, myKeys.toSet()) } + val myAnonymousIdentity = services.keyManagementService.freshKeyAndCert(identity, false) + val myKeys = services.keyManagementService.filterMyKeys(listOf(identity.owningKey, myAnonymousIdentity.owningKey)).toSet() - val anonymousIdentity = services.keyManagementService.freshKeyAndCert(identity, false) - val anonymousCash = Cash.State(amount, anonymousIdentity.party) - val anonymousKeys = services.keyManagementService.filterMyKeys(listOf(anonymousCash.owner.owningKey)) - assertTrue { NodeVaultService.isRelevant(anonymousCash, anonymousKeys.toSet()) } + // Well-known owner + assertTrue { myKeys.isOwnableStateRelevant(identity.party, participants = emptyList()) } + // Anonymous owner + assertTrue { myKeys.isOwnableStateRelevant(myAnonymousIdentity.party, participants = emptyList()) } + // Unknown owner + assertFalse { myKeys.isOwnableStateRelevant(createUnknownIdentity(), participants = emptyList()) } + // Under target version 3 only the owner is relevant. This is to preserve backwards compatibility + assertFalse { myKeys.isOwnableStateRelevant(createUnknownIdentity(), participants = listOf(identity.party)) } + } - val thirdPartyIdentity = AnonymousParty(generateKeyPair().public) - val thirdPartyCash = Cash.State(amount, thirdPartyIdentity) - val thirdPartyKeys = services.keyManagementService.filterMyKeys(listOf(thirdPartyCash.owner.owningKey)) - assertFalse { NodeVaultService.isRelevant(thirdPartyCash, thirdPartyKeys.toSet()) } + private fun createUnknownIdentity() = AnonymousParty(generateKeyPair().public) + + private fun Set.isOwnableStateRelevant(owner: AbstractParty, participants: List): Boolean { + class TestOwnableState : OwnableState { + override val owner: AbstractParty get() = owner + override val participants: List get() = participants + override fun withNewOwner(newOwner: AbstractParty): CommandAndState = throw AbstractMethodError() + } + + return NodeVaultService.isRelevant(TestOwnableState(), this) } // TODO: Unit test linear state relevancy checks From 74c5b8d127f199f6a2f33b9b4ab7a49b7c0cb75a Mon Sep 17 00:00:00 2001 From: Dominic Fox Date: Tue, 26 Feb 2019 17:48:43 +0000 Subject: [PATCH 034/159] CORDA-2674 observe the true type of an atomic type --- .../serialization/internal/model/LocalTypeInformationBuilder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeInformationBuilder.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeInformationBuilder.kt index 9400e6242f..005b8d3e88 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeInformationBuilder.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeInformationBuilder.kt @@ -104,7 +104,7 @@ internal data class LocalTypeInformationBuilder(val lookup: LocalTypeLookup, !EnumSet::class.java.isAssignableFrom(type) -> LocalTypeInformation.ACollection(type, typeIdentifier, LocalTypeInformation.Unknown) Map::class.java.isAssignableFrom(type) -> LocalTypeInformation.AMap(type, typeIdentifier, LocalTypeInformation.Unknown, LocalTypeInformation.Unknown) type == String::class.java -> LocalTypeInformation.Atomic(String::class.java, typeIdentifier) - type.kotlin.javaPrimitiveType != null ->LocalTypeInformation.Atomic(type.kotlin.javaPrimitiveType!!, typeIdentifier) + type.kotlin.javaPrimitiveType != null ->LocalTypeInformation.Atomic(type, typeIdentifier) type.isEnum -> LocalTypeInformation.AnEnum( type, typeIdentifier, From 2173228b27d01881514dbacb746fd9633348780c Mon Sep 17 00:00:00 2001 From: Dominic Fox Date: Tue, 26 Feb 2019 18:22:32 +0000 Subject: [PATCH 035/159] Unit test covering https://r3-cev.atlassian.net/browse/CORDA-2674 --- .../internal/amqp/JavaEvolutionTests.java | 49 ++++++++++++++++++ .../JavaEvolutionTests.testNullableInteger | Bin 0 -> 261 bytes 2 files changed, 49 insertions(+) create mode 100644 serialization/src/test/resources/net/corda/serialization/internal/amqp/JavaEvolutionTests.testNullableInteger diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java index fa8449840e..f488505710 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java @@ -97,4 +97,53 @@ public class JavaEvolutionTests { N2.class, TestSerializationContext.testSerializationContext); } + + // Class as it was when it was serialized and written to disk. Uncomment + // if the test referencing the object needs regenerating. + /* + @SuppressWarnings("unused") + static class POJOWithInteger { + private Integer id; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + } + */ + + public interface ForceEvolution { } + + // Class as it exists now with the newly added element + @SuppressWarnings("unused") + static class POJOWithInteger implements ForceEvolution { + private Integer id; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + } + + @Test + public void testNullableInteger() throws IOException { + // Uncomment to regenerate the base state of the test + //POJOWithInteger n = new POJOWithInteger(); + //n.setId(100); + //AMQPTestUtilsKt.writeTestResource(this, new SerializationOutput(factory).serialize( + // n, TestSerializationContext.testSerializationContext)); + + POJOWithInteger n2 = new DeserializationInput(factory).deserialize( + new SerializedBytes<>(AMQPTestUtilsKt.readTestResource(this)), + POJOWithInteger.class, + TestSerializationContext.testSerializationContext); + + assertEquals(Integer.valueOf(100), n2.getId()); + } } diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/JavaEvolutionTests.testNullableInteger b/serialization/src/test/resources/net/corda/serialization/internal/amqp/JavaEvolutionTests.testNullableInteger new file mode 100644 index 0000000000000000000000000000000000000000..4d60a11a6011eaacde85d8e7a9c12f58989aaf2d GIT binary patch literal 261 zcmYe!FG@*dWME)uIGO|`85kH3d}L-=tdy5pqL&PkvvT(^3-IwZ_VFxCO)DrZH%YIE zNQ-o|wLQSh7?J|l#&lpU1F1Xq!#5R z=IABn78dAvC6*<+mgVP^g5*L{i%W`C0{p%F!!t`VJb?<+Q;Qs2;RZ7wP-7y-c@76; e5Uyc4z|X#rDKljuGtg#N2L~r7xOtoh85sb>d{OHF literal 0 HcmV?d00001 From 6d5ccfaeace71ebdb0d8a0a4f8634e2b9e8646c5 Mon Sep 17 00:00:00 2001 From: Dominic Fox Date: Wed, 27 Feb 2019 10:27:47 +0000 Subject: [PATCH 036/159] Explanatory comment on test --- .../serialization/internal/amqp/JavaEvolutionTests.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java index f488505710..8360cc8af9 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java @@ -115,9 +115,15 @@ public class JavaEvolutionTests { } */ + /* + We want to force the evolution serializer factory to check that the property types of the local and + remote types match up, which only happens if both types have the same set of property names (i.e. + this might be a spurious evolution candidate). We do this by adding a marker interface to the type, + which will change its fingerprint but have no effect on its serialisation behaviour. + */ public interface ForceEvolution { } - // Class as it exists now with the newly added element + // Class as it exists now with the newly added interface @SuppressWarnings("unused") static class POJOWithInteger implements ForceEvolution { private Integer id; From b9e48043884c2923ec6aff2c6dbb4f19fdd36558 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Wed, 27 Feb 2019 14:32:39 +0000 Subject: [PATCH 037/159] ENT-3128: Update documentation for Node Explorer. (#4817) --- docs/source/node-explorer.rst | 10 +++++----- tools/explorer/README.md | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/node-explorer.rst b/docs/source/node-explorer.rst index 15a377fae9..b29cd26028 100644 --- a/docs/source/node-explorer.rst +++ b/docs/source/node-explorer.rst @@ -32,11 +32,11 @@ you to create local Corda networks on your desktop. For example: * Notary * Bank of Breakfast Tea (*Issuer node* for GBP) * Bank of Big Apples (*Issuer node* for USD) - * Alice (Participant node, for user Alice) - * Bob (Participant node, for user Bob) + * Alice (*Participant node*, for user Alice) + * Bob (*Participant node*, for user Bob) DemoBench will deploy all nodes with Corda's Finance CorDapp automatically, and -allow you to launch an instance of Node Explorer for each. You will be logged +allow you to launch an instance of Node Explorer for each. You will also be logged into the Node Explorer automatically. When connected to an *Issuer* node, a user can execute cash transaction commands to issue and move cash to itself or other @@ -55,8 +55,8 @@ run as Interface --------- Login - User can login to any Corda node using the explorer. Alternatively, ``gradlew explorer:runDemoNodes`` can be used to start up demo nodes for testing. - Corda node address, username and password are required for login, the address is defaulted to localhost:0 if leave blank. + User can login to any Corda node using the explorer. + Corda node address, username and password are required for login, the address is defaulted to localhost:0 if left blank. Username and password can be configured via the ``rpcUsers`` field in node's configuration file. .. image:: resources/explorer/login.png diff --git a/tools/explorer/README.md b/tools/explorer/README.md index a8974402fe..f6f54a2756 100644 --- a/tools/explorer/README.md +++ b/tools/explorer/README.md @@ -21,8 +21,8 @@ which allows you to create local Corda networks on your desktop. For example: * Notary * Bank of Breakfast Tea (*Issuer node* for GBP) * Bank of Big Apples (*Issuer node* for USD) - * Alice - * Bob + * Alice (*Participant node* for user Alice) + * Bob (*Participant node* for user Bob) DemoBench will deploy all nodes with Corda's Finance CorDapp automatically, and allow you to launch an instance of Node Explorer for each. From fbda11dbc8aee6386dbf15d1f9db9383d31e12fc Mon Sep 17 00:00:00 2001 From: James Higgs Date: Wed, 27 Feb 2019 14:30:11 +0000 Subject: [PATCH 038/159] Ensure criteria sent from a V3 RPC client can be deserialized --- .../core/node/services/vault/QueryCriteria.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/main/kotlin/net/corda/core/node/services/vault/QueryCriteria.kt b/core/src/main/kotlin/net/corda/core/node/services/vault/QueryCriteria.kt index 83490d874f..fe1a809062 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/vault/QueryCriteria.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/vault/QueryCriteria.kt @@ -11,6 +11,7 @@ import net.corda.core.identity.AbstractParty import net.corda.core.node.services.Vault import net.corda.core.schemas.StatePersistable import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.DeprecatedConstructorForDeserialization import net.corda.core.utilities.OpaqueBytes import java.security.PublicKey import java.time.Instant @@ -102,6 +103,7 @@ sealed class QueryCriteria : GenericQueryCriteria>? = null, @@ -110,14 +112,19 @@ sealed class QueryCriteria : GenericQueryCriteria>?) : this(status, contractStateTypes, participants = null) + @DeprecatedConstructorForDeserialization(version = 3) constructor(status: Vault.StateStatus, contractStateTypes: Set>?, stateRefs: List?) : this( status, contractStateTypes, stateRefs, participants = null ) + @DeprecatedConstructorForDeserialization(version = 4) constructor(status: Vault.StateStatus, contractStateTypes: Set>?, stateRefs: List?, notary: List?) : this( status, contractStateTypes, stateRefs, notary, participants = null ) + @DeprecatedConstructorForDeserialization(version = 5) constructor(status: Vault.StateStatus, contractStateTypes: Set>?, stateRefs: List?, notary: List?, softLockingCondition: SoftLockingCondition?) : this( status, contractStateTypes, stateRefs, notary, softLockingCondition, participants = null ) @@ -174,6 +181,7 @@ sealed class QueryCriteria : GenericQueryCriteria? = null, uuid: List? = null, @@ -182,6 +190,7 @@ sealed class QueryCriteria : GenericQueryCriteria>? = null ) : this(participants, uuid, externalId, status, contractStateTypes, Vault.RelevancyStatus.ALL) + @DeprecatedConstructorForDeserialization(version = 3) constructor( participants: List? = null, linearId: List? = null, @@ -191,6 +200,7 @@ sealed class QueryCriteria : GenericQueryCriteria? = null, linearId: List? = null, @@ -264,6 +274,7 @@ sealed class QueryCriteria : GenericQueryCriteria? = null, owner: List? = null, @@ -325,6 +336,7 @@ sealed class QueryCriteria : GenericQueryCriteria, status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED, @@ -381,10 +393,13 @@ sealed class AttachmentQueryCriteria : GenericQueryCriteria? = null, val versionCondition: ColumnPredicate? = null) : AttachmentQueryCriteria() { // V3 c'tors + @DeprecatedConstructorForDeserialization(version = 3) constructor(uploaderCondition: ColumnPredicate? = null, filenameCondition: ColumnPredicate? = null, uploadDateCondition: ColumnPredicate? = null) : this(uploaderCondition, filenameCondition, uploadDateCondition, null) + @DeprecatedConstructorForDeserialization(version = 1) constructor(uploaderCondition: ColumnPredicate?) : this(uploaderCondition, null) + @DeprecatedConstructorForDeserialization(version = 2) constructor(uploaderCondition: ColumnPredicate?, filenameCondition: ColumnPredicate?) : this(uploaderCondition, filenameCondition, null) override fun visit(parser: AttachmentsQueryCriteriaParser): Collection { From b434d49111f0f346b9a3d65fa6f9f92b390a8a28 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 27 Feb 2019 11:07:28 +0100 Subject: [PATCH 039/159] Docs: fix backup recommendations link --- docs/source/node-administration.rst | 2 +- docs/source/node-upgrade-notes.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/node-administration.rst b/docs/source/node-administration.rst index 3f58bc7dea..1b6e22e0c4 100644 --- a/docs/source/node-administration.rst +++ b/docs/source/node-administration.rst @@ -203,7 +203,7 @@ For launching on Windows without PowerShell, it is not possible to perform comma .. warning:: If this approach is taken, the passwords will appear in the windows command prompt history. -.. _ref-backup-recommendations: +.. _backup-recommendations: Backup recommendations ---------------------- diff --git a/docs/source/node-upgrade-notes.rst b/docs/source/node-upgrade-notes.rst index 24148562d3..6de9a3860e 100644 --- a/docs/source/node-upgrade-notes.rst +++ b/docs/source/node-upgrade-notes.rst @@ -36,7 +36,7 @@ It's always a good idea to make a backup of your data before upgrading any serve You can simply make a copy of the node's data directory to enable this. If you use an external non-H2 database please consult your database user guide to learn how to make backups. -We provide some `backup recommendations `_ if you'd like more detail. +We provide some :ref:`backup recommendations ` if you'd like more detail. Step 3. Replace ``corda.jar`` with the new version -------------------------------------------------- From b52fdf0e0fe8497b25da032bf874bee832b3145b Mon Sep 17 00:00:00 2001 From: James Higgs Date: Thu, 28 Feb 2019 11:34:28 +0000 Subject: [PATCH 040/159] Port the improved error message introduced in ENT-3068 to OS --- .../internal/amqp/EvolutionSerializerFactory.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt index 0e3804923c..8d017fa057 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt @@ -109,8 +109,9 @@ class DefaultEvolutionSerializerFactory( newProperties.forEach { propertyName -> if (localProperties[propertyName]!!.mustBeProvided) throw EvolutionSerializationException( this, - "Mandatory property $propertyName of local type is not present in remote type - " + - "did someone remove a property from the schema without considering old clients?") + "Mandatory property $propertyName of local type is not present in remote type. " + + "This implies the type has not evolved in a backwards compatible way. " + + "Consider making $propertyName nullable in the newer version of this type.") } } From cd321c9da2bf251ffe5850651a246450f24aa89e Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Thu, 28 Feb 2019 14:33:13 +0000 Subject: [PATCH 041/159] Correct extending flows java docs and add note on overriding responders (#4830) Add `@InitiatedBy` to the java docs on the responder flow, this is already shown in the kotlin version. Add a note on overriding responders, instructing developers to still include the `@InitiatedBy` annotation on the new responder even though the configuration setup can make developers think that defining the override will guarantee the initiator and responder will join up correctly. --- docs/source/flow-overriding.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/source/flow-overriding.rst b/docs/source/flow-overriding.rst index 986b60606c..a7b48a6ae1 100644 --- a/docs/source/flow-overriding.rst +++ b/docs/source/flow-overriding.rst @@ -75,6 +75,7 @@ with refactoring into `Base` and `Sub` classes. A simple example is shown below. } } + @InitiatedBy(Initiator.class) public class SubResponder extends BaseResponder { public SubResponder(FlowSession counterpartySession) { super(counterpartySession); @@ -91,7 +92,7 @@ Corda would detect that both ``BaseResponder`` and ``SubResponder`` are configur Corda will then calculate the hops to ``FlowLogic`` and select the implementation which is furthest distance, ie: the most subclassed implementation. In the above example, ``SubResponder`` would be selected as the default responder for ``Initiator`` -.. note:: The flows do not need to be within the same CordApp, or package, therefore to customise a shared app you obtained from a third party, you'd write your own CorDapp that subclasses the first." +.. note:: The flows do not need to be within the same CordApp, or package, therefore to customise a shared app you obtained from a third party, you'd write your own CorDapp that subclasses the first. Overriding a flow via node configuration ---------------------------------------- @@ -100,6 +101,8 @@ Whilst the subclassing approach is likely to be useful for most applications, th This would be useful if for example, a specific CordApp user requires such a different responder that subclassing an existing flow would not be a good solution. In this case, it's possible to specify a hardcoded flow via the node configuration. +.. note:: A new responder written to override an existing responder must still be annotated with ``@InitiatedBy`` referencing the base initiator. + The configuration section is named ``flowOverrides`` and it accepts an array of ``overrides`` .. container:: codeset From adad7862d6ac843e7647eba34e5be1f45a22d9ca Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Thu, 28 Feb 2019 16:01:33 +0000 Subject: [PATCH 042/159] ENT-3187 Check that we're not calling `toList` on concurrent collections (#4828) * ENT-3165 Kotlin toList() does not work on concurrent collections. OS backport. ENT-3165 Added comment. * ENT-3187 Additional use of toList() on concurrent data structure. --- core/src/main/kotlin/net/corda/core/internal/LazyPool.kt | 3 ++- .../net/corda/nodeapi/internal/persistence/CordaPersistence.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/internal/LazyPool.kt b/core/src/main/kotlin/net/corda/core/internal/LazyPool.kt index bc2d0132a6..2c94dea8df 100644 --- a/core/src/main/kotlin/net/corda/core/internal/LazyPool.kt +++ b/core/src/main/kotlin/net/corda/core/internal/LazyPool.kt @@ -62,7 +62,8 @@ class LazyPool( */ fun close(): Iterable { lifeCycle.justTransition(State.FINISHED) - val elements = poolQueue.toList() + // Does not use kotlin toList() as it currently is not safe to use on concurrent data structures. + val elements = ArrayList(poolQueue) poolQueue.clear() return elements } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt index ab039bf05f..0db8ac43b1 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt @@ -163,7 +163,8 @@ class CordaPersistence( } fun onAllOpenTransactionsClosed(callback: () -> Unit) { - val allOpen = liveTransactions.values.toList() + // Does not use kotlin toList() as that is not safe to use on concurrent collections. + val allOpen = ArrayList(liveTransactions.values) if (allOpen.isEmpty()) { callback() } else { From 45c56b68fb48c5096442b9be835d598be7ee191f Mon Sep 17 00:00:00 2001 From: Dominic Fox Date: Thu, 28 Feb 2019 13:57:56 +0000 Subject: [PATCH 043/159] ENT-3166 don't depend on cause for message --- .ci/api-current.txt | 4 ++-- .../core/contracts/TransactionVerificationException.kt | 10 ++++++---- ...ansactionVerificationExceptionSerialisationTests.kt | 7 ++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 87186bf097..d717b5a464 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -1039,13 +1039,13 @@ public static final class net.corda.core.contracts.TransactionVerificationExcept ## @CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$ContractCreationError extends net.corda.core.contracts.TransactionVerificationException - public (net.corda.core.crypto.SecureHash, String, Throwable) + public (net.corda.core.crypto.SecureHash, String, Throwable, String) @NotNull public final String getContractClass() ## @CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$ContractRejection extends net.corda.core.contracts.TransactionVerificationException - public (net.corda.core.crypto.SecureHash, String, Throwable) + public (net.corda.core.crypto.SecureHash, String, Throwable, String) public (net.corda.core.crypto.SecureHash, net.corda.core.contracts.Contract, Throwable) @NotNull public final String getContractClass() diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt index 3c80993d8d..72567d72e5 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt @@ -57,8 +57,8 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S * @property contractClass The fully qualified class name of the failing contract. */ @KeepForDJVM - class ContractRejection(txId: SecureHash, val contractClass: String, cause: Throwable) : TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, contract: $contractClass", cause) { - constructor(txId: SecureHash, contract: Contract, cause: Throwable) : this(txId, contract.javaClass.name, cause) + class ContractRejection internal constructor(txId: SecureHash, val contractClass: String, cause: Throwable?, message: String) : TransactionVerificationException(txId, "Contract verification failed: $message, contract: $contractClass", cause) { + internal constructor(txId: SecureHash, contract: Contract, cause: Throwable) : this(txId, contract.javaClass.name, cause, cause.message ?: "") } /** @@ -121,8 +121,10 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S * @property contractClass The fully qualified class name of the failing contract. */ @KeepForDJVM - class ContractCreationError(txId: SecureHash, val contractClass: String, cause: Throwable) - : TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, could not create contract class: $contractClass", cause) + class ContractCreationError internal constructor(txId: SecureHash, val contractClass: String, cause: Throwable?, message: String) + : TransactionVerificationException(txId, "Contract verification failed: $message, could not create contract class: $contractClass", cause) { + internal constructor(txId: SecureHash, contractClass: String, cause: Throwable) : this(txId, contractClass, cause, cause.message ?: "") + } /** * An output state has a notary that doesn't match the transaction's notary field. It must! diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt b/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt index 6ee97c6031..6c10861468 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt @@ -8,6 +8,7 @@ import net.corda.serialization.internal.amqp.DeserializationInput import net.corda.serialization.internal.amqp.SerializationOutput import net.corda.serialization.internal.amqp.SerializerFactoryBuilder import net.corda.serialization.internal.amqp.custom.PublicKeySerializer +import net.corda.serialization.internal.amqp.custom.ThrowableSerializer import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.TestIdentity @@ -18,7 +19,7 @@ class TransactionVerificationExceptionSerialisationTests { private fun defaultFactory() = SerializerFactoryBuilder.build( AllWhitelist, ClassLoader.getSystemClassLoader() - ) + ).apply { register(ThrowableSerializer(this)) } private val context get() = AMQP_RPC_CLIENT_CONTEXT @@ -52,7 +53,7 @@ class TransactionVerificationExceptionSerialisationTests { context) assertEquals(exception.message, exception2.message) - assertEquals(exception.cause?.message, exception2.cause?.message) + assertEquals("java.lang.Throwable: ${exception.cause?.message}", exception2.cause?.message) assertEquals(exception.txId, exception2.txId) } @@ -89,7 +90,7 @@ class TransactionVerificationExceptionSerialisationTests { context) assertEquals(exception.message, exception2.message) - assertEquals(exception.cause?.message, exception2.cause?.message) + assertEquals("java.lang.Throwable: ${exception.cause?.message}", exception2.cause?.message) assertEquals(exception.txId, exception2.txId) } From e7534b3a7d6159157cae5f5968efab92cf110a6f Mon Sep 17 00:00:00 2001 From: josecoll Date: Fri, 1 Mar 2019 16:50:41 +0000 Subject: [PATCH 044/159] Fix broken URL. (#4835) --- docs/source/corda-network/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/corda-network/index.md b/docs/source/corda-network/index.md index e85af547d5..cf092645e7 100644 --- a/docs/source/corda-network/index.md +++ b/docs/source/corda-network/index.md @@ -24,7 +24,7 @@ The main benefit of Corda Network for participants is being able to move cash, d or line of business to another. Business network operators also benefit by being able to access network-wide services, and reuse the [trust root](https://corda.network/trust-root/index.html) and network services, instead of building and managing their own. -The Corda Network website has a [high level overview](https://corda.network/participation/implementation-steps.html) of the joining process. +The Corda Network website has a [high level overview](https://corda.network/participation/index.html) of the joining process. Key services ============ From a18e44ed8f180d7d38fb8c919efc127b8542f292 Mon Sep 17 00:00:00 2001 From: James Brown Date: Fri, 1 Mar 2019 10:31:09 +0000 Subject: [PATCH 045/159] ENT-3025 Thread-safe liquibase migrations --- .../internal/persistence/SchemaMigration.kt | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt index ea9aec6300..a867ed43cf 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt @@ -19,6 +19,8 @@ import java.io.InputStream import java.nio.file.Path import java.sql.Statement import javax.sql.DataSource +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock // Migrate the database to the current version, using liquibase. // @@ -38,6 +40,7 @@ class SchemaMigration( const val NODE_BASE_DIR_KEY = "liquibase.nodeDaseDir" const val NODE_X500_NAME = "liquibase.nodeName" val loader = ThreadLocal() + private val mutex = ReentrantLock() } init { @@ -113,16 +116,21 @@ class SchemaMigration( System.setProperty(NODE_X500_NAME, ourName.toString()) val customResourceAccessor = CustomResourceAccessor(dynamicInclude, changelogList, classLoader) - val liquibase = Liquibase(dynamicInclude, customResourceAccessor, getLiquibaseDatabase(JdbcConnection(connection))) + // current version of Liquibase appears to be non-threadsafe + // this is apparent when multiple in-process nodes are all running migrations simultaneously + mutex.withLock { + val liquibase = Liquibase(dynamicInclude, customResourceAccessor, getLiquibaseDatabase(JdbcConnection(connection))) - val unRunChanges = liquibase.listUnrunChangeSets(Contexts(), LabelExpression()) + val unRunChanges = liquibase.listUnrunChangeSets(Contexts(), LabelExpression()) - when { - (run && !check) && (unRunChanges.isNotEmpty() && existingCheckpoints!!) -> throw CheckpointsException() // Do not allow database migration when there are checkpoints - run && !check -> liquibase.update(Contexts()) - check && !run && unRunChanges.isNotEmpty() -> throw OutstandingDatabaseChangesException(unRunChanges.size) - check && !run -> {} // Do nothing will be interpreted as "check succeeded" - else -> throw IllegalStateException("Invalid usage.") + when { + (run && !check) && (unRunChanges.isNotEmpty() && existingCheckpoints!!) -> throw CheckpointsException() // Do not allow database migration when there are checkpoints + run && !check -> liquibase.update(Contexts()) + check && !run && unRunChanges.isNotEmpty() -> throw OutstandingDatabaseChangesException(unRunChanges.size) + check && !run -> { + } // Do nothing will be interpreted as "check succeeded" + else -> throw IllegalStateException("Invalid usage.") + } } } } From 987cb23c2a3533c0112ee15225f7bab985329527 Mon Sep 17 00:00:00 2001 From: Richard G Brown Date: Sat, 2 Mar 2019 15:53:09 +0000 Subject: [PATCH 046/159] Clarify that bootstrapper only works in devMode --- docs/source/network-bootstrapper.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/network-bootstrapper.rst b/docs/source/network-bootstrapper.rst index 63fb3f7c95..1fe3ea13e8 100644 --- a/docs/source/network-bootstrapper.rst +++ b/docs/source/network-bootstrapper.rst @@ -26,7 +26,7 @@ Bootstrapping a test network The Corda Network Bootstrapper can be downloaded from `here `_. -Create a directory containing a node config file, ending in "_node.conf", for each node you want to create. Then run the +Create a directory containing a node config file, ending in "_node.conf", for each node you want to create. "devMode" must be set to true. Then run the following command: ``java -jar network-bootstrapper-VERSION.jar --dir `` From a000ac3dcc35f1d91bf119137b295a068436010f Mon Sep 17 00:00:00 2001 From: Katarzyna Streich Date: Tue, 26 Feb 2019 11:09:17 +0000 Subject: [PATCH 047/159] ENT-3075 Java versions with -ea at the end are valid too (#1853) --- .../main/kotlin/net/corda/core/internal/CordaUtils.kt | 3 +++ node/src/main/kotlin/net/corda/node/internal/Node.kt | 3 ++- .../test/kotlin/net/corda/node/internal/NodeTest.kt | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt index 9515c3c17d..2291f5e6b9 100644 --- a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt @@ -41,6 +41,9 @@ fun checkMinimumPlatformVersion(minimumPlatformVersion: Int, requiredMinPlatform } } +@Throws(NumberFormatException::class) +fun getJavaUpdateVersion(javaVersion: String): Long = javaVersion.substringAfter("_").substringBefore("-").toLong() + /** Provide access to internal method for AttachmentClassLoaderTests. */ @DeleteForDJVM fun TransactionBuilder.toWireTransaction(services: ServicesForResolution, serializationContext: SerializationContext): WireTransaction { diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 63330980e3..fa550bd233 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -17,6 +17,7 @@ import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.thenMatch import net.corda.core.internal.div import net.corda.core.internal.errors.AddressBindingException +import net.corda.core.internal.getJavaUpdateVersion import net.corda.core.internal.notary.NotaryService import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.RPCOps @@ -157,7 +158,7 @@ open class Node(configuration: NodeConfiguration, // when the ext.java8_minUpdateVersion gradle constant changes, so must this check val major = SystemUtils.JAVA_VERSION_FLOAT return try { - val update = SystemUtils.JAVA_VERSION.substringAfter("_").toLong() + val update = getJavaUpdateVersion(SystemUtils.JAVA_VERSION) // To filter out cases like 1.8.0_202-ea major == 1.8F && update >= 171 } catch (e: NumberFormatException) { // custom JDKs may not have the update version (e.g. 1.8.0-adoptopenjdk) false diff --git a/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt b/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt index fd2e1db83f..6f74525a70 100644 --- a/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt @@ -4,6 +4,7 @@ import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever import net.corda.core.identity.CordaX500Name import net.corda.core.internal.delete +import net.corda.core.internal.getJavaUpdateVersion import net.corda.core.internal.list import net.corda.core.internal.readObject import net.corda.core.node.NodeInfo @@ -29,6 +30,7 @@ import org.junit.rules.TemporaryFolder import java.nio.file.Path import java.time.Duration import kotlin.test.assertEquals +import kotlin.test.assertFailsWith import kotlin.test.assertNull class NodeTest { @@ -141,6 +143,14 @@ class NodeTest { } } + @Test + fun `test getJavaUpdateVersion`() { + assertThat(getJavaUpdateVersion("1.8.0_202-ea")).isEqualTo(202) + assertThat(getJavaUpdateVersion("1.8.0_202")).isEqualTo(202) + assertFailsWith { getJavaUpdateVersion("1.8.0_202wrong-format") } + assertFailsWith { getJavaUpdateVersion("1.8.0-adoptopenjdk") } + } + private fun getAllInfos(database: CordaPersistence): List { return database.transaction { val criteria = session.criteriaBuilder.createQuery(NodeInfoSchemaV1.PersistentNodeInfo::class.java) From fa2cd907c5fbc31fcfcf76e264c353ba1bafdb91 Mon Sep 17 00:00:00 2001 From: Katarzyna Streich Date: Wed, 27 Feb 2019 14:42:31 +0000 Subject: [PATCH 048/159] Add more readable error message. (#1855) ENT-3136 Add more readable error message. --- node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 15ded70296..e4c31906b9 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -36,6 +36,7 @@ import java.io.IOException import java.io.RandomAccessFile import java.lang.management.ManagementFactory import java.net.InetAddress +import java.nio.channels.UnresolvedAddressException import java.nio.file.Path import java.time.DayOfWeek import java.time.ZonedDateTime @@ -428,6 +429,8 @@ interface NodeStartupLogging { error.isExpectedWhenStartingNode() -> error.logAsExpected() error is CouldNotCreateDataSourceException -> error.logAsUnexpected() error is Errors.NativeIoException && error.message?.contains("Address already in use") == true -> error.logAsExpected("One of the ports required by the Corda node is already in use.") + error is Errors.NativeIoException && error.message?.contains("Can't assign requested address") == true -> error.logAsExpected("Exception during node startup. Check that addresses in node config resolve correctly.") + error is UnresolvedAddressException -> error.logAsExpected("Exception during node startup. Check that addresses in node config resolve correctly.") error.isOpenJdkKnownIssue() -> error.logAsExpected("Exception during node startup - ${error.message}. This is a known OpenJDK issue on some Linux distributions, please use OpenJDK from zulu.org or Oracle JDK.") else -> error.logAsUnexpected("Exception during node startup") } From 8306b3f7082e304ddcd5881118d73cb465ca1d46 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Wed, 27 Feb 2019 14:41:09 +0000 Subject: [PATCH 049/159] CORDA-2676: Allow more Network Bootstrapper code to be unloaded from JVM. --- .../internal/network/NetworkBootstrapper.kt | 67 ++++++++++--------- .../network/NetworkBootstrapperTest.kt | 51 ++++++++------ 2 files changed, 67 insertions(+), 51 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt index 53bdd1cae7..de95fcea8b 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt @@ -29,7 +29,7 @@ import net.corda.serialization.internal.SerializationFactoryImpl import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme import net.corda.serialization.internal.amqp.amqpMagic import java.io.File -import java.io.InputStream +import java.net.URL import java.nio.file.FileAlreadyExistsException import java.nio.file.Path import java.nio.file.StandardCopyOption.REPLACE_EXISTING @@ -53,14 +53,14 @@ import kotlin.streams.toList class NetworkBootstrapper @VisibleForTesting internal constructor(private val initSerEnv: Boolean, - private val embeddedCordaJar: () -> InputStream, + private val embeddedCordaJar: () -> URL, private val nodeInfosGenerator: (List) -> List, private val contractsJarConverter: (Path) -> ContractsJar) : NetworkBootstrapperWithOverridableParameters { constructor() : this( initSerEnv = true, - embeddedCordaJar = Companion::extractEmbeddedCordaJar, - nodeInfosGenerator = Companion::generateNodeInfos, + embeddedCordaJar = ::extractEmbeddedCordaJar, + nodeInfosGenerator = ::generateNodeInfos, contractsJarConverter = ::ContractsJarFile ) @@ -77,8 +77,8 @@ internal constructor(private val initSerEnv: Boolean, private val jarsThatArentCordapps = setOf("corda.jar", "runnodes.jar") - private fun extractEmbeddedCordaJar(): InputStream { - return Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar") + private fun extractEmbeddedCordaJar(): URL { + return Thread.currentThread().contextClassLoader.getResource("corda.jar") } private fun generateNodeInfos(nodeDirs: List): List { @@ -106,13 +106,19 @@ internal constructor(private val initSerEnv: Boolean, .redirectOutput(nodeInfoGenFile) .apply { environment()["CAPSULE_CACHE_DIR"] = "../.cache" } .start() - if (!process.waitFor(3, TimeUnit.MINUTES)) { + try { + if (!process.waitFor(3, TimeUnit.MINUTES)) { + process.destroyForcibly() + printNodeInfoGenLogToConsole(nodeInfoGenFile) + } + printNodeInfoGenLogToConsole(nodeInfoGenFile) { process.exitValue() == 0 } + return nodeDir.list { paths -> + paths.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get() + } + } catch (e: InterruptedException) { + // Don't leave this process dangling if the thread is interrupted. process.destroyForcibly() - printNodeInfoGenLogToConsole(nodeInfoGenFile) - } - printNodeInfoGenLogToConsole(nodeInfoGenFile) { process.exitValue() == 0 } - return nodeDir.list { paths -> - paths.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get() + throw e } } @@ -257,19 +263,23 @@ internal constructor(private val initSerEnv: Boolean, } } + private fun Path.listEndingWith(suffix: String): List { + return list { file -> file.filter { it.toString().endsWith(suffix) }.toList() } + } + private fun createNodeDirectoriesIfNeeded(directory: Path, fromCordform: Boolean): Boolean { var networkAlreadyExists = false val cordaJar = directory / "corda.jar" var usingEmbedded = false if (!cordaJar.exists()) { - embeddedCordaJar().use { it.copyTo(cordaJar) } + embeddedCordaJar().openStream().use { it.copyTo(cordaJar) } usingEmbedded = true } else if (!fromCordform) { println("Using corda.jar in root directory") } - val confFiles = directory.list { it.filter { it.toString().endsWith("_node.conf") }.toList() } - val webServerConfFiles = directory.list { it.filter { it.toString().endsWith("_web-server.conf") }.toList() } + val confFiles = directory.listEndingWith("_node.conf") + val webServerConfFiles = directory.listEndingWith("_web-server.conf") for (confFile in confFiles) { val nodeName = confFile.fileName.toString().removeSuffix("_node.conf") @@ -285,11 +295,10 @@ internal constructor(private val initSerEnv: Boolean, cordaJar.copyToDirectory(nodeDir, REPLACE_EXISTING) } - directory.list { paths -> - paths.filter { (it / "node.conf").exists() && !(it / "corda.jar").exists() }.forEach { - println("Copying corda.jar into node directory ${it.fileName}") - cordaJar.copyToDirectory(it) - } + val nodeDirs = directory.list { subDir -> subDir.filter { (it / "node.conf").exists() && !(it / "corda.jar").exists() }.toList() } + for (nodeDir in nodeDirs) { + println("Copying corda.jar into node directory ${nodeDir.fileName}") + cordaJar.copyToDirectory(nodeDir) } if (fromCordform) { @@ -304,15 +313,11 @@ internal constructor(private val initSerEnv: Boolean, } private fun gatherNodeDirectories(directory: Path): List { - return directory.list { paths -> - paths.filter { - val exists = (it / "corda.jar").exists() - if (exists) { - require((it / "node.conf").exists()) { "Missing node.conf in node directory ${it.fileName}" } - } - exists - }.toList() + val nodeDirs = directory.list { subDir -> subDir.filter { (it / "corda.jar").exists() }.toList() } + for (nodeDir in nodeDirs) { + require((nodeDir / "node.conf").exists()) { "Missing node.conf in node directory ${nodeDir.fileName}" } } + return nodeDirs } private fun distributeNodeInfos(nodeDirs: List, nodeInfoFiles: List) { @@ -434,13 +439,13 @@ internal constructor(private val initSerEnv: Boolean, private fun initialiseSerialization() { _contextSerializationEnv.set(SerializationEnvironment.with( SerializationFactoryImpl().apply { - registerScheme(AMQPParametersSerializationScheme) + registerScheme(AMQPParametersSerializationScheme()) }, AMQP_P2P_CONTEXT) ) } - private object AMQPParametersSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) { + private class AMQPParametersSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) { override fun rpcClientSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException() override fun rpcServerSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException() @@ -526,7 +531,7 @@ enum class CopyCordapps { fun copy(cordappJars: List, nodeDirs: List, networkAlreadyExists: Boolean, fromCordform: Boolean) { if (!fromCordform) { - println("Found the following CorDapps: ${cordappJars.map { it.fileName }}") + println("Found the following CorDapps: ${cordappJars.map(Path::getFileName)}") } this.copyTo(cordappJars, nodeDirs, networkAlreadyExists, fromCordform) } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt index b02b0f3b7d..f1f8145853 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt @@ -23,10 +23,12 @@ import net.corda.testing.internal.createNodeInfoAndSigned import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After +import org.junit.AfterClass import org.junit.Rule import org.junit.Test import org.junit.rules.ExpectedException import org.junit.rules.TemporaryFolder +import java.nio.file.Files import java.nio.file.Path import java.security.PublicKey import java.time.Duration @@ -45,13 +47,28 @@ class NetworkBootstrapperTest { @JvmField val testSerialization = SerializationEnvironmentRule() - private val fakeEmbeddedCordaJar = fakeFileBytes() + companion object { + private val fakeEmbeddedCorda = fakeFileBytes() + private val fakeEmbeddedCordaJar = Files.createTempFile("corda", ".jar").write(fakeEmbeddedCorda) - private val contractsJars = HashMap() + private fun fakeFileBytes(writeToFile: Path? = null): ByteArray { + val bytes = secureRandomBytes(128) + writeToFile?.write(bytes) + return bytes + } + + @JvmStatic + @AfterClass + fun cleanUp() { + Files.delete(fakeEmbeddedCordaJar) + } + } + + private val contractsJars = hashMapOf() private val bootstrapper = NetworkBootstrapper( initSerEnv = false, - embeddedCordaJar = fakeEmbeddedCordaJar::inputStream, + embeddedCordaJar = { fakeEmbeddedCordaJar.toUri().toURL() }, nodeInfosGenerator = { nodeDirs -> nodeDirs.map { nodeDir -> val name = nodeDir.fakeNodeConfig.myLegalName @@ -101,7 +118,7 @@ class NetworkBootstrapperTest { fun `single node conf file`() { createNodeConfFile("node1", bobConfig) bootstrap() - val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "node1" to bobConfig) + val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "node1" to bobConfig) networkParameters.run { assertThat(epoch).isEqualTo(1) assertThat(notaries).isEmpty() @@ -121,7 +138,7 @@ class NetworkBootstrapperTest { fun `single node directory with just node conf file`() { createNodeDir("bob", bobConfig) bootstrap() - assertBootstrappedNetwork(fakeEmbeddedCordaJar, "bob" to bobConfig) + assertBootstrappedNetwork(fakeEmbeddedCorda, "bob" to bobConfig) } @Test @@ -147,7 +164,7 @@ class NetworkBootstrapperTest { createNodeConfFile("alice", aliceConfig) createNodeConfFile("notary", notaryConfig) bootstrap() - val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig, "notary" to notaryConfig) + val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig, "notary" to notaryConfig) networkParameters.assertContainsNotary("notary") } @@ -165,7 +182,7 @@ class NetworkBootstrapperTest { createNodeConfFile("alice", aliceConfig) createNodeDir("bob", bobConfig) bootstrap() - assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig, "bob" to bobConfig) + assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig, "bob" to bobConfig) } @Test @@ -173,7 +190,7 @@ class NetworkBootstrapperTest { createNodeConfFile("alice", aliceConfig) val cordappBytes = createFakeCordappJar("sample-app", listOf("contract.class")) bootstrap() - val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig) + val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig) assertThat(rootDir / "alice" / "cordapps" / "sample-app.jar").hasBinaryContent(cordappBytes) assertThat(networkParameters.whitelistedContractImplementations).isEqualTo(mapOf( "contract.class" to listOf(cordappBytes.sha256()) @@ -185,7 +202,7 @@ class NetworkBootstrapperTest { createNodeConfFile("alice", aliceConfig) val cordappBytes = createFakeCordappJar("sample-app", listOf("contract.class")) bootstrap(copyCordapps = CopyCordapps.No) - val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig) + val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig) assertThat(rootDir / "alice" / "cordapps" / "sample-app.jar").doesNotExist() assertThat(networkParameters.whitelistedContractImplementations).isEqualTo(mapOf( "contract.class" to listOf(cordappBytes.sha256()) @@ -199,7 +216,7 @@ class NetworkBootstrapperTest { val networkParameters1 = (rootDir / "alice").networkParameters createNodeConfFile("bob", bobConfig) bootstrap() - val networkParameters2 = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig, "bob" to bobConfig) + val networkParameters2 = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig, "bob" to bobConfig) assertThat(networkParameters1).isEqualTo(networkParameters2) } @@ -209,7 +226,7 @@ class NetworkBootstrapperTest { bootstrap() createNodeConfFile("notary", notaryConfig) bootstrap() - val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig, "notary" to notaryConfig) + val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig, "notary" to notaryConfig) networkParameters.assertContainsNotary("notary") assertThat(networkParameters.epoch).isEqualTo(2) } @@ -225,7 +242,7 @@ class NetworkBootstrapperTest { maxMessageSize = maxMessageSize, maxTransactionSize = maxTransactionSize, eventHorizon = eventHorizon) - val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig) + val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig) assertThat(networkParameters.minimumPlatformVersion).isEqualTo(minimumPlatformVersion) assertThat(networkParameters.maxMessageSize).isEqualTo(maxMessageSize) assertThat(networkParameters.maxTransactionSize).isEqualTo(maxTransactionSize) @@ -299,12 +316,6 @@ class NetworkBootstrapperTest { private val rootDir get() = tempFolder.root.toPath() - private fun fakeFileBytes(writeToFile: Path? = null): ByteArray { - val bytes = secureRandomBytes(128) - writeToFile?.write(bytes) - return bytes - } - private fun bootstrap(copyCordapps: CopyCordapps = CopyCordapps.FirstRunOnly, packageOwnership: Map? = emptyMap(), minimumPlatformVerison: Int? = PLATFORM_VERSION, @@ -317,7 +328,7 @@ class NetworkBootstrapperTest { maxMessageSize = maxMessageSize, maxTransactionSize = maxTransactionSize, eventHorizon = eventHorizon, - packageOwnership = packageOwnership?.map { PackageOwner(it.key, it.value!!) } + packageOwnership = packageOwnership?.map { PackageOwner(it.key, it.value) } )) } @@ -364,7 +375,7 @@ class NetworkBootstrapperTest { private fun assertBootstrappedNetwork(cordaJar: ByteArray, vararg nodes: Pair): NetworkParameters { val networkParameters = (rootDir / nodes[0].first).networkParameters - val allNodeInfoFiles = nodes.map { (rootDir / it.first).nodeInfoFile }.associateBy({ it }, { it.readAll() }) + val allNodeInfoFiles = nodes.map { (rootDir / it.first).nodeInfoFile }.associateBy({ it }, Path::readAll) for ((nodeDirName, config) in nodes) { val nodeDir = rootDir / nodeDirName From dce411cfdf32ec572a59e0fd9afba1f5b1c503b5 Mon Sep 17 00:00:00 2001 From: RogerWillis Date: Wed, 20 Feb 2019 15:39:44 +0000 Subject: [PATCH 050/159] Removed part about exchanging transaction builder between parties. You can't do this as TransactionBuilder is not serializable. --- docs/source/tutorial-building-transactions.rst | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/source/tutorial-building-transactions.rst b/docs/source/tutorial-building-transactions.rst index 32be2f3758..61bc5beb90 100644 --- a/docs/source/tutorial-building-transactions.rst +++ b/docs/source/tutorial-building-transactions.rst @@ -48,11 +48,7 @@ Transactions in Corda contain a number of elements: period during which the proposed transaction can be committed to the ledger -A transaction is built by populating a ``TransactionBuilder``. Typically, -the ``TransactionBuilder`` will need to be exchanged back and forth between -parties before it is fully populated. This is an immediate consequence of -the Corda privacy model, in which the input states are likely to be unknown -to the other node. +A transaction is built by populating a ``TransactionBuilder``. Once the builder is fully populated, the flow should freeze the ``TransactionBuilder`` by signing it to create a ``SignedTransaction``. This is key to the ledger agreement process - once a flow has attached a node’s signature to a From 5a4db990e72bc330ddfb9aa64f090f7ee52841b2 Mon Sep 17 00:00:00 2001 From: Roger Willis Date: Wed, 20 Feb 2019 17:05:36 +0000 Subject: [PATCH 051/159] Update tutorial-building-transactions.rst Addressed @tudor-malene's comment --- docs/source/tutorial-building-transactions.rst | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/source/tutorial-building-transactions.rst b/docs/source/tutorial-building-transactions.rst index 61bc5beb90..e645ed3ce9 100644 --- a/docs/source/tutorial-building-transactions.rst +++ b/docs/source/tutorial-building-transactions.rst @@ -48,11 +48,7 @@ Transactions in Corda contain a number of elements: period during which the proposed transaction can be committed to the ledger -A transaction is built by populating a ``TransactionBuilder``. - -Once the builder is fully populated, the flow should freeze the ``TransactionBuilder`` by signing it to create a -``SignedTransaction``. This is key to the ledger agreement process - once a flow has attached a node’s signature to a -transaction, it has effectively stated that it accepts all the details of the transaction. +A transaction is built by populating a ``TransactionBuilder``. Once the builder is fully populated, the flow should freeze the ``TransactionBuilder`` by signing it to create a ``SignedTransaction``. This is key to the ledger agreement process - once a flow has attached a node’s signature to a transaction, it has effectively stated that it accepts all the details of the transaction. It is best practice for flows to receive back the ``TransactionSignature`` of other parties rather than a full ``SignedTransaction`` objects, because otherwise we have to separately check that this is still the same @@ -295,4 +291,4 @@ overall transaction id is still provable from the not expose that data to the other node directly. A full example of this can be found in the ``NodeInterestRates`` Oracle code from the ``irs-demo`` project which interacts with the ``RatesFixFlow`` flow. -Also, refer to the :doc:`tutorial-tear-offs`. \ No newline at end of file +Also, refer to the :doc:`tutorial-tear-offs`. From 9da30b431f62b15702ae6c2c8b37d14867b2e64f Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Mon, 4 Mar 2019 11:01:08 +0000 Subject: [PATCH 052/159] CORDA-2554 - Bootstrapper - option to include contracts to whitelist from signed JARs (#4712) * NetworkBoostrapper can optionally whitelist contracts from signed jars based on include_whitelist.txt file. * refactoring, docs * logs * add ne parameters to the generateWhitelist method at the end * Addressing review comments. * CORDA-2577 disable non-downgrade rule - test fix and docs --- docs/source/network-bootstrapper.rst | 3 +- .../internal/network/NetworkBootstrapper.kt | 6 ++-- .../internal/network/WhitelistGenerator.kt | 33 ++++++++++++------- .../network/WhitelistGeneratorTest.kt | 4 ++- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/docs/source/network-bootstrapper.rst b/docs/source/network-bootstrapper.rst index 1fe3ea13e8..451d1af768 100644 --- a/docs/source/network-bootstrapper.rst +++ b/docs/source/network-bootstrapper.rst @@ -91,7 +91,8 @@ By default the Bootstrapper will whitelist all the contracts found in the unsign Whitelisted contracts are checked by `Zone constraints`, while contract classes from signed JARs will be checked by `Signature constraints`. To prevent certain contracts from unsigned JARs from being whitelisted, add their fully qualified class name in the ``exclude_whitelist.txt``. These will instead use the more restrictive ``HashAttachmentConstraint``. -Refer to :doc:`api-contract-constraints` to understand the implication of different constraint types before adding ``exclude_whitelist.txt`` files. +To add certain contracts from signed JARs to whitelist, add their fully qualified class name in the ``include_whitelist.txt``. +Refer to :doc:`api-contract-constraints` to understand the implication of different constraint types before adding ``exclude_whitelist.txt`` or ``include_whitelist.txt`` files. For example: diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt index de95fcea8b..309ad7cae0 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt @@ -247,8 +247,10 @@ internal constructor(private val initSerEnv: Boolean, println("Gathering notary identities") val notaryInfos = gatherNotaryInfos(nodeInfoFiles, configs) println("Generating contract implementations whitelist") - // Only add contracts to the whitelist from unsigned jars - val newWhitelist = generateWhitelist(existingNetParams, readExcludeWhitelist(directory), cordappJars.filter { !isSigned(it) }.map(contractsJarConverter)) + val signedJars = cordappJars.filter { isSigned(it) } // signed JARs are excluded by default, optionally include them in order to transition states from CZ whitelist to signature constraint + val unsignedJars = cordappJars - signedJars + val newWhitelist = generateWhitelist(existingNetParams, readExcludeWhitelist(directory), unsignedJars.map(contractsJarConverter), + readIncludeWhitelist(directory), signedJars.map(contractsJarConverter)) val newNetParams = installNetworkParameters(notaryInfos, newWhitelist, existingNetParams, nodeDirs, networkParametersOverrides) if (newNetParams != existingNetParams) { println("${if (existingNetParams == null) "New" else "Updated"} $newNetParams") diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/WhitelistGenerator.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/WhitelistGenerator.kt index 9979337386..ef76b11d52 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/WhitelistGenerator.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/WhitelistGenerator.kt @@ -1,10 +1,7 @@ package net.corda.nodeapi.internal.network import net.corda.core.contracts.ContractClassName -import net.corda.core.internal.div -import net.corda.core.internal.exists -import net.corda.core.internal.readAllLines -import net.corda.core.internal.toMultiMap +import net.corda.core.internal.* import net.corda.core.node.NetworkParameters import net.corda.core.node.services.AttachmentId import net.corda.nodeapi.internal.ContractsJar @@ -12,15 +9,18 @@ import org.slf4j.LoggerFactory import java.nio.file.Path private const val EXCLUDE_WHITELIST_FILE_NAME = "exclude_whitelist.txt" +private const val INCLUDE_WHITELIST_FILE_NAME = "include_whitelist.txt" private val logger = LoggerFactory.getLogger("net.corda.nodeapi.internal.network.WhitelistGenerator") fun generateWhitelist(networkParameters: NetworkParameters?, excludeContracts: List, - cordappJars: List): Map> { + cordappJars: List, + includeContracts: List, + optionalCordappJars: List): Map> { val existingWhitelist = networkParameters?.whitelistedContractImplementations ?: emptyMap() if (excludeContracts.isNotEmpty()) { - logger.info("Exclude contracts from whitelist: ${excludeContracts.joinToString()}") + logger.info("Exclude contracts from $EXCLUDE_WHITELIST_FILE_NAME: ${excludeContracts.joinToString()}") existingWhitelist.keys.forEach { require(it !in excludeContracts) { "$it is already part of the existing whitelist and cannot be excluded." } } @@ -30,14 +30,23 @@ fun generateWhitelist(networkParameters: NetworkParameters?, .flatMap { jar -> (jar.scan() - excludeContracts).map { it to jar.hash } } .toMultiMap() - return (newWhiteList.keys + existingWhitelist.keys).associateBy({ it }) { + if (includeContracts.isNotEmpty()) + logger.info("Include contracts from $INCLUDE_WHITELIST_FILE_NAME: ${includeContracts.joinToString()} present in JARs: $optionalCordappJars.") + + val newSignedJarsWhiteList = optionalCordappJars + .flatMap { jar -> (jar.scan()).filter { includeContracts.contains(it) }.map { it to jar.hash } } + .toMultiMap() + + return (newWhiteList.keys + existingWhitelist.keys + newSignedJarsWhiteList.keys).associateBy({ it }) { val existingHashes = existingWhitelist[it] ?: emptyList() val newHashes = newWhiteList[it] ?: emptyList() - (existingHashes + newHashes).distinct() + val newHashesFormSignedJar = newSignedJarsWhiteList[it] ?: emptyList() + (existingHashes + newHashes + newHashesFormSignedJar).distinct() } } -fun readExcludeWhitelist(directory: Path): List { - val file = directory / EXCLUDE_WHITELIST_FILE_NAME - return if (file.exists()) file.readAllLines().map(String::trim) else emptyList() -} \ No newline at end of file +fun readExcludeWhitelist(directory: Path): List = readAllLines(directory / EXCLUDE_WHITELIST_FILE_NAME) + +fun readIncludeWhitelist(directory: Path): List = readAllLines(directory / INCLUDE_WHITELIST_FILE_NAME) + +private fun readAllLines(path: Path) : List = if (path.exists()) path.readAllLines().map(String::trim) else emptyList() diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/WhitelistGeneratorTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/WhitelistGeneratorTest.kt index 4bf6960c85..2bfb9c68b4 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/WhitelistGeneratorTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/WhitelistGeneratorTest.kt @@ -129,7 +129,9 @@ class WhitelistGeneratorTest { return generateWhitelist( testNetworkParameters(whitelistedContractImplementations = existingWhitelist), excludeContracts, - contractJars + contractJars, + emptyList(), + emptyList() ) } } From e3c0b6e7dff113275cd3ba40a8174926dd8830e4 Mon Sep 17 00:00:00 2001 From: Jonathan Locke Date: Mon, 4 Mar 2019 10:20:57 +0000 Subject: [PATCH 053/159] CORDA-2690: Reduce CordApp scanning logging Only log a line about looking for notary implementations if at least notary implementation is found. It might be unnecessary and confusing to log when no implementations are found. Replaced TrustedAuthorityNotaryService with SinglePartyNotaryService in comment --- .../corda/node/internal/cordapp/JarScanningCordappLoader.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt index f3b122f615..7a92d6184d 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt @@ -212,12 +212,14 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: } private fun findNotaryService(scanResult: RestrictedScanResult): Class? { - // Note: we search for implementations of both NotaryService and TrustedAuthorityNotaryService as + // Note: we search for implementations of both NotaryService and SinglePartyNotaryService as // the scanner won't find subclasses deeper down the hierarchy if any intermediate class is not // present in the CorDapp. val result = scanResult.getClassesWithSuperclass(NotaryService::class) + scanResult.getClassesWithSuperclass(SinglePartyNotaryService::class) - logger.info("Found notary service CorDapp implementations: " + result.joinToString(", ")) + if(!result.isEmpty()) { + logger.info("Found notary service CorDapp implementations: " + result.joinToString(", ")) + } return result.firstOrNull() } From b2fc6e943765f377863677f10820fc6e80377c36 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 4 Mar 2019 12:17:05 +0000 Subject: [PATCH 054/159] CORDA-2697: Upgrade to Corda Gradle plugins 4.0.41. (#4846) --- constants.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants.properties b/constants.properties index 14f4bd71ed..cb8ba495c4 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=4.0.39 +gradlePluginsVersion=4.0.41 kotlinVersion=1.2.71 # ***************************************************************# # When incrementing platformVersion make sure to update # From 0984e87c5ee9a0fb62f9a956afd2c7ca26233344 Mon Sep 17 00:00:00 2001 From: Dominic Fox <40790090+r3domfox@users.noreply.github.com> Date: Mon, 4 Mar 2019 13:45:14 +0000 Subject: [PATCH 055/159] CORDA-1947 - Regenerate test data and unignore test (#4838) * CORDA-1947 Regenerate test data and unignore test * Formatting --- .../internal/amqp/EnumEvolveTests.kt | 9 +++------ .../amqp/EnumEvolveTests.multiOperations.1.A | Bin 760 -> 728 bytes .../amqp/EnumEvolveTests.multiOperations.1.B | Bin 760 -> 728 bytes .../amqp/EnumEvolveTests.multiOperations.1.C | Bin 880 -> 728 bytes .../amqp/EnumEvolveTests.multiOperations.1.D | Bin 760 -> 728 bytes .../amqp/EnumEvolveTests.multiOperations.2.A | Bin 811 -> 779 bytes .../amqp/EnumEvolveTests.multiOperations.2.B | Bin 811 -> 779 bytes .../amqp/EnumEvolveTests.multiOperations.2.C | Bin 951 -> 779 bytes .../amqp/EnumEvolveTests.multiOperations.2.D | Bin 811 -> 779 bytes .../amqp/EnumEvolveTests.multiOperations.2.E | Bin 811 -> 779 bytes .../amqp/EnumEvolveTests.multiOperations.3.A | Bin 857 -> 825 bytes .../amqp/EnumEvolveTests.multiOperations.3.B | Bin 857 -> 825 bytes .../EnumEvolveTests.multiOperations.3.BOB | Bin 859 -> 827 bytes .../amqp/EnumEvolveTests.multiOperations.3.C | Bin 1013 -> 825 bytes .../amqp/EnumEvolveTests.multiOperations.3.D | Bin 857 -> 825 bytes .../amqp/EnumEvolveTests.multiOperations.4.A | Bin 1004 -> 972 bytes .../amqp/EnumEvolveTests.multiOperations.4.B | Bin 1004 -> 972 bytes .../EnumEvolveTests.multiOperations.4.BOB | Bin 1006 -> 974 bytes .../EnumEvolveTests.multiOperations.4.CAT | Bin 1006 -> 974 bytes .../amqp/EnumEvolveTests.multiOperations.4.D | Bin 1004 -> 972 bytes .../amqp/EnumEvolveTests.multiOperations.4.F | Bin 1004 -> 972 bytes .../amqp/EnumEvolveTests.multiOperations.4.G | Bin 1004 -> 972 bytes .../EnumEvolveTests.multiOperations.5.APPLE | Bin 1115 -> 1083 bytes .../amqp/EnumEvolveTests.multiOperations.5.B | Bin 1111 -> 1079 bytes .../EnumEvolveTests.multiOperations.5.BBB | Bin 1113 -> 1081 bytes .../EnumEvolveTests.multiOperations.5.CAT | Bin 1113 -> 1081 bytes .../amqp/EnumEvolveTests.multiOperations.5.D | Bin 1111 -> 1079 bytes .../EnumEvolveTests.multiOperations.5.FLUMP | Bin 1115 -> 1083 bytes .../amqp/EnumEvolveTests.multiOperations.5.G | Bin 1111 -> 1079 bytes 29 files changed, 3 insertions(+), 6 deletions(-) diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumEvolveTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumEvolveTests.kt index c3df921925..aadcd75d5e 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumEvolveTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumEvolveTests.kt @@ -1,9 +1,7 @@ package net.corda.serialization.internal.amqp import net.corda.core.internal.toPath -import net.corda.core.serialization.CordaSerializationTransformEnumDefault -import net.corda.core.serialization.CordaSerializationTransformEnumDefaults -import net.corda.core.serialization.SerializedBytes +import net.corda.core.serialization.* import net.corda.serialization.internal.amqp.testutils.deserialize import net.corda.serialization.internal.amqp.testutils.serialize import net.corda.serialization.internal.amqp.testutils.testDefaultFactory @@ -248,7 +246,7 @@ class EnumEvolveTests { // CordaSerializationTransformEnumDefault(old = "D", new = "E"), // CordaSerializationTransformEnumDefault(old = "C", new = "D") // ) - // @CordaSerializationTransformRenames ( + // @CordaSerializationTransformRenames( // CordaSerializationTransformRename(to = "CAT", from = "C"), // CordaSerializationTransformRename(to = "BOB", from = "E") // ) @@ -272,8 +270,7 @@ class EnumEvolveTests { // // Finally, the original version of teh class that we're going to be testing with enum class MultiOperations { A, B, C } - - @Ignore("https://r3-cev.atlassian.net/browse/CORDA-1497") + @Test fun multiOperations() { val resource = "${javaClass.simpleName}.${testName()}" diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.1.A b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.1.A index c28d4809779174a1c05252023cecf33dea2f7a9c..1ced4449f27ee3e5e2ce7b1964b1e7699661c69f 100644 GIT binary patch literal 728 zcmYe!FG@*dWME)uIGO|`85kHZFfcG31TvTz7AxhYmgpseR9LxGIOnJMX;&mirDS-y zr$uB`IT|~b+u9z`V?*-S(qIi zPzxDP0U1niH?SVK%ev4VZn<7@YEfolPG(hNNoIbYUS?iNYEfQdj$UGJVS%1&UTLmt zS$C6OvHG~;Xo`STocQI0QQB9sS91n zbB`~odt4nH9G&21vmLm|MkRwCTorivVTS_@c;yHm%(#c6Bj z-imAelg>W%{Pv|y&ic3Yy2LQ&hUp6c^F{o4Fu)`t@9`wn zjO_5gZOoKTUN@CwX}UiF)r@2b*vd#v0oxhL7EsgD(&W)t@Q{=0?DLdekgKz`Ez6ah RC{KTf&2FfAd;uz;{RJ)J^H=}? diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.1.B b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.1.B index 4f5e9ab81cbc80e43fdeef5fdfaae81b06e039e2..bdd9650ce071e2e1387181bad26c484cb39fbf2e 100644 GIT binary patch literal 728 zcmb_a%SyvQ6rG!-E!wR=pcFSks8&RTLY@?aXwfXJ;9^XsG>|4WkG5vzuMGakWZ}k@ z;@W?4Dl(w~L8P1Ea^}oE=ghrcocI_JLQgOE{6q-e5Ryt>$mn@5BAM!~7-!bfjQxv) zrFZL3&IjY0Nul@jMZeEfSmUK5(|F9iW9Rhb3J>*YbZr)k6|X5#FkBEi64!x`A3=EK zg(U78?6W}!LR?Kr65uc>a2CXo8bldMA{?rC`Y==ND4*K%IGhugq*>aT=3y3G&Pei2 zPCKS%i@q}3m;Mq;V|SHNg$-L22(|y`$65DdYnpBdLrwPH+)!i9rb-y3ndn^)``ctR z<|yJ(i?#TibPC-XWKcM)K_-Qx8e~!E323Vo~X_2nPKMDRwx^VB- zrEbN4FcnGZfFRgSxSV^Eb8gP6ESiD8b%ulF{_=Ol9CXm|5ybm$!4wcLi{6%7HF3tGoeI`Hu$ z2=A&ebgv*k3!<&XaS-+xjX35(%Hn`}IAJlT9v{<`hXJPkAi^XV`eYP(Bj&OsO)CD- zOL;qD@i!-_G)U7BGp(dpt?C zMt1n$5;LWf*OyANr1EPBsAeEbz*Yuo3fRs-wt$+3mL`wJf`^<`XUkJ|L9WhXTb3(1 RQJ!vx&2Fgrd;!X!{RJ}i^I8A^ diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.1.C b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.1.C index 14b41b550660b3186af340d6647e2a750a127445..dfd95cb128b05785ec39abdf3a3d262f9a150ae5 100644 GIT binary patch literal 728 zcmYe!FG@*dWME)uIGO|`85kHZFfcG31TvTz7AxhYmgpseR9LxGIOnJMX;&mirDS-y zr$uB`IT|~b+u9z`V?*-S(q3^C%;FNb+323d6*CS8d|BX{*blG+O$qXC+b z(?Ui=G$H4Oj7DfeE(;lr;X<4TtC*(Mf>UxD8(0HtyUC;!rr13T^D8X@|e~rse3cKU8Mgeev-f7%x)1P zE{GI{WRi0-b22m2R6TM^2>JYY2b+LL2nCOjR<)8gLfeTb%qenDZ?DFOZ;l$yE+;2E zii10kUC*9JBVd{I2Uk8E?=Egw=Bw;z@4P(oFJ8u>U2!`Yp1NU6%UtKI^DkvA4?1rR z9)sZ)d`mNPp(Kywf)_GPOqmoyB|I{D{F<38na8Y5qf&$-FYl&G5*^VZ%1F^wyTTKd!K`0}Vd7?)M+Xg{gW!Gx=puN~0D1@xHEKT(m+0HKH8d-e`WEHY!6;M zDW3f=Zbh~BZ{vpj1>MVJSd6PkXL zvzDbhQdi;o_%B20{0?N1%KHrllsW(F$6fEo(RIU=+I9Y3-;l8GFf|OaTxwT`{cSTk zcU4)`;0>`SgF(9jnG6mqkj3Dr0@(~Y653jnctgW2+N)GZz3TrN}z@{T2 KKAVAP%>DwsTId=8 literal 760 zcmb`F%}N6?5XUpS{Xl#51(f1Nh}DXsP}sH`rRZ9e#gAj!MoHbT?rw|pB)&=TMbd+J zuO9U(zJsYqN*4sdUc&ItfAX8jOgD^s6c9oW&mDCl1TP2~Zz?d+(^|k%+?_fOE>2rJ z_l>yPKk4jK&u?GaIHJ2PumK>fwaNoO*mrQyvDG`hy6QVCa)k=#7}mk~As% zLoem+h{fNYq};TKe&3Yq#$TGVBmSXRBE83p-TSTMjMBIofW z)r@TOzfH`NPEIEkWl_zqE})W;8UnU5Qd7WoMzRG|wX`&OG#1?Cq%vEck_&QmHnwHC SVv5pqJ8X7C)#nRP1=?Rd9`jxR diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.2.A b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.2.A index 434b43c42af94e004f5fe4dda370934f73682195..0e1501dd02d9625b7229d3b3672014a88da7246f 100644 GIT binary patch literal 779 zcmYe!FG@*dWME)uIGO|`85kHZFfcHE0Wz2w7AxhYmgpseR9KZ8rnq@UxP`lx1q6na z8CmH2Ii{5a+S(q_V? z7LVH9GjG7-+VsM!*A<2>UT?xCSIe+>c^&(ss^|%i1%q47D_9cT{zk{rI`Hu=2=BVE zjEB5>%gc@t$08atI_H>+l*NJuIAJlT0l%jyj|8US-5e9K2+1-EmaNZ`G^vD(AmyDo zi@!KYrRfm!zpW0fzl3uX1JjgK!=eNx+yC>`-u6Wt$88vmf_g3NSim7N7w{xC8rkr_ zP0Ud@dDB!>#r6Co9A%(}gyRg Xmevc?ViT(8Awhd?88<(?>j2~vLyZGk diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.2.B b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.2.B index f216584cdf0704080d97b6b7d079c90cbf993ddc..f63ca46c9c787f608fa1c6ea68957a41bea7c61f 100644 GIT binary patch literal 779 zcmYe!FG@*dWME)uIGO|`85kHZFfcHE0Wz2w7AxhYmgpseR9KZ8rnq@UxP`lx1q6na z8CmH2Ii{5a+S(q_V?$y2CBkD^4xSOw7rwN-W9D&(q7yD@iTNOU%(r%q=X?bImKw zbuG)!DN7AWEiNfm$t}$($@DKsEdnbpR&jQ4g`3NKK#hqQPdOZjML3k@Kmhwf#?*zb z%&YTn9v$7jlCV YhD&N%A~5_HG6LP|0(S)#d7#5x0mt6+;s5{u literal 811 zcmb_a%T59@6z!ei5oOC47~-<=K|(YknE|@dAPhmm(lQijnCalmz`~WkQvH##aPNoc z(yg%=Q^G_O6WKJk=brYQlY9Lr9#cRFJv@)}i4eRXWS4beqo)JGQtYo8=fiL~oB30> z7LVH9GjG7-+VsM!*A<2>UT?xCSIe+>c^&(ss^|%i1%q47D_9a5eK0zf)`5?2L3r1N zWqNt_mX{qPjzu(Pbj~psDT@USaKd6v1Ab3a9tljtyE!Ie5t3yTELoo=X;KLnLCQOG z7JqS)O4A|ce_I_|e+lO(wlSPj!=eNx+yC>`-u6Wt$88v61@&6kv4BHlF5pROG_v7; zo0y|+^5#)d74`gF2}c>IA>lX!H6@&6pq7Nw3`8VU4YapwpOJLT*sP Ya7j%|1cpB_Wldj*TZBZPuSh&BROL*PWPN>7k^9p>R?ldR3DjNRpx zx8icTzVT)=VCnP+*H2k`SLgoiy-ebX@P)^+)ldk23xeRlE>77ZsBt%Je9I-s+CnH49i-mE(MuReZ5!vLTma%WxDW z8l*l&Ie%!~02*t02WLOPE_mSp7Rq38|EJMaH9Cf2&J~e;$PI?uVM-Vdr0Qn8tdu&x z4WsRCaTpIhb9cFHjXsQ0t jVSpdkRD;J58sRX9_#JJQM)1OLq)m6WqOq0RH0yi-BxQ`H diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.2.D b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.2.D index 71b890bdef56632d7dd9f24f3f714cd157a523f4..fbfd4f7ab6676323b5924b52c1d2aadd8abac6fb 100644 GIT binary patch literal 779 zcmYe!FG@*dWME)uIGO|`85kHZFfcHE0Wz2w7AxhYmgpseR9KZ8rnq@UxP`lx1q6na z8CmH2Ii{5a+S(q_V?$y2CBkD^4xSOw7rwN-W9D&(q7yD@iTNOU%(r%q=X?bImKw zbuG)!DN7AWEiNfm$t}$($@DKsEdnbpR&jQ4g`3NKK#hqQPdOZjWrS;DIS|0UkTG?k zD|znmMRkv>gM*_J+-$Z3PuQqru!HMDmYmGu61dsup2Zb24hIrg;hNYFumj`CaUr7t znvl~%Mng0q=Y@<$XhJRv8I93|To*E$z=b#u&SIjHe-8#A(hGM8;{itmm+OEC^FnS= Z!f;7VO9Y1hLPnrlUEr?3A`f)9D*)0`^X32m literal 811 zcmb_a%T59@6z!ei5oOC47~-<=K|(YknStp>gFJ$SrDZ76Fw?=AfrTr7rTQaf;oc9? zrCVb$ri6(mCbDU6&pquqC-?eMJf?sUdUzh`6Crp($S&)`Mo$NVrPyCH&WGV}HuI-$ zEgrQxXWaphYtxHvy{<59@p=)S@h(mL?*EeP+r zuuLzn-tw|-#IcCRjLtdcB4x3l0Zv%VX~6Gk$|HekcsIvHEJCu3f+g#-Buy&eB1n0A z&f+glQfWHG{BNs6>o4IP#lSqusbNt9ldb>xYHj->j^j3rMnSz6b}Zl!nG1N58jWoD z-zMg$o4k2cR7E{MSHe*SYDhTFKurlJ8OW1xnt_Ofs)6=akJgTJuR5E3D)m9Ro9n(R Y+p@G?7>`Y;o`(eO*)wi_=<5LF6I1&GU;qFB diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.2.E b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.2.E index cf15290a05c7320a732ff2f571a2126f75a08ed0..c0a06856f8418069e7973ead3a1a45f76547b4d4 100644 GIT binary patch literal 779 zcmYe!FG@*dWME)uIGO|`85kHZFfcHE0Wz2w7AxhYmgpseR9KZ8rnq@UxP`lx1q6na z8CmH2Ii{5a+S(q_V?$y2CBkD^4xSOw7rwN-W9D&(q7yD@iTNOU%(r%q=X?bImKw zbuG)!DN7AWEiNfm$t}$($@DKsEdnbpR&jQ4g`3NKK#hqQPdOZjWrS;DIS|0UkTG?k zD|znmMRkv>gM*_J+-$Z3PuQqru!HMDmYmGu61dsup2Zb24hIrg;hNYFumj`CaUr7t znvl~%Mng0q=Y@<$XhJRv8I93|To*E$z=b#u&SIjHe-8#A(hGM8;{itmm+OEC^FnS= Z!f;7VO9Y1hLPnrlUEr?3A`f)9D*)Gw^XLEo literal 811 zcmb_a%T59@6z!ei5oOC47~-<=K|(YknE|@dAPhmm(lQijnCalmz`~WkQvH##aPNoc z(yg%=Q^G_O6WKJk=brYQlY9Lr9#cRFJv@)}i4eRXWS4beqo)JGQtYo8=fiL~oB30> z7LVH9GjG7-+VsM!*A<2>UT?xCSIe+>c^&(ss^|%i1%q47D_9apKWB6-tpgw5g7B^j z%k=WtLIBvse6x3^B#{v$Kxqv6B(a47X zZDNkP$(u(-Rn+ryB^+g-hJ@n`)Rb_Nfm#wyGZ2wbHPGJb(b{qDRd=&br9LQkbKO^E YTb9-f(_#~<=OICRZW%W}^mPF830ZXmVgLXD diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.3.A b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.3.A index 26cbcc8f4ece2138fa2e3359f4e550e669740d4b..1ae51edf84483458accd7317cd74ec04024b7561 100644 GIT binary patch literal 825 zcmb_aK}!Nb7@gVG74;N!?jX`}NsXvL*j-%&OBC5Gf)1mNHncnH?z%#B=&ua@k?~OH z{y=n#enz9jY%T>6UWSi1?|bikeBWT~Un2+rczEiI7XbJIfLaiR3LXm-hx%a2I5wNj zGjwF05_EXm9N?So$$4Wss#duU*LVk=OTSi%O4hJf?p2~ur$w!cx!`FEgQgSvD8dJR zd>O(wQ7AG)n!l!5Lz?RW_6c&y9SX^q>V$^ar^wZjH=XDZ*KK`O8oSzk;$U#XA`WF|+@dr?sBPHVo5}+8Te?HY9A=Eaj3Ql-kK* zf7^_~`)S#f;hE*07~D%h7K5b(RAaE5fa(lZzCl@qsa9LG?n^Mccvdzv@!~!uW%CZ? yW_&<7zOAzS@-*srh{OgM6lv>{{=>hB9v@2HQTR@L0y(~=GOdd#^01(-hWZ3!L;t=2 literal 857 zcmb_a%SyvQ6rGv0DQ#DRYZp@76k>g#K`7+WY>KsFqqv!jes1_i-xhRd0A?>UEi$J5xw5CHJ_?C=)=_zHkz@InGln;}V+an3k8bk%6o z&rCHto!m^K{+)9@Q}u3_DR7DB9FIJLshxIkKF)bEqENRB9mI9emmvJ$ z6%rn*?5#?NLR<-_F2RwfcwtK75c^6(Vh{V?Jx;x8sNmo>Qmk+mSlQIih)t3-X$3Ps z^@b6N7o4PJYL=KUu`TqMa2g{}G*#GTjY2{P|MN9i_GM|BZU_yTy~`^W&@5W6UWSkNy?NjJ_}*abUn2+rczEiI7XbJIfLaiR3LXm-hx%a2I5wNj zGjwF05_EXm9N?So$$4Wss#duU*LVk=OTSi%O4hJf?p2~ur$w!cx!`FEgO&s397W85 zA72CEnYslexfPI2oa)&}PraGY^_9=38nlKw+qSdW8}cND%8Mg#K`7+WY>KsFqqvtEwOc!p& zAJMH_rv)cACr3tHz^# zZmRj|j;Le|`RKj?>$4ZM;^ec3iuk^1 zL#7u8)?n)rZO%`oN3Jm62!-5rPtbqp^9aQOHEqN;iqmMYHHpe+B*X#Xv_-g2Z+Z*1 diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.3.BOB b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.3.BOB index 87a4636599cc079fdec3c77f650aa529d428d2ca..2657522877aebf8bbdb26add21b6c382785d9b9f 100644 GIT binary patch literal 827 zcmb_a%SyvQ6rG#&VcV79+C?cYLmwh46q2-|P%SMbRuNoG>$C=wv`x|o;=*4U{E_KG z*ZzRG6FlXU9-TI(qiU5EVTHHXIrnO%u%r)r;lB61nZ%zqkeQrGpO3swnb`*gu+mvalOI+*_PFqI#16}L@ AYXATM literal 859 zcmb_a%SyvQ6rGv$VcV79+JzK1g;*bG5DH0}Y>KsFqqvtEwOc!p& zAJMH_rxhnQCX2T@%1?B-P%_ZMeB4}8!qtdqv5f?zh#|wJ+I}MO1tHsjb^+RL?wNoTP2}{xDxu3 zgzvmUqE}|GWjYY@au7NMN3QG!DTxE@$q9*F?74S1b;Cf${!JvC!NfPS(94KLk~C@h z6EAfK5sBxVq}f$X@xR12(O=e68G&M`#4f57659Wtul}MhQ&lxxXjIr+Wyu1nNlTua zq(Y;({BJi?nOzn`RaQk5Y818$kWS%t0qRn?Q-BN#cfUck&oofDC&1eD87+D9>3yEI zh6S0H2dvK4B-)ro#vqrNUxY&L+7NDj=<^lw05vSc*7Io8*{VbqQNiOj#gRT(FpkV- z^AsJLCj=ebGza*)dwkZIj%qco!xi3s=OVDnaoHO7D!pnv>a?hJek*vI!l3OcQb!Rw z@Z(DmzKKs#%ktMOYe;cD!~sDbxkC{dQ=QNV2NZca@~0Esp)=o!$6kzG97bWupLr2! zPjIj#hb7x^q^8Pul)soV_$8E08Q#e;h?(=heOjygIEG3Vc&##YHsgc! literal 1013 zcmb_a+e*Vg5Zz7Ml-ei3XCI{aQi%0}2BDDaOR-jL6kj*(N~4=glQf9BSf{qRT0>Wd7;mdd49Gq(%HCljPh#v zxJ<`S!Nc0HMk8S9^cy$0m|aGv2mY-i#9-kW!$EX2ueq8652)iSQeM0yMER0X3xOK| zz-o2v`heT?Q<$FfEMy{lW<{99dKecxicZ_Q}>S!_nomY!oDI@NDm;L%|NXd`&o06GXBHGnRH$3MU>=2Y!z75GGgyMTZp zVKbeBKrbyRMbF34wdBFwf~y^DXAcL`<@XH!7>=~6&y@l+a;tLXaqan7%C0- zw#Fn_lEY$6x23MachO%=>HG@HrW9{y7$nU8-#*RteQaGfOsTE%cXdO;y3JB94I`jiFSbY_gyo318Dgrsar7*QaeC!cHTblX=bOry! literal 857 zcmb_a%SyvQ6rGv0DQ#DRYZp@76k>g#K`7+8Db|XO;&#%EG@6%58bn<9D}z5WUAPs0 zM7M687M$9kAlS`tIrrQ-_i)cBPCX0(0FTcue*u8607wQeB=EEukz5(g7)OVmnhbk| zsTQZ>n{m>+bFU|=-t964F7e#s;YoO~?Od2a(DrP#(+Zw3Ag+VH1mOp- zkmyy}Ta^xkxDv%4!HKWM?bTo2%hojA5E?Rjmsc#H*|ZY)SuQln z=6}1G#@wnHs<9fspH5+?1Q`_WlpvGB-4bL`xc3dJBPtmKKDsZ!`t${@*!lFKBED}r zkm-wnHQ2gDo3qGRnT4igxD_U5T1GhgCz(0|)v+-xWONK+U}!j+1T>t1f$0Jeivbyo zAOdf+bWLU*|Bdc~Rf#2;`FVPoc_pbud5Jj_4?Fqca+kw_SVp*c zEC&ME7c!S7|bSZL)()ef>)_tgwSfORw*QD(;`?yC1B|-wyOm4XR=L;C&8O4JVa4m@+5@Jbf~_psqYBn@N6 z;q>6NUmw*^dRk?2t~))|_O}(K(I667;6+glx%vPNCVh2qjQaatcUrxoyc~~#V$6Mb z>cdN4hWsX7@T`U`dCy{=9ax#H{i87=wVgPaXe|2>i>(XRdQJF`Mh62`U z7P%$c$QsVrR*0BWI)dN&CX;)Si9{mCUBsZAf>aDD2`JBFp{W=c>?FWg+QMKr0mVF3 zr{Zb?jCpMg_EJy>gBpYKFEpoeJTptq>G3VYOo|2|ck|x@Sru?G9nT4igxD_U5T1GhgCz(0|)v+-xWONE)WN0{=1T>t1f$0Jeivbyo zAOdf+bWLU*|Bdc~Rf#2;`FVPoc_pbud5Jj_4?Fqca+kw_ScF|H y2Ljj^GNvwcC1m2{XhsWlL7-dMh*GOM`8lJa7gwSfORw*QD(;`?yC1B_-wxa~{sL3=ft^{}f#o&*O z3-`Kk)z&&hu5J`iW zaX39VHS5FrNl&Xx&ULq^I-_kxX*7rg=6F$5L!RD8{fVjekCD0Wcc;}W%FFQ>s0Ls< zJk{Z)FF}5jZg>{UlJ_junYiTnLuid`$@UNoJj;D=I52M@<3i z)QjAbWn>L!96LlzY0-k;`X-ZmktK;lq+L`nC@(-N29*?)=dsXK3=DQsV4T{*U^fND zJXNR3)f5=>+8FFDKphNf49dSypUUyfEZL{Ww+s_08i3r*js>zR;9@*xEqH0sE+%Dm z7`;H&7UG|8Yo{3BZlH$D#_|V+CiRTv7Y&{q&3;LNtOz)F20hD#7&H^h8FxOYB|d)u DK!z}6 diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.4.BOB b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.4.BOB index b741fceac9f439ca428245dc442d4307deabd70d..a402d6f0c933a206df1c1b395086d0434cb6d5f6 100644 GIT binary patch delta 384 zcmaFIevW;DSp9Ye1_ow^#Y%apC3?yEMJb6^j)CUhxrIsPX~}smhMs{g-ckCg+3B{n z2Micd)CGH4B$XH>nT4igxD_U5T1GhgCz(0|)p0N_WOnj*3SnVrIGO}Bo`Hes0uYM< z8H^wXFGvI`r@_F$df+bWLU*|Rdc~Rf#2;`FVPoc_pbud5Jj_k2?9`a+$+{ zSVp*AEC&ME7c!s}A%E0~3)(2{PXW E0He@*P5=M^ literal 1006 zcmb_b%SyvQ6wM@k*mfm|;8v;|A+%Z_RSHSkvZ;5hE~>4L5t2c9`jc%>VGdsufN5(g3E zaC&fR)Q9zxo?4xpYfewG{ViE;G)M)^@y6plYvUi^~eH|U0Eu`GGdVvWg*t~Z3HZ;6(RVBngz7{b6ZZR^HFmgkD5bM1?&J95;q zXOE!{Lll;sk&P_Phe6B<%S}mT_X}i`|I70v0s%|P5F?KP#NGeZ)t#?Pl_XhV8aZkT zSfgI#7AzyFIAdEOVoGTXe(Rh>?(-}uPx3qlG6t0tq+n1@Kn2?ErmSPIodBcM76v;B zDB{tnz@U}@BVHSW-4xWpV2?qC7wS_bnwbUr^!PTL(2@?w-RxK-D*`S>W7dL~ChcNe zW{1^OB&(_T=iAx|#nT4igxD_U5T1GhgCz(0|)p0N_WOjB831MPrIGO}Bo`Hes0uYM< z8H^wXFGvI`r@_F$df+bWLU*|Rdc~Rf#2;`FVPoc_pbud5Jj_k2?9`a+$+{ zSVp*AEC&ME7c!s}A%E0~3)(2{PXW E0H-K>Q2+n{ literal 1006 zcmb_b%SyvQ6wM@k*mfm|;8v;|A+%bnRSJ1DErK;v0+#M#J4zsr+Dsp~65RP0gFiAZ z-0RAP-{3zuNoA5o1;K8HnRCybbLZSUeQz`{0mpH7&zE%NIPk)8!W-QP+~cMTk=PFy zN3+ATZf#II?P%rch3<3|d$=RZ^*Skm1>R&*MXugMy=hnJouKZ4*Pd0bDQ|_xf+`yZ z&y;wo#EV~&{1)9ru{8NeW1Y#1t~Y?@&=M^d!I5j)q7O%wX|B?bn(CIe1CaaZm?vujE(BxNfLA8%VpwL2 z(eq?I5ud-U8Do6Aff^ExnT4igxD_U5T1GhgCz(0|)v+-xWONB(W@tE?1T>t1f$0Jeivbyo zAOdf+bWLU*|Bdc~Rf#2;`FVPoc_pbud5Jj_4?Fqca+kw_SVp*c zEC&ME7c!gwSfORw?Asv#6JF^?;2t(Sh@?Tp zIGi1v_Ugm>Nms8-&keV$I-_kxX*7rg7I4s;qEP2mjgNaL?KZMrEmTV8fv1d6_0LQlF*f$okeNVF7>ye~;6ITy? zCxj*pP*8Fw4zi6A9LJoXq{%wFUm#ojUmhnD2v|~v^h69G?)v8ug6j7fqfV&3{ROtOz)F20hD#7-+HOjJp`rW1l|& DPJS?G diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.4.F b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.4.F index b9bedc2502364bada4459213896dd518397395df..ef5076d501187e7893a31d37bc5a688a641ebd06 100644 GIT binary patch delta 382 zcmaFEeujO5Sp8N81_ow^#Y%apC3?yEMJb6^j)CUhxrIsPX~}smhMs{g-ckCg+3B{n z2lNnT4igxD_U5T1GhgCz(0|)v+-xWONH*WoS5>1T>t1f$0Jeivbyo zAOdf+bWLU*|Bdc~Rf#2;`FVPoc_pbud5Jj_4?Fqca+kw_SVp*c zEC&ME7c!gwSfORw*QD(;`?yC1B_-wxa~{GMPSbCAjl127hE+ zxYva%zrlZSQe~P}1;K8HnRCybbLZSU{U93HfaAE^rwh7r9C+q9;gxO#?qS1+NbV<$ z!^y#^RU6b!x_WthZg^eI4YyUbUMDh`v(?@|j zf+mbnT=a%6a*Pm0Q%+oLC_1}eAY1%j9;XlpSW<>q5(W@={#RFLzAjx+RE=qf)D*Bz zy%3iyqv$x}Ix%8Oix&LWH-+3wEJ-2~?V^f7X#vtOD5sztkA;S6Vz83}lhh^#yD2E) zX$DQMq`-vN!eDO!YGY7kQ0|5LR7z%M$v!>4Wtd381mtdZ%#&3C7m_h+!b_WWaav}Z z(eq?&A^!QcR*LcM25QJ`EPqgFP|sL?!Q{!&?3d)pih#v4=-VE|pfRnT4igxD_U5T1GhgCz(0|)v+-xWONTt1f$0Jeivbyo zAOdf+bWLU*|Bdc~Rf#2;`FVPoc_pbud5Jj_4?Fqca+kw_SVp*c zEC&ME7c!gwSfORw*QD(;`?yC1B_-wxa~{vY9?`CAjl127hE+ zxYva%zrlZS5@niJ1;K8HnRCybbLZSU{a`q-0mpH-PZxCMIPlDI!Ykbf+{1ot2U^eboKJ&-0-@Z8*Zy=y-s8>#~Y8U$TxbZH?g$dF|zi9&a`qxc_NQN+Z34& zPjz_tOOW5B8=l3ofo$=Ad7MHZU`ZKbNf z^+H^-jH2U=>qLktEn4tf-xP8$u_TE|w2LYRr3FaCpqzqoJQftrX+i4b+g?SpJ~Upq{b(g2|Jk*)PeH6#E^K_ju8ap!}2;`0Y% CaxiWH diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.APPLE b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.APPLE index 95b55e36de930ecf79bed3e3565b6439581c852e..8254ede337b3293b34da7abe3cce4c0cc028568f 100644 GIT binary patch delta 389 zcmcc3v72LpSiK4Z0|PU|Vx_#)620X7qLf6d)R5rhyez-O{P4Uo?L=*h;;irt??7AI z14fJ}>hcTH0&`q5EG*4aor8-ieItDG-Q3H8>bRH|vN{F?__&5JFf<%Z0$RYpz;pqK z<$w%E5JMCs0+!PPNwOZe%ev4V?gG8y)S}G9oXo1klFa-(z0ACl)S|q^oXPQwViWJ` z_~CM&!+}^vxP2@K0@xQarY>|P!c+@&NuVRxh*GOM`8A`W2sW=zR$yAL-VO2?aRzOc IW_Dl%0GR)MkN^Mx delta 417 zcmdnZahqd;SiK7a0|PU|Vx_#)620X7qLf4{ACGcvefRR>ZdZ0E9)Aa|qW#B^KR z14fJ}>QbYM3rxZSy!{=W^tFovGSbW(Gu_L9>bRH|vN{F?__&5JFf<%Z0$RYpz;pqK z&43I>5JMLv0+urZNwOYz%ev4X?gG8M{FKzhf=s>4ypq(Syu=*6;?$zd#GK5k#FEVX zyovjr!f?6K;Xo=Q+)kDQ5$p>YQy2OYYw~1&MhksKpo7>*(xW>0GNYmhw&0k|!L(d| PGRU)}8Myg3lLI3FP!@;q diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.B b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.B index f6aaf7d2ff87ef338bbd9666a6e5fddc2f78ed1c..e54393c2ff906fb077fd9b6b27f5aafecccd6984 100644 GIT binary patch delta 381 zcmcc4v7KXrSiJ%R0|PU|Vx_#)620X7qLf6d)R5rhyez-O{P4Uo?L=*h;;irt??7AI z1A2@o>hcTH0&`q5EG*4aor8-ieItDG-Q3H8>e!eTGCGAYGBg}b0vgW1z;pqK<$w%E z5JMCs0+&-`J#d$Gp*!4mz2elO%*33`s>G7a{5-wPypq(Syu_S|hn@Uzxy#`|EW$39 z0|D#{8B-U!5;AdeG^2&OB+xBvM5$Gs{G3ry1e=#9%P=ih?*@5_ID0P972 AAOHXW delta 413 zcmdnaah+p=SiJ)S0|PU|Vx_#)620X7qLf4{ACGcvefRR>ZdZ0E9)Aa|qW#B^KR z1A2@o>QbYM3rxZSy!{=W^tFovGSbW(Gu_L9>e!eTGCGAYGBg}b0vgW1z;pqK&43I> z5JMLv0+-WcJ@A%wp+DSqy}bOC)Wm{Jz0ACl)S|q^9KGVyqRhmc%&Nqa%>2BGyPd*t zxzFK1D#A{d0}<>C8B-Vf5^M5gZ$=A!MWEZ*NYbM^`8=bd2)3Y@%)+!>e=^9kq#3yR IJCg$=04kk^i2wiq diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.BBB b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.BBB index cd181d1b7c89545df61a6e8882e0f7d5e0e9b7d4..dfbee9ef29187740b712851fad2c0e9865f40146 100644 GIT binary patch delta 383 zcmcb~v6EwhSiKSh0|PU|Vx_#)620X7qLf6d)R5rhyez-O{P4Uo?L=*h;;irt??7AI z0|tyJ>hcTH0&`q5EG*4aor8-ieItDG-Q3H8>NuDdGCMgrg|IL*98Cfm&%nTR0f^;* z3`P(`6eI$b(_mm=J#d$Gp*!4uz2elO%*33`s>G7a{5-wPypq(Syu_S|N1gm|xy<1} zEF;`5mIDFo3mH=vx)L&Rax9~Tx+KsoY(%M5o&1tfQ3RW(C(AJ{SMLUSia3KdOENn! F0sz-Ld&2+# delta 415 zcmdnVag$?$SiKVi0|PU|Vx_#)620X7qLf4{ACGcvefRR>ZdZ0E9)Aa|qW#B^KR z0|tyJ>QbYM3rxZSy!{=W^tFovGSbW(Gu_L9>NuDdGCMgrg|IL*98Cfm&%nTR0f^0j z3`P(`7bF6eGhkp~J@A%wp+DSyy}bOC)Wm{Jz0ACl)S|q^9KGVyqRhmc%&Nqa%>2BG zd!52?xzOQ2DkI!ZmID#&3mH=v`VwpMWM4)LeMO+#*htc&I{6}_q6oIYn9RnsTz@ji Nv!ofg`6rVDBLFA;hb#a9 diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.CAT b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.CAT index 27d9d766f71d37be47212de7082a1dd244c50e7b..16cf27d286df65e07e7b906582b101b1c760f548 100644 GIT binary patch delta 383 zcmcb~v6EwhSiKSh0|PU|Vx_#)620X7qLf6d)R5rhyez-O{P4Uo?L=*h;;irt??7AI z0|tyJ>hcTH0&`q5EG*4aor8-ieItDG-Q3H8>NuDdGCMnlgfKBQ98Cfm&%nTR0f^;* z3`P(`6eI$b(_mm=J#d$Gp*!4uz2elO%*33`s>G7a{5-wPypq(Syu_S|N1gm|xy<1} zEF;`5mIDFo3mH=vx)L&Rax9~Tx+KsoY(%M5o&1tfQ3RW(C(AJ{SMLUSia3KdOENn! F0s!eSd(!{_ delta 415 zcmdnVag$?$SiKVi0|PU|Vx_#)620X7qLf4{ACGcvefRR>ZdZ0E9)Aa|qW#B^KR z0|tyJ>QbYM3rxZSy!{=W^tFovGSbW(Gu_L9>NuDdGCMnlgfKBQ98Cfm&%nTR0f^0j z3`P(`7bF6eGhkp~J@A%wp+DSyy}bOC)Wm{Jz0ACl)S|q^9KGVyqRhmc%&Nqa%>2BG zd!52?xzOQ2DkI!ZmID#&3mH=v`VwpMWM4)LeMO+#*htc&I{6}_q6oIYn9RnsTz@ji Nv!ofg`6rVDBLF+{hdclP diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.D b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.D index 54420c13fbf47c1a73bf07e1a80e61bff1bd1f27..309cd942297ebf5544f60f45e0dc934d4f73a211 100644 GIT binary patch delta 381 zcmcc4v7KXrSiJ%R0|PU|Vx_#)620X7qLf6d)R5rhyez-O{P4Uo?L=*h;;irt??7AI z1A2@o>hcTH0&`q5EG*4aor8-ieItDG-Q3H8>e!eTGP;B?Gc+7c0vgW1z;pqK<$w%E z5JMCs0+&-`J#d$Gp*!4mz2elO%*33`s>G7a{5-wPypq(Syu_S|hn@Uzxy#`|EF;`J zmIDFo3mH=vx)L&Rax|lbx+KsoY(%M5o&20pQ3RWpC(AG`SMLUSia3Kdi!(bg0s!y5 Bdn5n= delta 413 zcmdnaah+p=SiJ)S0|PU|Vx_#)620X7qLf4{ACGcvefRR>ZdZ0E9)Aa|qW#B^KR z1A2@o>QbYM3rxZSy!{=W^tFovGSbW(Gu_L9>e!eTGP;B?Gc+7c0vgW1z;pqK&43I> z5JMLv0+-WcJ@A%wp+DSqy}bOC)Wm{Jz0ACl)S|q^9KGVyqRhmc%&Nqa%>2BGyPd*t zxzFK1DkI!nmID#&3mH=v`VwpMWN$_beMO+#*htc&I{7@Kq6oI2n9RboTz@jiv!ofg J`8$&XBLFXzhK&FK diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.FLUMP b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.FLUMP index 86b4ed8e6a5d0a910440392e45bb15de5edb975f..a58ea9edb3a3e54cce554c51f6c01aac658c462d 100644 GIT binary patch delta 389 zcmcc3v72LpSiK4Z0|PU|Vx_#)620X7qLf6d)R5rhyez-O{P4Uo?L=*h;;irt??7AI z14fJ}>hcTH0&`q5EG*4aor8-ieItDG-Q3H8>bRH|vby<%`UZrsGBg}b0$RYpz;pqK z<$w%E5JMCs0+!PPNwOZe%ev4V?gG8y)S}G9oXo1klFa-(z0ACl)S|q^oXPQwViWJ` z_~CM&!+}^vxP2@K0@xQarY>|P!c+@&NuVRxh*GOM`8A`W2sW=zR$yAL-VO2?aRzOc IW_Dl%0JAN9rvLx| delta 417 zcmdnZahqd;SiK7a0|PU|Vx_#)620X7qLf4{ACGcvefRR>ZdZ0E9)Aa|qW#B^KR z14fJ}>QbYM3rxZSy!{=W^tFovGSbW(Gu_L9>bRH|vby<%`UZrsGBg}b0$RYpz;pqK z&43I>5JMLv0+urZNwOYz%ev4X?gG8M{FKzhf=s>4ypq(Syu=*6;?$zd#GK5k#FEVX zyovjr!f?6K;Xo=Q+)kDQ5$p>YQy2OYYw~1&MhksKpo7>*(xW>0GNYmhw&0k|!L(d| PGRU)}8Myg3lLI3FY{-ZQ diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.G b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EnumEvolveTests.multiOperations.5.G index 92ed95c4d8a99cb494cc6875e17580c7e2cc73ef..64ef55eef26bf69060a574357fe1ca4d485682e9 100644 GIT binary patch delta 381 zcmcc4v7KXrSiJ%R0|PU|Vx_#)620X7qLf6d)R5rhyez-O{P4Uo?L=*h;;irt??7AI z1A2@o>hcTH0&`q5EG*4aor8-ieItDG-Q3H8>e!eTGP;MbF*F=a0vgW1z;pqK<$w%E z5JMCs0+&-`J#d$Gp*!4mz2elO%*33`s>G7a{5-wPypq(Syu_S|hn@Uzxy#`|EF;`J zmIDFo3mH=vx)L&Rax|lbx+KsoY(%M5o&20pQ3RWpC(AG`SMLUSia3Kdi!(bg0s!`L Bdny0` delta 413 zcmdnaah+p=SiJ)S0|PU|Vx_#)620X7qLf4{ACGcvefRR>ZdZ0E9)Aa|qW#B^KR z1A2@o>QbYM3rxZSy!{=W^tFovGSbW(Gu_L9>e!eTGP;MbF*F=a0vgW1z;pqK&43I> z5JMLv0+-WcJ@A%wp+DSqy}bOC)Wm{Jz0ACl)S|q^9KGVyqRhmc%&Nqa%>2BGyPd*t zxzFK1DkI!nmID#&3mH=v`VwpMWN$_beMO+#*htc&I{7@Kq6oI2n9RboTz@jiv!ofg J`8$&XBLFuEhLZpQ From cfccfd075e3eb59129a954d9a8a428b4f9b24324 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Mon, 4 Mar 2019 17:44:56 +0000 Subject: [PATCH 056/159] CORDA-2688 - Add Serialization Context option for no carpenting (#4849) * CORDA-2688 - Add Serialization Context option for no carpenting Can be used by the attachment class loader - Serialization Framework will still consume all Exceptions and throw a NotSerializableException * Fix tests --- .../core/serialization/SerializationAPI.kt | 19 +++++++-- .../internal/AttachmentsClassLoader.kt | 1 + .../internal/SerializationScheme.kt | 3 ++ .../internal/amqp/DeserializationInput.kt | 2 +- .../internal/amqp/ObjectSerializer.kt | 2 +- .../internal/amqp/RemoteSerializerFactory.kt | 22 ++++++---- .../internal/amqp/SerializerFactoryBuilder.kt | 8 ++-- .../internal/model/TypeLoader.kt | 11 ++++- ...aCalculatedValuesToClassCarpenterTest.java | 3 +- .../amqp/DeserializeNeedingCarpentryTests.kt | 40 ++++++++++++++----- .../amqp/DeserializeSimpleTypesTests.kt | 4 +- .../carpenter/ClassCarpenterTestUtils.kt | 9 +++-- ...berCompositeSchemaToClassCarpenterTests.kt | 15 ++++--- .../InheritanceSchemaToClassCarpenterTests.kt | 22 ++++++---- ...berCompositeSchemaToClassCarpenterTests.kt | 9 +++-- .../model/ClassCarpentingTypeLoaderTests.kt | 9 +++-- 16 files changed, 122 insertions(+), 57 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt index 101329e05a..5ead84aef6 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt @@ -1,8 +1,6 @@ @file:KeepForDJVM package net.corda.core.serialization -import co.paralleluniverse.io.serialization.Serialization -import net.corda.core.CordaInternal import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement import net.corda.core.KeepForDJVM @@ -12,6 +10,7 @@ import net.corda.core.serialization.internal.effectiveSerializationEnv import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.sequence +import java.io.NotSerializableException import java.sql.Blob data class ObjectWithCompatibleContext(val obj: T, val context: SerializationContext) @@ -152,7 +151,15 @@ interface SerializationContext { */ val lenientCarpenterEnabled: Boolean /** - * If true the serialization evolver will fail if the binary to be deserialized contains more fields then the current object from the classpath. + * If true, deserialization calls using this context will not fallback to using the Class Carpenter to attempt + * to construct classes present in the schema but not on the current classpath. + * + * The default is false. + */ + val carpenterDisabled: Boolean + /** + * If true the serialization evolver will fail if the binary to be deserialized contains more fields then the current object from + * the classpath. * * The default is false. */ @@ -182,6 +189,12 @@ interface SerializationContext { */ fun withLenientCarpenter(): SerializationContext + /** + * Returns a copy of the current context with carpentry of unknown classes disabled. On encountering + * such a class during deserialization the Serialization framework will throw a [NotSerializableException]. + */ + fun withoutCarpenter() : SerializationContext + /** * Return a new context based on this one but with a strict evolution. * @see preventDataLoss diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt index ea5f0d5f08..93a61c0f2f 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt @@ -327,6 +327,7 @@ object AttachmentsClassLoaderBuilder { .withClassLoader(transactionClassLoader) .withWhitelist(whitelistedClasses) .withCustomSerializers(serializers) + .withoutCarpenter() } // Deserialize all relevant classes in the transaction classloader. diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt index 9c6162a874..2c03e2fa56 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt @@ -26,6 +26,7 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe override val encoding: SerializationEncoding?, override val encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist, override val lenientCarpenterEnabled: Boolean = false, + override val carpenterDisabled: Boolean = false, override val preventDataLoss: Boolean = false, override val customSerializers: Set> = emptySet()) : SerializationContext { /** @@ -45,6 +46,8 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe override fun withLenientCarpenter(): SerializationContext = copy(lenientCarpenterEnabled = true) + override fun withoutCarpenter(): SerializationContext = copy(carpenterDisabled = true) + override fun withPreventDataLoss(): SerializationContext = copy(preventDataLoss = true) override fun withClassLoader(classLoader: ClassLoader): SerializationContext { diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt index f5ed5dd924..503d370fd6 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt @@ -169,7 +169,7 @@ class DeserializationInput constructor( val objectRead = when (obj) { is DescribedType -> { // Look up serializer in factory by descriptor - val serializer = serializerFactory.get(obj.descriptor.toString(), schemas) + val serializer = serializerFactory.get(obj.descriptor.toString(), schemas, context) if (type != TypeIdentifier.UnknownType.getLocalType() && serializer.type != type && with(serializer.type) { !isSubClassOf(type) && !materiallyEquivalentTo(type) } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt index 03722bbd7f..af241c4efc 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt @@ -17,7 +17,7 @@ interface ObjectSerializer : AMQPSerializer { if (typeInformation is LocalTypeInformation.NonComposable) throw NotSerializableException( "Trying to build an object serializer for ${typeInformation.typeIdentifier.prettyPrint(false)}, " + - "but it is not constructible from its public properties, and so requires a custom serialiser.") + "but it is not constructable from its public properties, and so requires a custom serialiser.") val typeDescriptor = factory.createDescriptor(typeInformation) val typeNotation = TypeNotationGenerator.getTypeNotation(typeInformation, typeDescriptor) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt index c92947651b..e7b00f618a 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt @@ -1,5 +1,6 @@ package net.corda.serialization.internal.amqp +import net.corda.core.serialization.SerializationContext import net.corda.core.utilities.contextLogger import net.corda.serialization.internal.model.* import org.hibernate.type.descriptor.java.ByteTypeDescriptor @@ -16,8 +17,8 @@ interface RemoteSerializerFactory { * @param typeDescriptor The type descriptor for the type to obtain a serializer for. * @param schema The schemas sent along with the serialized data. */ - @Throws(NotSerializableException::class) - fun get(typeDescriptor: TypeDescriptor, schema: SerializationSchemas): AMQPSerializer + @Throws(NotSerializableException::class, ClassNotFoundException::class) + fun get(typeDescriptor: TypeDescriptor, schema: SerializationSchemas, context: SerializationContext): AMQPSerializer } /** @@ -57,14 +58,18 @@ class DefaultRemoteSerializerFactory( private val logger = contextLogger() } - override fun get(typeDescriptor: TypeDescriptor, schema: SerializationSchemas): AMQPSerializer = + override fun get( + typeDescriptor: TypeDescriptor, + schema: SerializationSchemas, + context: SerializationContext + ): AMQPSerializer = // If we have seen this descriptor before, we assume we have seen everything in this schema before. descriptorBasedSerializerRegistry.getOrBuild(typeDescriptor) { logger.trace("get Serializer descriptor=$typeDescriptor") // Interpret all of the types in the schema into RemoteTypeInformation, and reflect that into LocalTypeInformation. val remoteTypeInformationMap = remoteTypeModel.interpret(schema) - val reflected = reflect(remoteTypeInformationMap) + val reflected = reflect(remoteTypeInformationMap, context) // Get, and record in the registry, serializers for all of the types contained in the schema. // This will save us having to re-interpret the entire schema on re-entry when deserialising individual property values. @@ -79,7 +84,10 @@ class DefaultRemoteSerializerFactory( "Could not find type matching descriptor $typeDescriptor.") } - private fun getUncached(remoteTypeInformation: RemoteTypeInformation, localTypeInformation: LocalTypeInformation): AMQPSerializer { + private fun getUncached( + remoteTypeInformation: RemoteTypeInformation, + localTypeInformation: LocalTypeInformation + ): AMQPSerializer { val remoteDescriptor = remoteTypeInformation.typeDescriptor // Obtain a serializer and descriptor for the local type. @@ -117,9 +125,9 @@ ${localTypeInformation.prettyPrint(false)} } } - private fun reflect(remoteInformation: Map): + private fun reflect(remoteInformation: Map, context: SerializationContext): Map { - val localInformationByIdentifier = typeLoader.load(remoteInformation.values).mapValues { (_, type) -> + val localInformationByIdentifier = typeLoader.load(remoteInformation.values, context).mapValues { (_, type) -> localTypeModel.inspect(type) } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt index 665eda5c56..7c5ec79c49 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt @@ -17,10 +17,10 @@ object SerializerFactoryBuilder { whitelist, classCarpenter, DefaultDescriptorBasedSerializerRegistry(), - true, - null, - false, - false) + allowEvolution = true, + overrideFingerPrinter = null, + onlyCustomSerializers = false, + mustPreserveDataWhenEvolving = false) } @JvmStatic diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeLoader.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeLoader.kt index ca541f6102..500a3555c4 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeLoader.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeLoader.kt @@ -1,5 +1,6 @@ package net.corda.serialization.internal.model +import net.corda.core.serialization.SerializationContext import net.corda.serialization.internal.carpenter.* import java.io.NotSerializableException import java.lang.ClassCastException @@ -14,7 +15,7 @@ interface TypeLoader { * * @param remoteTypeInformation The type information for the remote types. */ - fun load(remoteTypeInformation: Collection): Map + fun load(remoteTypeInformation: Collection, context: SerializationContext): Map } /** @@ -25,7 +26,10 @@ class ClassCarpentingTypeLoader(private val carpenter: RemoteTypeCarpenter, priv val cache = DefaultCacheProvider.createCache() - override fun load(remoteTypeInformation: Collection): Map { + override fun load( + remoteTypeInformation: Collection, + context: SerializationContext + ): Map { val remoteInformationByIdentifier = remoteTypeInformation.associateBy { it.typeIdentifier } // Grab all the types we can from the cache, or the classloader. @@ -33,6 +37,9 @@ class ClassCarpentingTypeLoader(private val carpenter: RemoteTypeCarpenter, priv try { identifier to cache.computeIfAbsent(identifier) { identifier.getLocalType(classLoader) } } catch (e: ClassNotFoundException) { + if (context.carpenterDisabled) { + throw e + } null } }.toMap() diff --git a/serialization/src/test/java/net/corda/serialization/internal/carpenter/JavaCalculatedValuesToClassCarpenterTest.java b/serialization/src/test/java/net/corda/serialization/internal/carpenter/JavaCalculatedValuesToClassCarpenterTest.java index d0e39bb214..105b017d08 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/carpenter/JavaCalculatedValuesToClassCarpenterTest.java +++ b/serialization/src/test/java/net/corda/serialization/internal/carpenter/JavaCalculatedValuesToClassCarpenterTest.java @@ -7,6 +7,7 @@ import net.corda.core.serialization.SerializedBytes; import net.corda.serialization.internal.AllWhitelist; import net.corda.serialization.internal.amqp.*; import net.corda.serialization.internal.amqp.Schema; +import net.corda.serialization.internal.amqp.testutils.TestSerializationContext; import net.corda.serialization.internal.model.RemoteTypeInformation; import net.corda.serialization.internal.model.TypeIdentifier; import net.corda.testing.core.SerializationEnvironmentRule; @@ -77,7 +78,7 @@ public class JavaCalculatedValuesToClassCarpenterTest extends AmqpCarpenterBase RemoteTypeInformation renamed = rename(typeInformation, typeToMangle, mangle(typeToMangle)); - Class pinochio = load(renamed); + Class pinochio = load(renamed, TestSerializationContext.testSerializationContext); Object p = pinochio.getConstructors()[0].newInstance(4, 2, "4"); assertEquals(2, pinochio.getMethod("getI").invoke(p)); diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentryTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentryTests.kt index 1d7fa23dd6..0d5fbec240 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentryTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentryTests.kt @@ -1,11 +1,15 @@ package net.corda.serialization.internal.amqp import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializedBytes import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.amqp.testutils.* import net.corda.serialization.internal.carpenter.* import org.junit.Test +import java.io.NotSerializableException import kotlin.test.assertEquals +import kotlin.test.assertFailsWith import kotlin.test.assertNotEquals import kotlin.test.assertTrue @@ -35,6 +39,17 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) { // Deserialize with whitelisting on to check that `CordaSerializable` annotation present. private val sf2 = testDefaultFactoryWithWhitelist() + private inline fun DeserializationInput.deserializeWithoutAndWithCarpenter( + bytes: SerializedBytes, + context: SerializationContext? = null + ) : T { + assertFailsWith(NotSerializableException::class) { + deserialize(bytes, T::class.java, (context ?: testSerializationContext).withoutCarpenter()) + } + + return deserialize(bytes, T::class.java, context ?: testSerializationContext) + } + @Test fun verySimpleType() { val testVal = 10 @@ -52,19 +67,20 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) { assertEquals(deserializedObj1::class.java, deserializedObj2::class.java) assertEquals(testVal, deserializedObj2::class.java.getMethod("getA").invoke(deserializedObj2)) - val deserializedObj3 = DeserializationInput(sf2).deserialize(serialisedBytes) + val deserializedObj3 = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes) assertNotEquals(clazz, deserializedObj3::class.java) assertNotEquals(deserializedObj1::class.java, deserializedObj3::class.java) assertNotEquals(deserializedObj2::class.java, deserializedObj3::class.java) assertEquals(testVal, deserializedObj3::class.java.getMethod("getA").invoke(deserializedObj3)) + // NOTE: There is no point attempting this without the carepenter a second time as having carpented things up once + // it will, of course, just succeed even with the carpenter disabled val deserializedObj4 = DeserializationInput(sf2).deserialize(serialisedBytes) assertNotEquals(clazz, deserializedObj4::class.java) assertNotEquals(deserializedObj1::class.java, deserializedObj4::class.java) assertNotEquals(deserializedObj2::class.java, deserializedObj4::class.java) assertEquals(deserializedObj3::class.java, deserializedObj4::class.java) assertEquals(testVal, deserializedObj4::class.java.getMethod("getA").invoke(deserializedObj4)) - } @Test @@ -79,7 +95,7 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) { val concreteB = clazz.constructors[0].newInstance(testValB) val concreteC = clazz.constructors[0].newInstance(testValC) - val deserialisedA = DeserializationInput(sf2).deserialize( + val deserialisedA = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter( TestSerializationOutput(VERBOSE, sf1).serialize(concreteA)) assertEquals(testValA, deserialisedA::class.java.getMethod("getA").invoke(deserialisedA)) @@ -114,7 +130,7 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) { val classInstance = clazz.constructors[0].newInstance(testVal) val serialisedBytes = TestSerializationOutput(VERBOSE, sf1).serialize(classInstance) - val deserializedObj = DeserializationInput(sf2).deserialize(serialisedBytes) + val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes) assertTrue(deserializedObj is I) assertEquals(testVal, (deserializedObj as I).getName()) @@ -133,7 +149,8 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) { clazz.constructors[0].newInstance(2), clazz.constructors[0].newInstance(3))) - val deserializedObj = DeserializationInput(sf2).deserialize(TestSerializationOutput(VERBOSE, sf1).serialize(outer)) + val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter( + TestSerializationOutput(VERBOSE, sf1).serialize(outer)) assertNotEquals((deserializedObj.a[0])::class.java, (outer.a[0])::class.java) assertNotEquals((deserializedObj.a[1])::class.java, (outer.a[1])::class.java) @@ -164,9 +181,9 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) { val outer = outerType.constructors[0].newInstance(innerType.constructors[0].newInstance(2)) val serializedI = TestSerializationOutput(VERBOSE, sf1).serialize(inner) - val deserialisedI = DeserializationInput(sf2).deserialize(serializedI) + val deserialisedI = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serializedI) val serialisedO = TestSerializationOutput(VERBOSE, sf1).serialize(outer) - val deserialisedO = DeserializationInput(sf2).deserialize(serialisedO) + val deserialisedO = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedO) // ensure out carpented version of inner is reused assertEquals(deserialisedI::class.java, @@ -184,7 +201,7 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) { val classInstance = outerClass.constructors.first().newInstance(nestedClass.constructors.first().newInstance("name")) val serialisedBytes = TestSerializationOutput(VERBOSE, sf1).serialize(classInstance) - val deserializedObj = DeserializationInput(sf2).deserialize(serialisedBytes) + val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes) val inner = deserializedObj::class.java.getMethod("getInner").invoke(deserializedObj) assertEquals("name", inner::class.java.getMethod("getName").invoke(inner)) @@ -204,7 +221,7 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) { nestedClass.constructors.first().newInstance("bar")) val serialisedBytes = TestSerializationOutput(VERBOSE, sf1).serialize(classInstance) - val deserializedObj = DeserializationInput(sf2).deserialize(serialisedBytes) + val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes) assertEquals("foo", deserializedObj.a::class.java.getMethod("getName").invoke(deserializedObj.a)) assertEquals("bar", deserializedObj.b::class.java.getMethod("getName").invoke(deserializedObj.b)) @@ -226,7 +243,8 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) { unknownClass.constructors.first().newInstance(7, 8))) val serialisedBytes = TestSerializationOutput(VERBOSE, sf1).serialize(toSerialise) - val deserializedObj = DeserializationInput(sf2).deserialize(serialisedBytes) + + val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes) var sentinel = 1 deserializedObj.l.forEach { assertEquals(sentinel++, it::class.java.getMethod("getV1").invoke(it)) @@ -249,8 +267,8 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) { val serialisedBytes = TestSerializationOutput(VERBOSE, sf1).serialize( concreteClass.constructors.first().newInstance(12, "timmy")) - val deserializedObj = DeserializationInput(sf2).deserialize(serialisedBytes) + val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes) assertTrue(deserializedObj is I) assertEquals("timmy", (deserializedObj as I).getName()) assertEquals("timmy", deserializedObj::class.java.getMethod("getName").invoke(deserializedObj)) diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeSimpleTypesTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeSimpleTypesTests.kt index 0baf197d83..df9bc8316b 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeSimpleTypesTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeSimpleTypesTests.kt @@ -550,7 +550,7 @@ class DeserializeSimpleTypesTests { @Test fun classHasNoPublicConstructor() { assertFailsWithMessage("Trying to build an object serializer for ${Garbo::class.java.name}, " + - "but it is not constructible from its public properties, and so requires a custom serialiser.") { + "but it is not constructable from its public properties, and so requires a custom serialiser.") { TestSerializationOutput(VERBOSE, sf1).serializeAndReturnSchema(Garbo.make(1)) } } @@ -558,7 +558,7 @@ class DeserializeSimpleTypesTests { @Test fun propertyClassHasNoPublicConstructor() { assertFailsWithMessage("Trying to build an object serializer for ${Greta::class.java.name}, " + - "but it is not constructible from its public properties, and so requires a custom serialiser.") { + "but it is not constructable from its public properties, and so requires a custom serialiser.") { TestSerializationOutput(VERBOSE, sf1).serializeAndReturnSchema(Greta(Garbo.make(1))) } } diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTestUtils.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTestUtils.kt index 0ec2fed1ab..189d176d3c 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTestUtils.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTestUtils.kt @@ -2,6 +2,7 @@ package net.corda.serialization.internal.carpenter import com.google.common.reflect.TypeToken import net.corda.core.serialization.ClassWhitelist +import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializedBytes import net.corda.serialization.internal.amqp.* import net.corda.serialization.internal.amqp.testutils.deserializeAndReturnEnvelope @@ -83,11 +84,11 @@ open class AmqpCarpenterBase(whitelist: ClassWhitelist) { else -> this } - protected fun RemoteTypeInformation.load(): Class<*> = - typeLoader.load(listOf(this))[typeIdentifier]!!.asClass() + protected fun RemoteTypeInformation.load(context : SerializationContext): Class<*> = + typeLoader.load(listOf(this), context)[typeIdentifier]!!.asClass() - protected fun assertCanLoadAll(vararg types: RemoteTypeInformation) { - assertTrue(typeLoader.load(types.asList()).keys.containsAll(types.map { it.typeIdentifier })) + protected fun assertCanLoadAll(context: SerializationContext, vararg types: RemoteTypeInformation) { + assertTrue(typeLoader.load(types.asList(), context).keys.containsAll(types.map { it.typeIdentifier })) } protected fun Class<*>.new(vararg constructorParams: Any?) = diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/CompositeMemberCompositeSchemaToClassCarpenterTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/CompositeMemberCompositeSchemaToClassCarpenterTests.kt index b1700ae8c2..4b5bbe94b2 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/CompositeMemberCompositeSchemaToClassCarpenterTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/CompositeMemberCompositeSchemaToClassCarpenterTests.kt @@ -2,6 +2,7 @@ package net.corda.serialization.internal.carpenter import net.corda.core.serialization.CordaSerializable import net.corda.serialization.internal.AllWhitelist +import net.corda.serialization.internal.amqp.testutils.testSerializationContext import org.junit.Test import java.io.NotSerializableException import java.util.* @@ -26,7 +27,7 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) { val (_, envelope) = B(A(10), 20).roundTrip() // We load an unknown class, B_mangled, which includes a reference to a known class, A. - assertCanLoadAll(envelope.getMangled()) + assertCanLoadAll(testSerializationContext, envelope.getMangled()) } @Test @@ -41,7 +42,8 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) { // We load an unknown class, B_mangled, which includes a reference to an unknown class, A_mangled. // For this to work, we must include A_mangled in our set of classes to load. - assertCanLoadAll(envelope.getMangled().mangle(), envelope.getMangled()) + assertCanLoadAll(testSerializationContext, + envelope.getMangled().mangle(), envelope.getMangled()) } @Test @@ -56,7 +58,8 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) { // We load an unknown class, B_mangled, which includes a reference to an unknown class, A_mangled. // This will fail, because A_mangled is not included in our set of classes to load. - assertFailsWith { assertCanLoadAll(envelope.getMangled().mangle()) } + assertFailsWith { assertCanLoadAll(testSerializationContext, + envelope.getMangled().mangle()) } } // See https://github.com/corda/corda/issues/4107 @@ -71,7 +74,7 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) { val uuid = UUID.randomUUID() val(_, envelope) = IOUStateData(10, uuid, "new value").roundTrip() - val recarpented = envelope.getMangled().load() + val recarpented = envelope.getMangled().load(testSerializationContext) val instance = recarpented.new(null, uuid, 10) assertEquals(uuid, instance.get("ref")) } @@ -90,7 +93,7 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) { "java.util.Map", mangledMap.prettyPrint(false)) - assertCanLoadAll(infoForD, mangledMap, mangledC) + assertCanLoadAll(testSerializationContext, infoForD, mangledMap, mangledC) } @Test @@ -104,6 +107,6 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) { val mangledNotAMap = envelope.typeInformationFor>().mangle() val mangledC = envelope.getMangled() - assertCanLoadAll(infoForD, mangledNotAMap, mangledC) + assertCanLoadAll(testSerializationContext, infoForD, mangledNotAMap, mangledC) } } diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/InheritanceSchemaToClassCarpenterTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/InheritanceSchemaToClassCarpenterTests.kt index 336471c9ac..d066315340 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/InheritanceSchemaToClassCarpenterTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/InheritanceSchemaToClassCarpenterTests.kt @@ -2,6 +2,7 @@ package net.corda.serialization.internal.carpenter import net.corda.core.serialization.CordaSerializable import net.corda.serialization.internal.AllWhitelist +import net.corda.serialization.internal.amqp.testutils.testSerializationContext import org.junit.Test import kotlin.test.* import java.io.NotSerializableException @@ -41,7 +42,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) { val (_, env) = A(20).roundTrip() val mangledA = env.getMangled() - val carpentedA = mangledA.load() + val carpentedA = mangledA.load(testSerializationContext) val carpentedInstance = carpentedA.new(20) assertEquals(20, carpentedInstance.get("j")) @@ -52,10 +53,11 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) { @Test fun interfaceParent2() { + @Suppress("UNUSED") class A(override val j: Int, val jj: Int) : J val (_, env) = A(23, 42).roundTrip() - val carpentedA = env.getMangled().load() + val carpentedA = env.getMangled().load(testSerializationContext) val carpetedInstance = carpentedA.constructors[0].newInstance(23, 42) assertEquals(23, carpetedInstance.get("j")) @@ -70,7 +72,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) { class A(override val i: Int, override val ii: Int) : I, II val (_, env) = A(23, 42).roundTrip() - val carpentedA = env.getMangled().load() + val carpentedA = env.getMangled().load(testSerializationContext) val carpetedInstance = carpentedA.constructors[0].newInstance(23, 42) assertEquals(23, carpetedInstance.get("i")) @@ -88,7 +90,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) { class A(override val i: Int, override val iii: Int) : III val (_, env) = A(23, 42).roundTrip() - val carpentedA = env.getMangled().load() + val carpentedA = env.getMangled().load(testSerializationContext) val carpetedInstance = carpentedA.constructors[0].newInstance(23, 42) assertEquals(23, carpetedInstance.get("i")) @@ -108,8 +110,8 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) { class B(override val i: I, override val iiii: Int) : IIII val (_, env) = B(A(23), 42).roundTrip() - val carpentedA = env.getMangled().load() - val carpentedB = env.getMangled().load() + val carpentedA = env.getMangled().load(testSerializationContext) + val carpentedB = env.getMangled().load(testSerializationContext) val carpentedAInstance = carpentedA.new(23) val carpentedBInstance = carpentedB.new(carpentedAInstance, 42) @@ -127,7 +129,9 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) { // if we remove the nested interface we should get an error as it's impossible // to have a concrete class loaded without having access to all of it's elements - assertFailsWith { assertCanLoadAll(env.getMangled().mangle()) } + assertFailsWith { assertCanLoadAll( + testSerializationContext, + env.getMangled().mangle()) } } @Test @@ -137,7 +141,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) { val (_, env) = A(23).roundTrip() // This time around we will succeed, because the mangled I is included in the type information to be loaded. - assertCanLoadAll(env.getMangled().mangle(), env.getMangled()) + assertCanLoadAll(testSerializationContext, env.getMangled().mangle(), env.getMangled()) } @Test @@ -146,6 +150,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) { val (_, env) = A(23, 42).roundTrip() assertCanLoadAll( + testSerializationContext, env.getMangled().mangle().mangle(), env.getMangled(), env.getMangled() @@ -158,6 +163,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) { val (_, env) = A(23, 42).roundTrip() assertCanLoadAll( + testSerializationContext, env.getMangled().mangle().mangle(), env.getMangled(), env.getMangled().mangle() diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt index f5848a8f3b..7174c6a04a 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt @@ -3,6 +3,7 @@ package net.corda.serialization.internal.carpenter import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializableCalculatedProperty import net.corda.serialization.internal.AllWhitelist +import net.corda.serialization.internal.amqp.testutils.testSerializationContext import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals @@ -15,7 +16,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhi data class A(val a: Int, val b: Long) val (_, env) = A(23, 42).roundTrip() - val carpentedInstance = env.getMangled().load().new(23, 42) + val carpentedInstance = env.getMangled().load(testSerializationContext).new(23, 42) assertEquals(23, carpentedInstance.get("a")) assertEquals(42L, carpentedInstance.get("b")) @@ -27,7 +28,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhi data class A(val a: Int, val b: String) val (_, env) = A(23, "skidoo").roundTrip() - val carpentedInstance = env.getMangled().load().new(23, "skidoo") + val carpentedInstance = env.getMangled().load(testSerializationContext).new(23, "skidoo") assertEquals(23, carpentedInstance.get("a")) assertEquals("skidoo", carpentedInstance.get("b")) @@ -57,7 +58,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhi squared: String """.trimIndent(), remoteTypeInformation.prettyPrint()) - val pinochio = remoteTypeInformation.mangle().load() + val pinochio = remoteTypeInformation.mangle().load(testSerializationContext) assertNotEquals(pinochio.name, C::class.java.name) assertNotEquals(pinochio, C::class.java) @@ -78,7 +79,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhi val (_, env) = C(5).roundTrip() - val pinochio = env.getMangled().load() + val pinochio = env.getMangled().load(testSerializationContext) val p = pinochio.new(5) assertEquals(5, p.get("doubled")) diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/model/ClassCarpentingTypeLoaderTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/model/ClassCarpentingTypeLoaderTests.kt index f7b942ccd1..683241c117 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/model/ClassCarpentingTypeLoaderTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/model/ClassCarpentingTypeLoaderTests.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.google.common.reflect.TypeToken import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.amqp.asClass +import net.corda.serialization.internal.amqp.testutils.testSerializationContext import net.corda.serialization.internal.carpenter.ClassCarpenterImpl import org.junit.Test import java.lang.reflect.Type @@ -12,8 +13,8 @@ import kotlin.test.assertEquals class ClassCarpentingTypeLoaderTests { val carpenter = ClassCarpenterImpl(AllWhitelist) - val remoteTypeCarpenter = SchemaBuildingRemoteTypeCarpenter(carpenter) - val typeLoader = ClassCarpentingTypeLoader(remoteTypeCarpenter, carpenter.classloader) + private val remoteTypeCarpenter = SchemaBuildingRemoteTypeCarpenter(carpenter) + private val typeLoader = ClassCarpentingTypeLoader(remoteTypeCarpenter, carpenter.classloader) @Test fun `carpent some related classes`() { @@ -44,7 +45,9 @@ class ClassCarpentingTypeLoaderTests { "previousAddresses" to listOfAddresses.mandatory ), emptyList(), emptyList()) - val types = typeLoader.load(listOf(personInformation, addressInformation, listOfAddresses)) + val types = typeLoader.load(listOf(personInformation, addressInformation, listOfAddresses), + testSerializationContext) + val addressType = types[addressInformation.typeIdentifier]!! val personType = types[personInformation.typeIdentifier]!! From fae74eecdefc7e46f9f39d10e5dbfeecc8056598 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 5 Mar 2019 08:49:23 +0000 Subject: [PATCH 057/159] CORDA-2694: Prevent Node Explorer from crashing should it receive unknown transaction objects. (#4842) * CORDA-2694: Prevent Node Explorer from crashing should it receive unknown transaction objects. Also ensure that LazyMappedList can only handle TransactionDeserialisationExceptions. * CORDA-2694: Add unit tests for eager LazyMappedList behaviour. * CORDA-2694: Hide LazyMappedList from the client:jfx module. * CORDA-2694: Create an unknown transaction state that has the correct notary. --- .../client/jfx/model/NodeMonitorModel.kt | 8 ++--- .../client/jfx/model/TransactionDataModel.kt | 33 ++++++++++++++++-- .../corda/core/internal/ConstraintsUtils.kt | 1 - .../net/corda/core/internal/InternalUtils.kt | 21 ++++++++++++ .../core/transactions/WireTransaction.kt | 1 - .../net/corda/core/utilities/KotlinUtils.kt | 3 -- .../core/utilities/LazyMappedListTest.kt | 34 +++++++++++++++++++ 7 files changed, 89 insertions(+), 12 deletions(-) diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt index 95caf1bb17..389ebc6194 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt @@ -119,19 +119,19 @@ class NodeMonitorModel : AutoCloseable { }.toSet() val consumedStates = statesSnapshot.states.toSet() - unconsumedStates val initialVaultUpdate = Vault.Update(consumedStates, unconsumedStates, references = emptySet()) - vaultUpdates.startWith(initialVaultUpdate).subscribe({ vaultUpdatesSubject.onNext(it) }, {}) + vaultUpdates.startWith(initialVaultUpdate).subscribe(vaultUpdatesSubject::onNext, {}) // Transactions val (transactions, newTransactions) = proxy.internalVerifiedTransactionsFeed() - newTransactions.startWith(transactions).subscribe({ transactionsSubject.onNext(it) }, {}) + newTransactions.startWith(transactions).subscribe(transactionsSubject::onNext, {}) // SM -> TX mapping val (smTxMappings, futureSmTxMappings) = proxy.stateMachineRecordedTransactionMappingFeed() - futureSmTxMappings.startWith(smTxMappings).subscribe({ stateMachineTransactionMappingSubject.onNext(it) }, {}) + futureSmTxMappings.startWith(smTxMappings).subscribe(stateMachineTransactionMappingSubject::onNext, {}) // Parties on network val (parties, futurePartyUpdate) = proxy.networkMapFeed() - futurePartyUpdate.startWith(parties.map { MapChange.Added(it) }).subscribe({ networkMapSubject.onNext(it) }, {}) + futurePartyUpdate.startWith(parties.map(MapChange::Added)).subscribe(networkMapSubject::onNext, {}) } } diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TransactionDataModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TransactionDataModel.kt index 7d208748c8..7c16ab4425 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TransactionDataModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TransactionDataModel.kt @@ -5,11 +5,24 @@ import net.corda.client.jfx.utils.distinctBy import net.corda.client.jfx.utils.lift import net.corda.client.jfx.utils.map import net.corda.client.jfx.utils.recordInSequence -import net.corda.core.contracts.ContractState -import net.corda.core.contracts.StateAndRef -import net.corda.core.contracts.StateRef +import net.corda.core.contracts.* +import net.corda.core.crypto.entropyToKeyPair +import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party +import net.corda.core.internal.eagerDeserialise +import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.WireTransaction +import java.math.BigInteger.ZERO + +private class Unknown : Contract { + override fun verify(tx: LedgerTransaction) = throw UnsupportedOperationException() + + object State : ContractState { + override val participants: List = emptyList() + } +} /** * [PartiallyResolvedTransaction] holds a [SignedTransaction] that has zero or more inputs resolved. The intent is @@ -41,10 +54,24 @@ data class PartiallyResolvedTransaction( } companion object { + private val DUMMY_NOTARY = Party(CordaX500Name("Dummy Notary", "Nowhere", "ZZ"), entropyToKeyPair(ZERO).public) + fun fromSignedTransaction( transaction: SignedTransaction, inputTransactions: Map ): PartiallyResolvedTransaction { + /** + * Forcibly deserialize our transaction outputs up-front. + * Replace any [TransactionState] objects that fail to + * deserialize with a dummy transaction state that uses + * the transaction's notary. + */ + val unknownTransactionState = TransactionState( + data = Unknown.State, + contract = Unknown::class.java.name, + notary = transaction.notary ?: DUMMY_NOTARY + ) + transaction.coreTransaction.outputs.eagerDeserialise { _, _ -> unknownTransactionState } return PartiallyResolvedTransaction( transaction = transaction, inputs = transaction.inputs.map { stateRef -> diff --git a/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt b/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt index 319771a7cb..0553184a27 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt @@ -1,7 +1,6 @@ package net.corda.core.internal import net.corda.core.contracts.* -import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.keys import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.utilities.loggerFor diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index a2ceee9210..65184d6547 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -524,6 +524,7 @@ fun MutableSet.toSynchronised(): MutableSet = Collections.synchronized /** * List implementation that applies the expensive [transform] function only when the element is accessed and caches calculated values. * Size is very cheap as it doesn't call [transform]. + * Used internally by [net.corda.core.transactions.TraversableTransaction]. */ class LazyMappedList(val originalList: List, val transform: (T, Int) -> U) : AbstractList() { private val partialResolvedList = MutableList(originalList.size) { null } @@ -532,6 +533,15 @@ class LazyMappedList(val originalList: List, val transform: (T, Int) -> return partialResolvedList[index] ?: transform(originalList[index], index).also { computed -> partialResolvedList[index] = computed } } + internal fun eager(onError: (TransactionDeserialisationException, Int) -> U?) { + for (i in 0 until size) { + try { + get(i) + } catch (ex: TransactionDeserialisationException) { + partialResolvedList[i] = onError(ex, i) + } + } + } } /** @@ -540,6 +550,17 @@ class LazyMappedList(val originalList: List, val transform: (T, Int) -> */ fun List.lazyMapped(transform: (T, Int) -> U): List = LazyMappedList(this, transform) +/** + * Iterate over a [LazyMappedList], forcing it to transform all of its elements immediately. + * This transformation is assumed to be "deserialisation". Does nothing for any other kind of [List]. + * WARNING: Any changes made to the [LazyMappedList] contents are PERMANENT! + */ +fun List.eagerDeserialise(onError: (TransactionDeserialisationException, Int) -> T? = { ex, _ -> throw ex }) { + if (this is LazyMappedList<*, T>) { + eager(onError) + } +} + private const val MAX_SIZE = 100 private val warnings = Collections.newSetFromMap(createSimpleCache(MAX_SIZE)).toSynchronised() diff --git a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt index b13f8ec6b1..f27444c498 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt @@ -9,7 +9,6 @@ import net.corda.core.contracts.ComponentGroupEnum.OUTPUTS_GROUP import net.corda.core.crypto.* import net.corda.core.identity.Party import net.corda.core.internal.* -import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION import net.corda.core.node.NetworkParameters import net.corda.core.node.ServiceHub import net.corda.core.node.ServicesForResolution diff --git a/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt b/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt index c21bd2d271..60e55b0745 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt @@ -4,15 +4,12 @@ package net.corda.core.utilities import net.corda.core.DeleteForDJVM import net.corda.core.KeepForDJVM -import net.corda.core.internal.LazyMappedList import net.corda.core.internal.concurrent.get -import net.corda.core.internal.createSimpleCache import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.CordaSerializable import org.slf4j.Logger import org.slf4j.LoggerFactory import java.time.Duration -import java.util.* import java.util.concurrent.ExecutionException import java.util.concurrent.Future import kotlin.reflect.KProperty diff --git a/core/src/test/kotlin/net/corda/core/utilities/LazyMappedListTest.kt b/core/src/test/kotlin/net/corda/core/utilities/LazyMappedListTest.kt index 1cd47f542d..c1dc7cf1cb 100644 --- a/core/src/test/kotlin/net/corda/core/utilities/LazyMappedListTest.kt +++ b/core/src/test/kotlin/net/corda/core/utilities/LazyMappedListTest.kt @@ -1,11 +1,20 @@ package net.corda.core.utilities +import net.corda.core.contracts.ComponentGroupEnum.* import net.corda.core.internal.lazyMapped +import net.corda.core.internal.TransactionDeserialisationException +import net.corda.core.internal.eagerDeserialise +import net.corda.core.serialization.MissingAttachmentsException +import org.junit.Rule import org.junit.Test +import org.junit.rules.ExpectedException import kotlin.test.assertEquals class LazyMappedListTest { + @get:Rule + val exception: ExpectedException = ExpectedException.none() + @Test fun `LazyMappedList works`() { val originalList = (1 until 10).toList() @@ -33,4 +42,29 @@ class LazyMappedListTest { assertEquals(1, callCounter) } + @Test + fun testMissingAttachments() { + exception.expect(MissingAttachmentsException::class.java) + exception.expectMessage("Uncatchable!") + + val lazyList = (0 until 5).toList().lazyMapped { _, _ -> + throw MissingAttachmentsException(emptyList(), "Uncatchable!") + } + + lazyList.eagerDeserialise { _, _ -> -999 } + } + + @Test + fun testDeserialisationExceptions() { + val lazyList = (0 until 5).toList().lazyMapped { _, index -> + throw TransactionDeserialisationException( + OUTPUTS_GROUP, index, IllegalStateException("Catch this!")) + } + + lazyList.eagerDeserialise { _, _ -> -999 } + assertEquals(5, lazyList.size) + lazyList.forEachIndexed { idx, item -> + assertEquals(-999, item, "Item[$idx] mismatch") + } + } } From f2b21bea5a003560e40de1e3e3030c45538fa71e Mon Sep 17 00:00:00 2001 From: Thomas Schroeter Date: Tue, 5 Mar 2019 11:29:26 +0000 Subject: [PATCH 058/159] [CORDA-2698] Allow theoretical page size of `Integer.MAX_VALUE` (#4845) * [ENT-3213] Allow theoretical page size of `Integer.MAX_VALUE` * Fix vault query test --- .../net/corda/node/services/vault/NodeVaultService.kt | 9 ++++++++- .../corda/node/services/vault/NodeVaultServiceTest.kt | 11 +++++++++++ .../net/corda/node/services/vault/VaultQueryTests.kt | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index 853b3ad319..ea6995e323 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -515,7 +515,14 @@ class NodeVaultService( } @Throws(VaultQueryException::class) - private fun _queryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class, skipPagingChecks: Boolean): Vault.Page { + private fun _queryBy(criteria: QueryCriteria, paging_: PageSpecification, sorting: Sort, contractStateType: Class, skipPagingChecks: Boolean): Vault.Page { + // We decrement by one if the client requests MAX_PAGE_SIZE, assuming they can not notice this because they don't have enough memory + // to request `MAX_PAGE_SIZE` states at once. + val paging = if (paging_.pageSize == Integer.MAX_VALUE) { + paging_.copy(pageSize = Integer.MAX_VALUE - 1) + } else { + paging_ + } log.debug { "Vault Query for contract type: $contractStateType, criteria: $criteria, pagination: $paging, sorting: $sorting" } return database.transaction { // calculate total results where a page specification has been defined diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index afa9e23613..a9c50a3ede 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -169,6 +169,17 @@ class NodeVaultServiceTest { } } + @Test + fun `can query with page size max-integer`() { + database.transaction { + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 3, DUMMY_CASH_ISSUER) + } + database.transaction { + val w1 = vaultService.queryBy(PageSpecification(pageNumber = 1, pageSize = Integer.MAX_VALUE)).states + assertThat(w1).hasSize(3) + } + } + @Test fun `states not local to instance`() { database.transaction { diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 94c0f56294..29d10e9a16 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -1541,7 +1541,7 @@ abstract class VaultQueryTestsBase : VaultQueryParties { database.transaction { vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 100, DUMMY_CASH_ISSUER) @Suppress("EXPECTED_CONDITION") - val pagingSpec = PageSpecification(DEFAULT_PAGE_NUM, @Suppress("INTEGER_OVERFLOW") MAX_PAGE_SIZE + 1) // overflow = -2147483648 + val pagingSpec = PageSpecification(DEFAULT_PAGE_NUM, @Suppress("INTEGER_OVERFLOW") Integer.MAX_VALUE + 1) // overflow = -2147483648 val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) vaultService.queryBy(criteria, paging = pagingSpec) } From 8f4534027675c5917fb1d614d49fa2f1fc279527 Mon Sep 17 00:00:00 2001 From: Katarzyna Streich Date: Tue, 5 Mar 2019 13:15:26 +0000 Subject: [PATCH 059/159] CORDA-2706: Change logging level from warn to info (#4847) CORDA-2706: Change logging level from warn to info --- .../services/statemachine/SingleThreadedStateMachineManager.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt index aeb12194e0..92844f5eb5 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt @@ -453,7 +453,8 @@ class SingleThreadedStateMachineManager( "unknown session $recipientId, discarding..." } } else { - logger.warn("Cannot find flow corresponding to session ID $recipientId.") + // It happens when flows restart and the old sessions messages still arrive from a peer. + logger.info("Cannot find flow corresponding to session ID $recipientId.") } } else { val flow = mutex.locked { flows[flowId] } From 1337c35ee65d4c36dcd3cbfc73340fb666914e70 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Tue, 5 Mar 2019 13:25:04 +0000 Subject: [PATCH 060/159] ENT-3219 Handle more exceptions for addMissingDependencies (#4839) CORDA-2692 Addressed code review comments. CORDA-2692 Address code review comments CORDA-2692 Address code review comments --- .../net/corda/core/internal/CordaUtils.kt | 9 +-- .../core/transactions/SignedTransaction.kt | 62 ++++++++++++++----- .../core/transactions/TransactionBuilder.kt | 49 ++++++++++----- 3 files changed, 84 insertions(+), 36 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt index 2291f5e6b9..e802ecdb06 100644 --- a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt @@ -1,6 +1,7 @@ package net.corda.core.internal import net.corda.core.DeleteForDJVM +import net.corda.core.contracts.Attachment import net.corda.core.contracts.ContractAttachment import net.corda.core.contracts.ContractClassName import net.corda.core.flows.DataVendingFlow @@ -109,15 +110,15 @@ fun noPackageOverlap(packages: Collection): Boolean { } /** - * Scans trusted (installed locally) contract attachments to find all that contain the [className]. + * Scans trusted (installed locally) attachments to find all that contain the [className]. * This is required as a workaround until explicit cordapp dependencies are implemented. * DO NOT USE IN CLIENT code. * - * @return the contract attachments with the highest version. + * @return the attachments with the highest version. * * TODO: Should throw when the class is found in multiple contract attachments (not different versions). */ -fun AttachmentStorage.internalFindTrustedAttachmentForClass(className: String): ContractAttachment?{ +fun AttachmentStorage.internalFindTrustedAttachmentForClass(className: String): Attachment? { val allTrusted = queryAttachments( AttachmentQueryCriteria.AttachmentsQueryCriteria().withUploader(Builder.`in`(TRUSTED_UPLOADERS)), AttachmentSort(listOf(AttachmentSort.AttachmentSortColumn(AttachmentSort.AttachmentSortAttribute.VERSION, Sort.Direction.DESC)))) @@ -125,7 +126,7 @@ fun AttachmentStorage.internalFindTrustedAttachmentForClass(className: String): // TODO - add caching if performance is affected. for (attId in allTrusted) { val attch = openAttachment(attId)!! - if (attch is ContractAttachment && attch.openAsJAR().use { hasFile(it, "$className.class") }) return attch + if (attch.openAsJAR().use { hasFile(it, "$className.class") }) return attch } return null } diff --git a/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt index cf92239e2d..6b5be2392a 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt @@ -7,6 +7,7 @@ import net.corda.core.KeepForDJVM import net.corda.core.contracts.* import net.corda.core.crypto.* import net.corda.core.identity.Party +import net.corda.core.internal.TransactionDeserialisationException import net.corda.core.internal.TransactionVerifierServiceInternal import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.internalFindTrustedAttachmentForClass @@ -18,6 +19,7 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.contextLogger import net.corda.core.utilities.getOrThrow +import java.io.NotSerializableException import java.security.KeyPair import java.security.PublicKey import java.security.SignatureException @@ -226,27 +228,53 @@ data class SignedTransaction(val txBits: SerializedBytes, // TODO: allow non-blocking verification. services.transactionVerifierService.verify(ltx).getOrThrow() } catch (e: NoClassDefFoundError) { - // Transactions created before Corda 4 can be missing dependencies on other cordapps. - // This code attempts to find the missing dependency in the attachment storage among the trusted contract attachments. - // When it finds one, it instructs the verifier to use it to create the transaction classloader. - // TODO - add check that transaction was created before Corda 4. - - // TODO - should this be a [TransactionVerificationException]? - val missingClass = requireNotNull(e.message) { "Transaction $ltx is incorrectly formed." } - - val attachment = requireNotNull(services.attachments.internalFindTrustedAttachmentForClass(missingClass)) { - "Transaction $ltx is incorrectly formed. Could not find local dependency for class: $missingClass." + if (e.message != null) { + verifyWithExtraDependency(e.message!!, ltx, services, e) + } else { + throw e + } + } catch (e: NotSerializableException) { + if (e.cause is ClassNotFoundException && e.cause!!.message != null) { + verifyWithExtraDependency(e.cause!!.message!!.replace(".", "/"), ltx, services, e) + } else { + throw e + } + } catch (e: TransactionDeserialisationException) { + if (e.cause is NotSerializableException && e.cause.cause is ClassNotFoundException && e.cause.cause!!.message != null) { + verifyWithExtraDependency(e.cause.cause!!.message!!.replace(".", "/"), ltx, services, e) + } else { + throw e } - - log.warn("""Detected that transaction ${this.id} does not contain all cordapp dependencies. - |This may be the result of a bug in a previous version of Corda. - |Attempting to verify using the additional dependency: $attachment. - |Please check with the originator that this is a valid transaction.""".trimMargin()) - - (services.transactionVerifierService as TransactionVerifierServiceInternal).verify(ltx, listOf(attachment)).getOrThrow() } } + // Transactions created before Corda 4 can be missing dependencies on other CorDapps. + // This code attempts to find the missing dependency in the attachment storage among the trusted attachments. + // When it finds one, it instructs the verifier to use it to create the transaction classloader. + private fun verifyWithExtraDependency(missingClass: String, ltx: LedgerTransaction, services: ServiceHub, exception: Throwable) { + // If that transaction was created with and after Corda 4 then just fail. + // The lenient dependency verification is only supported for Corda 3 transactions. + // To detect if the transaction was created before Corda 4 we check if the transaction has the NetworkParameters component group. + if (this.networkParametersHash != null) { + throw exception + } + + val attachment = requireNotNull(services.attachments.internalFindTrustedAttachmentForClass(missingClass)) { + """Transaction $ltx is incorrectly formed. Most likely it was created during version 3 of Corda when the verification logic was more lenient. + |Attempted to find local dependency for class: $missingClass, but could not find one. + |If you wish to verify this transaction, please contact the originator of the transaction and install the provided missing JAR. + |You can install it using the RPC command: `uploadAttachment` without restarting the node. + |""".trimMargin() + } + + log.warn("""Detected that transaction ${this.id} does not contain all cordapp dependencies. + |This may be the result of a bug in a previous version of Corda. + |Attempting to verify using the additional trusted dependency: $attachment for class $missingClass. + |Please check with the originator that this is a valid transaction.""".trimMargin()) + + (services.transactionVerifierService as TransactionVerifierServiceInternal).verify(ltx, listOf(attachment)).getOrThrow() + } + /** * Resolves the underlying base transaction and then returns it, handling any special case transactions such as * [NotaryChangeWireTransaction]. diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index 2eb82bac9d..2551fade8b 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -7,7 +7,6 @@ import net.corda.core.contracts.* import net.corda.core.crypto.* import net.corda.core.identity.Party import net.corda.core.internal.* -import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION import net.corda.core.internal.cordapp.CordappResolver import net.corda.core.node.NetworkParameters import net.corda.core.node.ServiceHub @@ -18,6 +17,7 @@ import net.corda.core.node.services.KeyManagementService import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationFactory import net.corda.core.utilities.contextLogger +import java.io.NotSerializableException import java.security.PublicKey import java.time.Duration import java.time.Instant @@ -172,21 +172,25 @@ open class TransactionBuilder( try { wireTx.toLedgerTransaction(services).verify() } catch (e: NoClassDefFoundError) { - val missingClass = e.message - requireNotNull(missingClass) { "Transaction is incorrectly formed." } - - val attachment = services.attachments.internalFindTrustedAttachmentForClass(missingClass!!) - ?: throw IllegalArgumentException("Attempted to find dependent attachment for class $missingClass, but could not find a suitable candidate.") - - log.warnOnce("""The transaction currently built is missing an attachment for class: $missingClass. - Automatically attaching contract dependency $attachment. - It is strongly recommended to check that this is the desired attachment, and to manually add it to the transaction builder. - """.trimIndent()) - - addAttachment(attachment.id) + val missingClass = e.message ?: throw e + addMissingAttachment(missingClass, services) return true - // Ignore these exceptions as they will break unit tests. - // The point here is only to detect missing dependencies. The other exceptions are irrelevant. + } catch (e: TransactionDeserialisationException) { + if (e.cause is NotSerializableException && e.cause.cause is ClassNotFoundException) { + val missingClass = e.cause.cause!!.message ?: throw e + addMissingAttachment(missingClass.replace(".", "/"), services) + return true + } + return false + } catch (e: NotSerializableException) { + if (e.cause is ClassNotFoundException) { + val missingClass = e.cause!!.message ?: throw e + addMissingAttachment(missingClass.replace(".", "/"), services) + return true + } + return false + // Ignore these exceptions as they will break unit tests. + // The point here is only to detect missing dependencies. The other exceptions are irrelevant. } catch (tve: TransactionVerificationException) { } catch (tre: TransactionResolutionException) { } catch (ise: IllegalStateException) { @@ -195,6 +199,21 @@ open class TransactionBuilder( return false } + private fun addMissingAttachment(missingClass: String, services: ServicesForResolution) { + val attachment = services.attachments.internalFindTrustedAttachmentForClass(missingClass) + ?: throw IllegalArgumentException("""The transaction currently built is missing an attachment for class: $missingClass. + Attempted to find a suitable attachment but could not find any in the storage. + Please contact the developer of the CorDapp for further instructions. + """.trimIndent()) + + log.warnOnce("""The transaction currently built is missing an attachment for class: $missingClass. + Automatically attaching contract dependency $attachment. + Please contact the developer of the CorDapp and install the latest version, as this approach might be insecure. + """.trimIndent()) + + addAttachment(attachment.id) + } + /** * This method is responsible for selecting the contract versions to be used for the current transaction and resolve the output state [AutomaticPlaceholderConstraint]s. * The contract attachments are used to create a deterministic Classloader to deserialise the transaction and to run the contract verification. From 9ab4a3e24cd0dec94630b70c20440aaae24caa97 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Tue, 5 Mar 2019 13:26:06 +0000 Subject: [PATCH 061/159] ENT-3141 - Improve jar verification. (#4841) ENT-3141 Address code review comments --- .../core/internal/JarSignatureCollector.kt | 8 ++- .../persistence/NodeAttachmentService.kt | 57 +++++++++++++----- .../persistence/NodeAttachmentServiceTest.kt | 49 +++++++++++++++ .../resources/changed-file-signed-jar.jar | Bin 0 -> 177846 bytes .../test/resources/extra-files-signed-jar.jar | Bin 0 -> 180524 bytes node/src/test/resources/legal-signed-jar.jar | Bin 0 -> 179581 bytes .../resources/removed-files-signed-jar.jar | Bin 0 -> 10468 bytes 7 files changed, 96 insertions(+), 18 deletions(-) create mode 100644 node/src/test/resources/changed-file-signed-jar.jar create mode 100644 node/src/test/resources/extra-files-signed-jar.jar create mode 100644 node/src/test/resources/legal-signed-jar.jar create mode 100644 node/src/test/resources/removed-files-signed-jar.jar diff --git a/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt b/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt index 202980b907..38435916d5 100644 --- a/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt +++ b/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt @@ -19,6 +19,11 @@ object JarSignatureCollector { */ private val unsignableEntryName = "META-INF/(?:(?:.*[.](?:SF|DSA|RSA|EC)|SIG-.*)|INDEX\\.LIST)".toRegex() + /** + * @return if the [entry] [JarEntry] can be signed. + */ + fun isNotSignable(entry: JarEntry): Boolean = entry.isDirectory || unsignableEntryName.matches(entry.name) + /** * Returns an ordered list of every [PublicKey] which has signed every signable item in the given [JarInputStream]. * @@ -57,8 +62,7 @@ object JarSignatureCollector { private val JarInputStream.fileSignerSets: List>> get() = entries.thatAreSignable.shreddedFrom(this).toFileSignerSet().toList() - private val Sequence.thatAreSignable: Sequence get() = - filterNot { entry -> entry.isDirectory || unsignableEntryName.matches(entry.name) } + private val Sequence.thatAreSignable: Sequence get() = filterNot { isNotSignable(it) } private fun Sequence.shreddedFrom(jar: JarInputStream): Sequence = map { entry -> val shredder = ByteArray(1024) // can't share or re-use this, as it's used to compute CRCs during shredding diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt index 09b8410312..13296ffd99 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt @@ -41,6 +41,7 @@ import java.nio.file.Paths import java.security.PublicKey import java.time.Instant import java.util.* +import java.util.jar.JarEntry import java.util.jar.JarInputStream import javax.annotation.concurrent.ThreadSafe import javax.persistence.* @@ -71,11 +72,24 @@ class NodeAttachmentService( // Note that JarInputStream won't throw any kind of error at all if the file stream is in fact not // a ZIP! It'll just pretend it's an empty archive, which is kind of stupid but that's how it works. // So we have to check to ensure we found at least one item. - private fun checkIsAValidJAR(stream: InputStream) { + // + // For signed Jars add additional checks to close security holes left by the default jarSigner verifier: + // - All entries listed in the Manifest are in the JAR file. + // - No extra files in the JAR that were not listed in the Manifest. + // Together with the check that all entries need to be signed by the same signers that is performed when the signers are read, + // it should close any possibility of foul play. + internal fun checkIsAValidJAR(stream: InputStream) { val jar = JarInputStream(stream, true) var count = 0 + + // Can be null for not-signed JARs. + val allManifestEntries = jar.manifest?.entries?.keys?.toMutableList() + val extraFilesNotFoundInEntries = mutableListOf() + val manifestHasEntries= allManifestEntries != null && allManifestEntries.isNotEmpty() + while (true) { val cursor = jar.nextJarEntry ?: break + if (manifestHasEntries && !allManifestEntries!!.remove(cursor.name)) extraFilesNotFoundInEntries.add(cursor) val entryPath = Paths.get(cursor.name) // Security check to stop zips trying to escape their rightful place. require(!entryPath.isAbsolute) { "Path $entryPath is absolute" } @@ -83,6 +97,17 @@ class NodeAttachmentService( require(!('\\' in cursor.name || cursor.name == "." || cursor.name == "..")) { "Bad character in $entryPath" } count++ } + + // Only perform these checks if the JAR was signed. + if (manifestHasEntries) { + if (allManifestEntries!!.size > 0) { + throw SecurityException("Signed jar has been tampered with. Files ${allManifestEntries} have been removed.") + } + val extraSignableFiles = extraFilesNotFoundInEntries.filterNot { JarSignatureCollector.isNotSignable(it) } + if (extraSignableFiles.size > 0) { + throw SecurityException("Signed jar has been tampered with. Files ${extraSignableFiles} have been added to the JAR.") + } + } require(count > 0) { "Stream is either empty or not a JAR/ZIP" } } } @@ -311,7 +336,7 @@ class NodeAttachmentService( currentDBSession().find(NodeAttachmentService.DBAttachment::class.java, attachmentId.toString()) != null } - private fun verifyVersionUniquenessForSignedAttachments(contractClassNames: List, contractVersion: Int, signers: List?){ + private fun verifyVersionUniquenessForSignedAttachments(contractClassNames: List, contractVersion: Int, signers: List?) { if (signers != null && signers.isNotEmpty()) { contractClassNames.forEach { val existingContractsImplementations = queryAttachments(AttachmentQueryCriteria.AttachmentsQueryCriteria( @@ -327,20 +352,20 @@ class NodeAttachmentService( } } - private fun increaseDefaultVersionIfWhitelistedAttachment(contractClassNames: List, contractVersionFromFile: Int, attachmentId : AttachmentId) = - if (contractVersionFromFile == DEFAULT_CORDAPP_VERSION) { - val versions = contractClassNames.mapNotNull { servicesForResolution.networkParameters.whitelistedContractImplementations[it]?.indexOf(attachmentId) }.filter { it >= 0 }.map { it + 1 } // +1 as versions starts from 1 not 0 - val max = versions.max() - if (max != null && max > contractVersionFromFile) { - val msg = "Updating version of attachment $attachmentId from '$contractVersionFromFile' to '$max'" - if (versions.toSet().size > 1) - log.warn("Several versions based on whitelistedContractImplementations position are available: ${versions.toSet()}. $msg") - else - log.debug(msg) - max + private fun increaseDefaultVersionIfWhitelistedAttachment(contractClassNames: List, contractVersionFromFile: Int, attachmentId: AttachmentId) = + if (contractVersionFromFile == DEFAULT_CORDAPP_VERSION) { + val versions = contractClassNames.mapNotNull { servicesForResolution.networkParameters.whitelistedContractImplementations[it]?.indexOf(attachmentId) } + .filter { it >= 0 }.map { it + 1 } // +1 as versions starts from 1 not 0 + val max = versions.max() + if (max != null && max > contractVersionFromFile) { + val msg = "Updating version of attachment $attachmentId from '$contractVersionFromFile' to '$max'" + if (versions.toSet().size > 1) + log.warn("Several versions based on whitelistedContractImplementations position are available: ${versions.toSet()}. $msg") + else + log.debug(msg) + max + } else contractVersionFromFile } else contractVersionFromFile - } - else contractVersionFromFile // TODO: PLT-147: The attachment should be randomised to prevent brute force guessing and thus privacy leaks. private fun import(jar: InputStream, uploader: String?, filename: String?): AttachmentId { @@ -495,7 +520,7 @@ class NodeAttachmentService( private fun makeAttachmentIds(it: Map.Entry>, contractClassName: String): Pair { val signed = it.value.filter { it.signers?.isNotEmpty() ?: false }.map { AttachmentId.parse(it.attId) } if (!devMode) - check (signed.size <= 1) //sanity check + check(signed.size <= 1) //sanity check else log.warn("(Dev Mode) Multiple signed attachments ${signed.map { it.toString() }} for contract $contractClassName version '${it.key}'.") val unsigned = it.value.filter { it.signers?.isEmpty() ?: true }.map { AttachmentId.parse(it.attId) } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt index 0762f71199..3740308608 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt @@ -40,11 +40,14 @@ import org.junit.Before import org.junit.Ignore import org.junit.Test import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.net.URL import java.nio.charset.StandardCharsets import java.nio.file.FileAlreadyExistsException import java.nio.file.FileSystem import java.nio.file.Path import java.util.* +import java.util.jar.JarInputStream import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNotEquals @@ -722,6 +725,52 @@ class NodeAttachmentServiceTest { } } + @Test + fun `The strict JAR verification function fails signed JARs with removed or extra files that are valid according to the usual jarsigner`() { + + // Signed jar that has a modified file. + val changedFileJAR = this::class.java.getResource("/changed-file-signed-jar.jar") + + // Signed jar with removed files. + val removedFilesJAR = this::class.java.getResource("/removed-files-signed-jar.jar") + + // Signed jar with extra files. + val extraFilesJAR = this::class.java.getResource("/extra-files-signed-jar.jar") + + // Valid signed jar with all files. + val legalJAR = this::class.java.getResource("/legal-signed-jar.jar") + + fun URL.standardVerifyJar() = JarInputStream(this.openStream(), true).use { jar -> + while (true) { + jar.nextJarEntry ?: break + } + } + + // A compliant signed JAR will pass both the standard and the improved validation. + legalJAR.standardVerifyJar() + NodeAttachmentService.checkIsAValidJAR(legalJAR.openStream()) + + // Signed JAR with removed files passes the non-strict check but fails the strict check. + removedFilesJAR.standardVerifyJar() + assertFailsWith(SecurityException::class) { + NodeAttachmentService.checkIsAValidJAR(removedFilesJAR.openStream()) + } + + // Signed JAR with a changed file fails both the usual and the strict test. + assertFailsWith(SecurityException::class) { + changedFileJAR.standardVerifyJar() + } + assertFailsWith(SecurityException::class) { + NodeAttachmentService.checkIsAValidJAR(changedFileJAR.openStream()) + } + + // Signed JAR with an extra file passes the usual but fails the strict test. + extraFilesJAR.standardVerifyJar() + assertFailsWith(SecurityException::class) { + NodeAttachmentService.checkIsAValidJAR(extraFilesJAR.openStream()) + } + } + // Not the real FetchAttachmentsFlow! private class FetchAttachmentsFlow : FlowLogic() { @Suspendable diff --git a/node/src/test/resources/changed-file-signed-jar.jar b/node/src/test/resources/changed-file-signed-jar.jar new file mode 100644 index 0000000000000000000000000000000000000000..e28514f980030271ea612b8d4200ea29dc5f4919 GIT binary patch literal 177846 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

    FgQdV`@wq0GeZQHi(o8LXNX5H_dv+kUGX8vaW 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 literal 0 HcmV?d00001 diff --git a/node/src/test/resources/extra-files-signed-jar.jar b/node/src/test/resources/extra-files-signed-jar.jar new file mode 100644 index 0000000000000000000000000000000000000000..004687b1b8d25a0308e3c96cf3ff44f2e0745ddd GIT binary patch literal 180524 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

    Dh(d1; znJ_y(9*#>Ny1(prqRQ&EA;E|DIkaSM@{d()fYp7XX!n_1MPlRUm5gGm_`CWHtJ6$% z+;N$0p$FnS)>0d*_uWA;+dB++dcS6xx|)oMPT#%R^N7Bh`FxKnW}zGT!FVSS^NN3r z=KM~#MDZI+N$Ze?+&!K{o1dKX);Me$$L@{1c}2Sg*1P5mb4}f>);}tje^InJ)cWd* z4I#b~!;WfSAEn*e0moc!XpW-{3LU@=nuFnEZ?{Hps)9*{{rGpIWtPL*9o>&lpgP$9 z=tO1yk2Oh(s=hL@D8l#5PZxL~$)CqPfg271IHVp}oG;h|N1So1JsLqhk4&56!FZF_ zt#yUJZh0f{29_zgsqkn=Mfj7A=1l?|-2f>jUY%uKKj(Xz|JUaOJ;2nOnT9Hn(5>XYJa^L6-NDNFmhMyb5t z)V1JOZ0b*CJO{;N40nG$m?apuuJE0~EJK@B#sq)FWM|alUK!oBby&B|TZs3?Q$nVQ zLcP^ZG6RP8X7p7NocwA;!VHbq0z%j+H1M*Rrd5LK_wgdi(VQeX9ZLek-5)j~3v=YE zR2veyGy5CH2h}3@{$xLv}wh!@6lDhL$40L&3k5lkg^L9hb^!F5wSlyN~wJ z?3AMy3YMEpLr2bGy%Uvi$8u;&xCZui<~f4eFzue)T$P3mw^*ON!Cx?x$k;;1*kdkJ zEDOlm{f{1$NY#oH7pR8rKVqaC>g5+^y-S;$(w!N?!C{KZgxhI}^w?(x3|ivAAwbeK zL&IxEXr_%A>r2;R;=6mt5puiJo#ga5R@+gNx;1SsqVf5+7YBk>&6Zkay{jxzz)Nwn zj+B+FEry9(Q}AgvLR1De8Yj<6g!q^ll^meT5^dIZoW8yoZP#NvJR=M(LaGEl1yUAN z;yNx|`9k`}C|K)V|6VaYW>8|ChgP?Uw0ZgNmO--@GjF|Ds^mytPY77%_K)sxJ`@vc zyCOadRu+SIHZ4Yw3?wd_dmur(9fp3xsKkAQwE3?U8z@}}<_J)#r$}A8Y_K9;J4(eB zD8TycY@8IZ_z+EBPlg^Sk{5rvs}s$wC}b1du_8ty<%jaXoSP#EF|)nFZj)>dDvv3R z(L2K_!Zg+8=4R+AIJ*VOokq!8CQjQ@^r2b^WnbGq{>m)`*Rjt4B)Z~`So)MkFOn_GS& z6|%t3p!R{9RFam?B!-3y)Ydg-!e)D%;Kn)q$RX2Y%M;VWUN7kqk3@1W>Xc31DcT1k zje!8y{gaDN)!|d2`3#*fI(MJ`Myi4#qXXX?d1UM*$t1dc$I)MyB?MQi=8UDP*+uFT zP#Xb;@t_#leYyRje3=i5Y77&l!Tn{)Gk6ae%ECAQs&Ggg6UWN8@TyW&n6Q+-ps`T@ z4g627{>qqW*6*K)lK$U1kN*Fr9G3rxC^c$RO4#Bkyu3(@3=jxFK%)cj{cK))35=dn zgrgXNIG8xngwSfV6|`W*xs_)LNNyiujZ#O@iIu~WrU^=(8Ma^KDcr<&Od;`=N(|e+ zH(TO!9c}U#H~!xr(EU)oh8ih@tW&MZ`^VPOPMT;WlE3#&)iZP`y}F1W3YAc2Gs$UH zLQvq`M@HRKyXuj^jBRNAExK5xGr?y%EhW`C5`Rzg)TL4JIDc(lgwk}S(x!B*Yx2$> z&#?&{zFmrjNsst81xjN!O&Ae2Wp->B*4VKZZ;~3xRfLu+WD|{a;7n9PoM~Mrbf_uy zj!9p)i|ZokHT;`%;)4-Q+E;K>8H8X6rP;282X1{B{rj`FsvR|1-KD?_FU`CbhUu$* zENlGbFu;bctoPuE2Oa9jNro^CUmY9VdFpCh>qw(t5}1K-6@jrvF~E|`3Z)$QaVanD zD?TxgA6k`J;e)lE+C@VSl{QSy(H%G4+OS)+#KvAnxbmI_V-u6oRMiY+m}OF4MBn1w zCNBbMoGOt6_61)C(YqN|WPI2IF}GM8fNw6+=NTor-Ck&0;>|pgZyth?0o)zj;{no* z8Qxvsi~|hIiEyeYeK?5QZwfr&;V}YtaxroryNcX7!n`=v#d$HW^)|(VUE*>?#?5Fg zJE~x)qkM(2cS%vuob?9qh<-394nywjA;iHmq$@ko;>qk6MW*KvLIkOPFuMiJ<-9hC zgg)UnzSbCJk*kfxQf9%~qlhF&g1k$PtN1n-Sr#TQTC}CMR_4qZmn9G+Ep9fm3mR_w zkOgig_CiZ}szny);{K6@3cs^ftSGp-n334!pHGDi4LDI^W-Tv|DUWw$i6!xFTsmc8 zlaA&iM8g^gcD4Mn@zaa`Rm^LB-5l30y_3fK>KIA%1A?7@VNTIj%fS`*FB_thuE_m& z>HX~{xz2etd2F>9u5NIrdwHv&mhd;)6>!a*H`I@ZSI~brN9$~#@QSnDPCYzF>>(9p zGsa7jLsX7}VffxHi6To;P+jjXbFYDEiZ$3W8a7(^|KgnRwwxF)O7e=NAI^y&zrfDP z?@B`=lC9oLaBEy6aBZ`Myl5s&}+)gaOsQlJ7oBuyLLD|l%h2Nz$) zD8fAMI^9)Hk$pnOW!Ll%=cLPy*e$f9bjcbYs85qCOWW6^iyrytH?{Ga6<*8d20DJq)w$dU+OUOuv5^p1n5gH9m;t(>Ef`!3^l5Ck-^kVJ|k z$z`o&j5*QX#b>ReJFnR9oqYxg93*%M2af*Bk>?Ffj3R;}SgPtP%j)XtPkrCMe_;&Z z{26E{3_sMegSY%CQPpV$6PY0GF|V9!&Tb;1MApzI3R6lqg(8Bn?@n7-me`>UU<~U~ zvm~hen||MwFcB&8+yn1ehIaFP+?cAjL`SDQlwsaa6mlvt8hYPPGj7_7A#%!LuPe^2Wt;{9zvKkvj$S~7QZu@RhoCda-XEW> zflW~uR@5Fq#4#S<*uwbYu`uJ83950@qq0Q9IGzNjYig9sNXAZiB9c2u4#V*?$JoTo zFB`m#$CeY!mZKL2o~9}I;+|0nCWQmuY?KLo+=m&8qPHjOp||HTAC!zkN+4w9m0&+x zUQ50}x>3o|GMjYPr1XwO%**1s!%qi@T@gz*^cPS zoa}wHJ`~ohWvm3SIV(#K_#E=vwn-s#@i)eLYPAt7+?5SIxV>WdXaSevD0wqbTy+Zp z*ToDWs<^?oN|;nF-O(?oO=u2R%HjpUgfcB&e@4&Z66HILSg%zNb*MV?nL7XzKI?L9 z?-k`VPdkWjb!CZ{qH*VzO{jLPj5qu#D&^5SiDax#)N2QRgI!X4$?Yj1IL z2(xIY8-tXYf8);$Y3JVaZ^9PvX6A(O4vdf8jyq3z!KgChJ9yk&Ci#(Y+=Mwl^F)4d zF*_qnAM!omaoQ_RMb;qb6uQS0%Gnku4RAhl$5ZBB3;`?s=`NIR?*=SY6f*@T#!`); z#!_dkwbc22TcL`@jCIZa&&;jXEY{%vS*G~?&w>xzf6iQ0eRb_0@VO!-NrO&|w<^e| z1uc%ZuotNI57dwnTr6EC9*36C+OpM;QO5^mseE@fkmX% zx}_paf#^uA)|X~>E(jLAUv}OHtjL`O+pt773n-WM}()0{Qa7Bf zg55A}onD_8hXc3UUcBI4GZfp{z`)mGuUjt($vV6&o|H(@NRiGU4Gm7WNHccHF3hUf zSe7wd6`;RhgvN&5W-##7&kXD}!+HInkM7@yAN09AV5s-)IwSOC7(>AA!Fh?*^O_Au zPRiERISIz++*uqbS2jYfmG&;%OZFn;Njp+HtTq@4s!h6}ZGThQ(R7zOd-I`V=1PCL zA4}{_?7IH^G~RIJ^?u+H{w7i{;3}T9s2qD-;mRA_HA==>gZgB{q)Y!7`mlKMlTduTatzq_YdpH1|{a^ z=`a=A<4dGNc28r$DfA6FK>Vw3cx%q>;~`?5S+0uF_)FHV`ma=BF70vHSHHu)dNiuO z;T9IB3&hqXPSScXuJ*={73~qj3<%!XMwJHfhBPG78x`j)qHdXfC@BmC-HyBwt{mMB zJ&OO32H_L8nO}se7jhbJ`S2rQE`ltgm|sL$^TT)mvUO~})J5CyT%xKWI4>&7;SKof zZF-k}K%5e&bNVje(klcs_v!Cfjl+OZa;!WnpVGSyF-_ts#)^jW{V(u;qKqvjYxl)Z zlMo2)-KN=Fh5c;=a3DS!#hY*n=#3h4? zYB<(&NPx4p+nKQgX;U}em8{__Yf^|%>RKaHgeyybVtlKXZA(jyxb@9@eGPB@t~awc zN!Plt!kxOG$?^8?r|*roeQKE8mMi?Pkm~ROWF^=*ejG@OTszw0;18-6Au*9V>`w6o zS1es@D|KLAtKy6{5{6LUn4G68?G(TfWECOa+!kGJ)xL%L>fT;nz?hwh{M3;5zsU*) z9Yz$exEW-vBEq~{>I$f9Slry$nOK=hj;2B;t5_J>#A%ZC%rbMGDnO8>GPo$PQ4Tr# z*;q-j|a)0f>!uWezD|vCw?rc!RoDl_d=q|+=-k%~2(_*2DXiLPoxm)|m!eUCN5SY7;oo8|5edd_fiQxx>)(jKTrq0zRFy< zkV6xn!FhSso7uf!OSY}NUKO$%*{&0b#5It4B})mD@rXWSA@0{L)NsF#V+&0JDy69aK@lo$|wY( z_Y58lz}$zxw2^JhY1aINEh!%gL{d?7X?(?FqubY6w5=;*UQOrY zHBTsYWTQNYEv?te_Scuijl`4hT)h3=STXl+q9TM3R0lf zUvUlKVQx>8tr2XV7HKxli&1H6NlqP+j`uqhR$fKTvV5+t!l@#l}(-{UTnse`@Y^$HgaQ=q_YQ- zMh+1()W1`>q!YDJXBQ=nssBJVnOmf}SG&vTCN$Y3&Rp2!v^0)t=CVw8A9kZQ7c(uK zv^?--uK&TY4}8i!$`L>yLjpmc$w`Ll9rSclDx2XR;qFk?>i8%Im=CPTK2Ax>St@TX zGmRd1;n-&3>2Y24Wk*wW3|UPuqXYy;ono@9AIiygjVC|xP+aP-<8bOony5F=81v3# z=BEz9J|mgSQCD`&ykRQpsr_4oc)fE`s4FXXaU_6$^ZNrOq&Mc~w)mP6%$-fE zQSNRr`S8u!p!km*yd)O|{28$fSK0!yju^jz$EB)7wq8*87v=0uT;uug-|>GOi69ts ze4$7a#Sh|f0F<32${2x=a)4Rn>RPH%5JkLSl$b}9UW}Bn{dVXyjVBMsVaLM&@S)^kE)X2IcC($XtvicDps=v}Q zveDG>m}8T=jo@m*=lB;Q8r^IBjk+0yiIBI-o;7Sm@J9ZW_AlB=NAH|91wvsIKa;oT zKsds($azd<;w}pna=v*5@`h5qpLM(nL9VXjE4-Kx8(D9bC2S!?W*S|KQ?=o-m zTj)H~LO%_w!D}>r;I*x$bg5`P_3-x=h?Ja;E;En+WRM`(K!$hiQ--O@{CII=ag)d4 z)@&?FOTrjSY$vUAOFIpvy~GLtwdZ!^-cgP`v-EjlHF#Tg$JE=e7Y{se#;7;tvOr}& z8*3C0rcLCSH$N|#mz<`vW$m*5r+cMA^>Lx5KhQClPLGP0#6yuhuq2?16g+<%8RWys zo$-?2>DXxQqz!53E?Jd-?S+1>S^pH3PfYuTkg5o?W!SvcsjcxEFV~S5@i}&iU8qy1 zaTwQ-o7ZT@=461RmvP)X-KjOT+$hs=+4mnfnh%?yg zg=FnKP?tWuAXJ`1>xMjujxjhVRn1f44{QzH!VIX>cSOb-Aqw{l9dSS%TwrBSS=$%a z8m0ZA!V_}t(cxbu#UG3m)?f`@5x+CTqH&IKO~D-jWwYVAU35%i362RqhG2_&NhPGn ziY+1Ew8`PN@16348IWxWL&O^-9m{{HWDE{4#`v>>E9~)3 zc-blIL->x}vm3;?Ch*Y%orav({cLwmJwP=*>V3zTmSoR;JX zsuc)10}wvS2o<{@%u2i*&vdgP>HK#S+NJ5qA=ql9Z#C@ux)hE?SngKd7@ly4N(jkwgV_` zjHOJVOB(2<%>2g<0BFpp(w01gWXb~HXh2kL@W}JmEP6+cWeE`;N%q$xu#USQ^3&`u zrnj;8F+Z|`&Q)|oFc zDkS3U@HO89MVvB5~c}E>6j0)n|oGh~Ci`Ayvzezg9?nfsn^8a4)$n6FJyWVEH-0 zkU`NqczS@$Do%?`$u0w;4!8d$*fS;Rv`_w2JaZ6|@Rl->0hI1m{M2*#M}6e+vdt$< z`D>eHGMzfcjL32)`-+^VwM{`eY$GwgNJ%^J?q7go}ffY0{POt7f2n8z<6<`e;+T=q>dj=a&3O=JIBu zd$swQCmnQ)rLaMP=_%*|IxE{pZo=i-M(oPW#h(QH6_ba}9G%Vm*cGim9d>!#rbfB^ zAjAZ&*R+!Swn7Rl2aNQ4-MOFC%l$VhxFxiK^nEvD|oQa&OaayGO);e-k>lmYQPn7Z+ zcWKvqiJyc^7=*JI6U2@U?<;DjUgcU1tvj%*Jjk6f;JO}}H&eO5C_1#}x?Z|-#eKPW z4UIkI>a93b!v%NQDm)~^eRt6*IRwOIzi1Z<6Q1^02&*oVS;k*vlp9OoqH>K!=S{m< zt1+&N{p9K`DH0zW`uE1$(pQiV02}~78tZ?zmCyEn#hMC+PA;B;b|!+h_O5pSshV7) zrY--o)%IoPtBnmD6%imX0W0AXu*PATMHaj&nPgxj0hOFQFTMHv>tV`gd2pJmf*}5t0e?%l#)sWns zs-$+hnbs?f;xk?O+A(=9aFX|R+{vtR>!cW* zaxju*m#$3J*rp&?nNgw2)R8h7PqivKhQ*I%emstn@{4geY7|1l9I8N0;a%LIa?SLF zYZ9sr#OMbHd^A=NArGP+RORJB{RLiz3FO(+U2B~@?(s<^YC3&!u?)$Kla2!Z$SUIemCMPay=anTN$T@k$_~Wb)5(#`rPNM6dLM(;LK?RMIn(ub zrBy}#Pa73T{GSpS_5<OmMop1t3cM|#OI2d+K&8rvC=L^0Og+;z;+IOPU>TLv5* z@(VugqC6D4>PJYra{E1v9disHz0`@bIoZ^dAF;{Qldy0tH=Nam;0Yrz{9#Qnx|%1a za}=dI@v<2NA@`wCdMKmdogyLGEg<+HM7A+1A&#S9ZR~U4!F+`EJ(vh2Sg)5fmPjJ} z6`K}hnn<=)aGAsGaU;D~GwBp|_EI!tBq1oDwXnh6iBOZ1^zg!_xnW{dB~ z=5+*brJqXi_HsSGmzmee8y7G%sw(Gesa(4f46;U zC}ny+Cq(&#im%8r5`3cnFr=M$WdR?oKX!>K-p{$N+qtCxR$R^4@8HQHwQy@243qG5 zYN}8z|7y|8Z+nbQRfVGd$v4LRd1LLA1UoM0U2aTVU{et;0V;EklEa^?qRzBYqh8Ln z>yj4M+ zfMF-s+O!#ey+Z_dKFx9rOL_KGo~@f1zkn+U@a)l=pk`O?<+vtkT2o?di z!D+{lIr^oQPW1xT|<+l2wXQ`nWx#(>Q!{;5?oH@Z847!}JS z{_vieq1;?{93ss^p<7qEY=>Lb;U9*Y@e5ccAkLTQb1OiL%-L6nsVp$7T*Jd;TV&QL zGB3}_?cv*rN~HS=OaukbAhPnv<$X%-4y;GP_YD}QtDF&&kBP~}!sdmIDPUt(xM@#; zr3|7lg2P512e#=;AAnJGNhAF><*dB)wEJ(BB^) zU39c@8slHXWPRtC;$N1&r3r=8W;ox00QDCPYFQy^Znlk(f{Fv z;p$>(<4mur0tx{5&!2#Qh64X*6ab)qErzLWEDTd{s-z6KRu1Uq}7E?AS77r0c~F8=eGZpSSF6 z6`$=654rQ%G9@Q30TMxi-`B4OTWQf{;67Pi?$Qz25~M(?u9p*k5}h{Mycv$V%otWs z+YhtLoYPZPeV_b1T`!(byN4=Ajw8aM)bx8BJ$V;4^?DFho z>jt+sapsPnTWT<@)o=Y|sgmSR_JlgfsYyvZ6(*rnQaeaz_|jZW8i9Mam1^0s)0QIX zbLGtU3bF&XQYKuFk|x-&*Fm*xGE=fc-OoH0(T6ZjNy$+Xjv<;E8l2R-mS>Dz~BwF z&qzX7bY`Q)Yv;dhp`)4aX41V6&B6&m!9n}m^N<-;&tkXW3JFGLIn)#I+jRx zvX1d~4oE&N?-9_~n7|yKQ{irQugcvgky}%nyHS6p;_qV75@=nZf)*73yd|& z0w0CuZ7kbeq9^x!4lhZ;DKeKABOnajvcyC7p|PTg7ac9EJLf=8H9m#PAK?XrmGAkz zkO2(0#EX`r4bEHfs<&leT=v+p-humAog~`_=w2r28$g$pS|HJt0MmS5z>T6XRyn_% z)oAtnB$N8bupp!|n>m6qyIH+tXvVA_yO`$K<+FKG#jwUWGBNFMscODWgby2a3G%y5 z^6za$@EBBlmyex1uqFEf?e%2^N9(djuSTTe%y1WQ2{?83KTE(&-HYhHZ^5{(Q>@V; zKPIC2gsnFhWY1g&!KUuMLF|nBN5YUgfG$&z7ZsB)+Q=W*Z~j(ZV2xjEIFH2z{onQ63%m-s#{-n@Pf^?g>!U zL(B1}>iJ1u8VN#e0juf@An@VgL2ZPEU8Y(|@6WYJd^TIYv@)HhC)9|jFVh4Tn#q;) zEUIa0S>C0+()tZlO`3i!V#Q5yIa<%3BU#hF{gtF^O$%4+EvFjAs)H%NDfG}7HAh>|ZMDcv9- zAl)4j3P^{PbSd2pN{G@(Nr>*ovi{>Q~yEcNVX_RO9=v-iZ|XI5OjCwQn& zZMtjjuy@y?Y=FW~(w%$H+`v%O)`^-s#rk{#H*8}r$%C8BDpETvPt|5zrs4SMLG|aH z%GQt2wQt&Mt#nmiLTTs462Hz%oi%QI6Y3SPGqJrXAtsXCZznMh%L`FWiBZDIP_-=}VHvHA7hxSc z>xf4$rxBW)@lwdKt#jXs2A;cco<5mAk@Ld&!CS1S%q5W@T{Yxr{8H!k@IfW9m3-Y( zCw-=*eXA3c=bRbi!(nD`Y|UvLxQP;aR;h!t<766iogaH zQ-JzF#5C8NESR1ohBEvRRY&{>(Kf}=>qR!14x?eHckT&wTmsaai!Spwif8Z@?(j-? z?yTX~Ox@di|Gah}*QwW5FrECa{_JBHJ8Xe^f{OfC6SR^xOM4IN;Il3LNr}I{)cd6KH8WH_JLgzu(Ev+%@pKb?DuJa@M zZ$4H&R$pTtJF4`T5s5RT|DnYmkWYECC)-ID2pq@q5rfxkc%HR{-T+S?i6>HYuaw=_ zj(u%1Bea#IV}Qn-nTt#_R0*Um=0)O-m~j78uBQFSyu(9l?+8(Mx{LRFO(zXQf~xKz z)WhCm^?-w5GZ%T)okY%N*J|P@jh)2G5PKws*E`S4XoDQ9hYdp~;DoDuS$YRL%$iVm zCX8}6@toNNZyrm`llt|3)|7flQ5j3b2iG|SI!^cmhkHtZ8E#Jrc;}D3mO@cH1>6lswEnJx4V^PZU^adwbG%*N&(9^p2y=T~4bL5R+kbGl#PhK3nLdX((OzsR zIc7W_EKH|r#oJ~(+JP-^Cz}r#I-|5jG0MqhwZ{7-6k5?fa!35Az8}LzX^+uWIihJf z^Eg}a+(M&1&vA%LR-c;;2$XASqiG? z7bt@a8Qi_9R2fc%eO(1ns~VwZH7lEf&>yuD>8SLC`>Gc;yWmfj=p{B1dZMnO3Cim~{d{&SYV<#mo#7)-7)?;t!yC9|8urD%qSL-rdx>|UawpYxdLN_P zsNR3~PSN^-p64y>4*5s#kUm0xZP?~-Rs^>Z@~;tAZ7>j|*&@{7;_fHewXHVqt^%+h8|Fn|qVj~{DKH6+vhog}Tr;tV)fuBN$h(D2!KH@;8 zVJaRn>Fx4Sq;Ms6NR6kYeZ~CD-9LS3+lweWW3XalcZ8UdmWM&O$?r9`Ed9Q(Xf?xr zmM9CIgF0D?D9SVAXT!JYZZBik$z>+Eu6y3z)LQX+_DE$#;~5J5zQ1T7mRp!9!%YF& zwhRrjoj=YXW;Hod-~*wpGxP6aP=a5ExAsP6AS;8v8P?MOR-~ALy_wn{;x7fWVah$$ zm~2>r-*Px&%w>mnWnG#i-s($tgx^*nk0ck3Os2=^?tX&wB*rDDY{XI11A(CmJ4E(Q zX5PjTZTrFC?fCcK8V*yxe;d%#Yl3m{eT#Gmd}XtDH%j`W1x2WOew+~(Cb!is{w-`L z*iL1Bv_SH-+k0f5`~u3u5(s#$#8#E=ooE8H8(O8ltRlD2#k#`YfLsQTGvJl*7oDoB zn`W#UDD9;bl?sZTsgo!o8&woT{r93gobU#ZdP6jP+n`$!`#Hyu70jK zVW1X6w-&mDg z|F+XuktYp5g!&D`@xkENxgV+yD|*jsMQEuMDhxsEcsnW!jWXrCf#S8&wGu3hiS&lM zX;dF`%?64+VZ5C~8A_@f=ciSMHa}v8&qyq+*NC0)mRYEu%tDC}jxCOtdIfM#^F$yT zFD^p$E@CjFDwj($uHj@#Ed8;O^vFE|DEc%cSW1fWTR9D+1Z=hJAkYm z{xFq`?8hZbNz-_!rY42wb@!uxP~dwls2exfo*Vj1Yu<=MeFNVX_+m79djHd>dnlAN z`y02q(?*1cR;^0hMw0l{-YK&1jrW}8DQ3*2!itg)_EVaEoI9K|%{ZKLc0ND*5+VG2 z1^$WT$SZ4_%IG*X%m)Ga$;s*SI*(Xh3zX&Uo(5FjrIDI&2qByuS9pLj^SbE!b2bA0 z)QIhcPi-zV<9&H|-(+tUlwi1Pkm9_xnGWz|ZKEA-KNj!9aX1oqr8A1}Xht9A*-Ofk!%_=frSsPEoRTox6{S?esmH!`H& zHLbX@;xw*`97Cf%!bql-4eQ=<3r zMch3eVS*3%R-xfm@Me)i&DLGacqWFXO^K}X531$g_^e^E%?{RNShKDLj7~FiiaxwU z-bJjF_cpt^V2b!*g=sPqcGkek-W^X_U{txw!?GS9drIb1$Q?a3hRW_5ieS(f=p@an zO-h)xDjPhR!D?g6@i_lp(fWf>-Bq}a2tTXU2R@Dl{DC2XWDsq1#aLy> z(GXH`oDHiU+Uj;zmd1RR^s-e*TN2mknpbo!6~*EfNEe7b6CA zBILqUxx*2@LP@}I62EYjWFcr4wmW<}BhNl&dAPm$;Bl=!izwO*y@gYkj%Lxrx#;gT zqJ$#5gP5)u#@=fCO~wwzx4&VTi?)Az?5$lrmio8=&mQ}*<8xJOw`|Wd`>K#f*3o5W zhb8ZPi%Akro4g-d#Ky~x;5fj|tGAl*L(d6r>nYVzPl}QgDeE?*lW;Gfe8*BMv1kn$ z4;+u+p08vw^CR`zt&JT&cp7|YGNC(Bfht^SP>Ia2?*>h_JX=aL_$pOO`h{x{x!5!) zsNE9j76UHYaHc~ss>X9Jct^T9gf$iN=BQLcF}FA&|9u8r;nNL5Gy(-SzV$6pi&!_g z1M@{krA3*E{0!xC+~nDOo>x*!#oLQ5!G#Gb)e@c~^YGT^$89m~c>_CCeLtul4P<$I zZq1K=g~P4UgH{sgalcHloAn^h4*ljRzKXKwv1+js%nkj09*;ilYDL>@^(xLEPs#K} zWiU}&4Mmo9zc}2=9rMH3&+1tB+eUG_ZH(CV&A#p)@j!lz6WdywSpu!rlf5<}oGEH@ zI1*<4o4IOkbFG9~F44uBqObKQC?_2%zT%?`%`g&ag<=Z%jNj73S~#R%I7C138Nqej z6E%CAe-pT$ifYh^eErB~^m~Cn6Q77^u+W;61>eYX`1N5^2Xwi&RD+x*b^a_~2TTWz z`DBh7lEjn-$fCNMf!Y`rY5gW6tMOlnUw0CS!(fJbn8Wj1?Gv(8`(0+cK#{${)i8ey?6QNFm@G)Oz~c`Aw8Ae zKoBF1OzG0nUUpKeb5_bF6qIz{qO5a1N1R)^8n7E(auFDpvT15Sn3XA;D$))EcSnumD9Z`BqJD6_7jW^^PP0qZ>2UxO^4TKo7&`5cu6&iuD{wgCWqIKhJQpi{Yv1?`v+?* zM-8glu5$2sR0_qsT~!*oA$a^+>s8OUT;0O)S2=5AM(MouMnWEMt423wBFUd{D}=_# zPH`gJt$oRPqRR6I=9yZ!nz~mmZ=ak|+z3zZdMY)q!jrl_^Y9TdMHxjp#h%nU-UHjZ z_vSt$thwllMybWN!{=}p`S~~gd{sig$%okArv4ePJZN1Spu*G?>kNg>_rAVn6BC1{cPFkPlYIW}1gqxW_nz~nfi%=|I%~>Oe|Owk&0A>p_n+tI1;nN|r2CY220HOvy9;Hg z`m~VpW#9C%ddCN9>l)h<_Kn9JnEVhL`y#(ZJ1CdfpBd_m7o5#W;_M`$U!F)MFZ-6n zPJvEWAnI`zrB9E`-3Z?Lccpe!NMD+;%i`M`*`Fup;b`j`)YRc7DbxERh}mU))gw(K zRk=rs#!uGZc^c}Hf0Rq{h!~!Sm3`l%Jo~8FNGQUhM#Phi-9~xS>L%z(NTv?HT){%b zOtIz+oP1`o6ak`muMQf@I_EMf#Ye0F=h_8z@!Y12ECU0lq&sCXL8T#W&EKTQ9s;MU zP`h1CJ9*C@o`l3y=FT;F+nS)D#-~YCwU!fVPw<}5En%e<*|BkbVSJTA^1~&9)B5bI zKJ#WegGsnoe+`a((n9AXUIS>JR$N);qbg#6E*c%?Ub9(>EuB(Wy$SzIvj@q$4PQ-o zE!+bZJko9(*RWfWpdc*lj^D$d!bN_=@`iGET#7@2LM5WED^>Ca>P#wpGGqGWvr^A| zar?pTAeX$Z>Sz`BJ4MQ7v{Z{Z(!P-Brt;84m%8#$!RE4R-JlbV4TzykH+x)IZOvm$c?;>67ve;2pj#Rcequoh+1Bd#^*yO{ZXZoSL*V)MlakJ5^QP7*pC(*c= z=-YHm4&+8LMDOJjYhp~37%`}Zs~7zk;i-sy*hW!1qJq>i50~yPi6u=!k@z(gRhBE^ z*WV;;BZ!}u%DbbVHwgp6n{GJYIPfRi_;&Lx|Brq|_Uf4Z!WJEyUGiuOm0r5 z4s}~`vV-%eYpb3~7d~%BA1w{dlO5*@IPgXhvJ_6$nSL9iL@Ab?KtoO(Ec*Njy>puW z{Y^Ti?-iDeAso~MpDM_sSmI!coH?vCr~|uccel*a9vEm8BTtXkE^h}qS7G<9BCr%jp=*!1C86R18+~)o6e7DeArI4+X{_b@het}@sc!V*oa2D5$xya z@yIze7_k^9D#K0-%RsezkKGJDwH7Yle&7D(WVnnza|0nZchJ#4J6Uyx;#oH-tyBWq9zhHPfvwYR+~`(+)Q}cM-x0yC}jX z6E;119^QV7K?Qr$F2>@>{D$F&s+aDkuZ4qorl2j9qUK{Ah%}6$i2YlLk zQSQ}?lSTQ1&!Xl-Ka5zRg*4rhr22(I&}YF3r}0j|AFMC^!zW4^t#tV1)`O{%t5dmb z1bdaeS`KYxkL&kCiNwUbi&u(4+>AC;TzNu9Jc?0|fP-_wjBcpUscqgRFM|l-oN{gw zP*F#=?r8ekCO00ATe-P1AD#;dsm36TjwV^ha+WqHex&p|`sN?*h3uIk!D7Ro6t*ea z=_JtO7%H&DJk-i9frQJI`U1l~rdL2n+*o3Yn1FdA*`>1HAnAikD_k_U~oyPv)yPE2MA2PCYjI0u>* zQ%iTtQVN#xBsHTIzqdUNMNkPelVMSm{Zfge#}mKKU;6OC0!7)kK7xMzmhNHpcV=Ai zKIjO|Xz@W6xA$1;eC3c! z`F8N(W7_T9o3Aiy0>4Ldtrrw)hv(!{J-3e6P3Wqm*dyPtNTXMxVqI$5D=f)pFUfrS zSf_5x&Z6JI#wxs^gCHTjFz$Xe*HhPz1R7~r-oj$GO$5jfN2s@7W_2mstmq_K`CB2*n7I-e6;xP(2u)`+)0JXpC@{`H zr_d_&9UK?jt=n=C^xR8u>X+SXK>uOJ4txuaRPGgmig;M$QWuISU(7>)N?&b7+IZ`Z ztX^(Wx9A4{RQYsrN~~Txow9wpcxl!i{0=+Kvb;kZ|5UkjnjB%*r;%o*nIYYSFOApw z^s*B`K9XFgqkA1@VqPj(+H6Q8M$ZU&9iE0POcaXs6%g4(c{=IMeUg$K(Cv!4Ri%%Z zg8idm&bK8cyFT8r3PzJcZB33kjv-mntHsvpSmK3HA}3MbRQN%ux;`=e0Htg9Lpwr@BN3?~`p_NR`d%i+!HCHhNxp|`P6a-9h(;ifKA@3d)njh0oSq**X ziWwDOd^g&kM`V_$9cAE<#i?Yk545`J?T`BXeStU#ws_2{mc)3Wvk~XMU-#D>Uk1cY z5$SR4Z|jjVr1;T}*+Z71$)0%P+@!GH^(Eb=8p8W@!n8;4Fd8S@@#(5)o&@uIy;BLG zNH?iO@``A*!QR|HmOqN&@vE>rSN?z^Srjk3bR^Ixo5eqiAI%@AihCB`(vnF!c=#}` zq(_fouj`TUM~wn@sjmH(mG4Y56Uwz$pH4@vXh)(er)%+PiC?n7QPBD51uj z+)aM}14(kKyGeU)(Xuadl)Xq-CdmUOSMzA&c@E=LyJ|uCkeWrgxk8Z~myY;%^pv#| zqWV@jbKAuHex}|REyXw_8e22Y^DV}QX}j3aZF zTY31Ky!1kCwy~IHX)Qr*bDcQE#ZNV|XDT1;hLOJ<6`{Cd`&8Z`lZK1-nP~}Y3<)@R z9kP}oVm};pY!`p1wRA?2x;?u1p$2ygMNTd z2N%6D`@>6xabQbyKFZAUS2M@N*fyg{@|J;G21KnNw_M^>lyImlD?)HX24No=nxdOO zt$=PqlfHGTsxK{Af;fwjh*pyuzyZAnJphaJR0=v5_A_ihVm-^(d-E_)Q=2iL=0Xp^ zNy5d$e!w}+K>GqK3Ht$~hRIe+Uv|E@nIHNtoY%jmi#+Vyt1dU8psFx`m!`b?cP0Us zg1oog>2H~`rU^% z`A`Lx!_Iw#eP~EB<7lC8Nh}n3W36p;6wTm8l229eS5$oT;Ha=4I6q!ufvO(T&y&e~ zC>;ED){{t@*>i#3TWY`7Ze68`#ZY*Df%+rv69%?w3vRr&gCVZyY<@xyZ#@SkU8@{C zoQ9Efa_aVZ_hLV3RU|$$AeSgw*2+4D>h{F0o=TlF$g@Rx-|6^8&OMK z=fD?5J;ln_wj=!zvOhpa4Us#|qiL}OyKfZ_rX}7~t<~+8ra9`ilC0xJ5QAno@c)2^ zs!OlcDfT7mY1*@Adr0ODn6v>8J|rQ^h;S4J7g)SV==z1 z!Z9H0%U?6xP*)`!_hX)f;YB1!6R2I^Q|kH|qt`0< zKHMl>zLL9gpnupQUh$R$9}?w_K(Z*=X-(2iQnWq`^{z;L5+RdB2KU^(`}o5oD|Z|O zcPYjv*b>^l*N7-teERT$#)u>{3>BY#xy+dGL(R7A%d~e`^t?H2O3OM2Z3arY^<;a< znXU;IipbR`H|YojP1<4e)5M>lNs8x5)6BEbVjaNGEIxBKbR!*ZV5?6#>&`{GF6Gu|bqnINW4mqqA z+vW3_*2n{je4{N3kR8isGc3(MHTuNN7o(=<*&oLQ`)nH(twwa$xKVGyI=NchaU8@& zc16qck*(tb9H*NSN zLvAE%koaz2$sAc|O)=(^a*Ctkp3lT(i&$+Q5>efPCDf8T^PV0HOGu5D-WW?rD^dA9 z10zFW{T~}HJ4olHo&eEc(0`*rC@2J|DLWlS^NHGpJz(a4al;=86cLm)$brSk#?IJ) z#l+m&z}g7JV(4gYY0PYDXJBjz(onwz1yd{MsC)6_=z<7!1NIvbIskh^p`gOl6>LUX z(Hg#J-A5m;!jLg@rwtwzql12PGcULu+cidJ{J}e?&7F9-gC>q87lSFB=R}Lfr=MMg z(xv!vt6Nuj=tqJ(8YGH(v&;ydP~26W^+lz>lZev3y!i%UhdHuO``^YGlOzWV`UNN(X#smwNLK{RTV-SU*u} zLOELPB(j6{_B@*3jCI<|65+bx zUh;B)Sq^_bCe?>fZmh&^;B|v0n`N{la(h(6QB=?)CJHvvz8}R~`q@0qaEPy?YJ*se znpDlkIqb%}O;!Dl470cId>8C9r^epe&0a@zDWFoN$}O?yHJS3TpQG*;^@-fwQ8a1gI-9&G~Bl~rD)-PQIv8YL~LRC0^j#5-)gAw z#}URwtOS+SM?BFghD3$0RwW6r4CF@a4D*bf>w?TWq*&aCDLgx%$az*0wPsd4l^~|1DlQ_X zB+jh%7YQ974xAQ%KKT*o)Zk?Zqx_Ww39+Bc@SpA7z{`I;w}27<&!?37Ckg-E(|!4U z97Tr;7vQC8;O7q5`@28C4=m6+*cli(*fU$$I9Qrn>si?tJ6^b$e{U7PEcT)p|0xzM znAjE?s6YJHm0rCK_UhTcU;UTMyOID5FaaX8|6+st%qs~Z0uvPaOF{pwq(8UNU#=HK zeWp`9z_*tId-XZML@q!ODPm+~=V)LlV*J?A-oXl3QNA=)yG#<57+S#tnqUK<@qm$x z7=Kj@e->WU+!9!)wllC)1UWbu7+Qk<*tHJn4DL5&*-k0ed$7I#B(D zbF??N2HD$-8CZg>jScK5flQ{Yfwj4fH3Vdx{lma_NKjCVP|V8~X!>meb4&>tH&s~5mp16W}&ET`<> z#46Z0L8204kD~VhR0n`60!DR`|C>}*kTD2k1)-Ahu|ioQ0j?OpKD=3pO3NH^@vyLk;HmA?>_;$6(C%0 zNG!wa(VReb<|eL`rgk=tw)TJdCdFm5WgARMH3PIvCj;(M1k=*iD}-hspL0%t(|B6-0CP9o5= zQh=TX-iK3?u7(mfw*whnm?8uc+4oq>i`!lOfW8GjSx8HRL~<3oI5{tB0Bq$GH*kdj z+D6fZY5+j=K*fQppO23#^i(j!7q=F+VDuFi620957 zm>PWz5I}aICypR%BUgxi`rQE{bq&DC{R?Bc<*E!w?wPU@;iwK^y!s2{M(0%-ki2qQ zSf|_@fbsG#4ASnaG9Wo)9^xX~U$+NO|5X`~d~oF2v3@n6N6ugLP#(T2-z(Yf}wd+H#VpoLt8gSjHP*7ih+zfb#;k9#ZA_VOk*At=Zf#&Q0suWzi${RO+ zo2pT8v;=O5xPEd7UZ+kz2_RMhMDTgUBFyiJKnm*LITHvtzm1>Vknu9V?h76DYrDsDrF+>?qaEtw0*q=8%Hs9QPMrF7i2EK%0X{ z5xJOG!DyL*tQ8DwZGoFY>>+5Rsb~50;;RiH+{^R4EbJ?wAV|M(@qiA0JQo+J{Q0nv z{a(6(y*;o%_dnCiCig63838W?jRpn93nn=9N`imR(_Y2L6w=CmQv(cZ9mtu2XV_?I zucFAs5vD(ftc&lHLa4c#goEWezyy#0iw1Wz8%);+UL|F^DW>xpSdxH}{ELcstk)-A zVQBOdQxFUURKx*P1ozu>T-OFds7^Ng9xG7 z@6{dyT44hI0^BYp%&&^j0LEH#2Xhc4_s3>j`hW*8`Xa#S!D~>!`YNnTqxw}EEx^cY za}Y3lc0fh&G2{y{JO15j&%b;kFsxq%<4h_<;|pkJ1Vj(u3oj&2SHUP5TAG_0{E?{q z@5;*+{1z50FJ&vB9X+5O_!0=N$KS)5gvqqqMQuqh=MxH=Ym-9I<$@30}L+Va80Z4q!B zjzpJn+m+YEse|mSAdPi44kpeiK+T2%H4DBbey{nOxNG&rX z1vRRF*^obCUI}F_4>C3d*@;}lm{(q1{`p((;OWbkz*dMW7%~{u6l4u-6LJ7uOsOEQ zF#P;=YH)NU|35@SvY($nV+)Sv75;~4Nb33bPBT}UR)UUi_^51u|FRO2Tz!@ zdR`R^!FYc@Netdm_I!SexO6u7SJz)%R_EsfOPXNKMC^BM7I=kTD$Nz@_UGdj;0uQ2 zA%CMVzo=#sn2WvKzdEvk_lJQ3^H(UJr#B*gMFT&;0r#&19G4DnD9gbCu?%oc74WB< M8JHPf#L!Ux2fs9YcK`qY literal 0 HcmV?d00001 diff --git a/node/src/test/resources/legal-signed-jar.jar b/node/src/test/resources/legal-signed-jar.jar new file mode 100644 index 0000000000000000000000000000000000000000..1546760d22f5a630be2c42b41d8ccf344575fb5c GIT binary patch literal 179581 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

    Dh(d1; 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%0A_wsu~6YvX-odm5V*AwGe->hSJXpXru6}=-uuk^ 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 M3l!9~sB?q=KjR+J;{X5v literal 0 HcmV?d00001 diff --git a/node/src/test/resources/removed-files-signed-jar.jar b/node/src/test/resources/removed-files-signed-jar.jar new file mode 100644 index 0000000000000000000000000000000000000000..f173ff2e7aa5aa32dd2242de6523b3193a05cc30 GIT binary patch literal 10468 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 Date: Tue, 5 Mar 2019 14:51:54 +0000 Subject: [PATCH 062/159] CORDA-2642, CORDA-2699: Upgrade to Corda Gradle plugins 4.0.42 to remove Jolokia from sample CorDapps. (#4855) --- constants.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants.properties b/constants.properties index cb8ba495c4..fe57487de0 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=4.0.41 +gradlePluginsVersion=4.0.42 kotlinVersion=1.2.71 # ***************************************************************# # When incrementing platformVersion make sure to update # From 3bb996d22f1ced65ddf431584600c59f497d6aa9 Mon Sep 17 00:00:00 2001 From: Dimos Raptis Date: Wed, 6 Mar 2019 09:55:59 +0000 Subject: [PATCH 063/159] DOCS - clarify the upgrade policy for finality flow (#4853) --- docs/source/app-upgrade-notes.rst | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index cd0d083913..9f864b162e 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -30,6 +30,8 @@ Although the RPC API is backwards compatible with Corda 3, the RPC wire protocol updated in lockstep with the node to use the new version of the RPC library. Corda 4 delivers RPC wire stability and therefore in future you will be able to update the node and apps without updating RPC clients. +.. _cordapp_upgrade_version_numbers_ref: + Step 2. Adjust the version numbers in your Gradle build files ------------------------------------------------------------- @@ -163,11 +165,25 @@ The previous ``FinalityFlow`` API is insecure. It doesn't have a receive flow, s all signed transactions that are sent to it, without checks. It is **highly** recommended that existing CorDapps migrate away to the new API, as otherwise things like business network membership checks won't be reliably enforced. -This is a three step process: +The flows that make use of ``FinalityFlow`` in a CorDapp can be classified in the following 2 basic categories: + +* **non-initiating flows**: these are flows that finalise a transaction without the involvement of a counterpart flow at all. +* **initiating flows**: these are flows that initiate a counterpart (responder) flow. + +There is a main difference between these 2 different categories, which is relevant to how the CorDapp can be upgraded. +The second category of flows can be upgraded to use the new ``FinalityFlow`` in a backwards compatible way, which means the upgraded CorDapp can be deployed at the various nodes using a *rolling deployment*. +On the other hand, the first category of flows cannot be upgraded to the new ``FinalityFlow`` in a backwards compatible way, so the changes to these flows need to be deployed simultaneously at all the nodes, using a *lockstep deployment*. + +.. note:: A *lockstep deployment* is one, where all the involved nodes are stopped, upgraded to the new version of the CorDapp and then re-started. + As a result, there can't be any nodes running different versions of the CorDapp at any time. + A *rolling deployment* is one, where every node can be stopped, upgraded to the new version of the CorDapp and re-started independently and on its own pace. + As a result, there can be nodes running different versions of the CorDapp and transact with each other successfully. + +The upgrade is a three step process: 1. Change the flow that calls ``FinalityFlow``. 2. Change or create the flow that will receive the finalised transaction. -3. Make sure your application's minimum and target version numbers are both set to 4 (see step 2). +3. Make sure your application's minimum and target version numbers are both set to 4 (see :ref:`cordapp_upgrade_version_numbers_ref`). Upgrading a non-initiating flow ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -222,7 +238,7 @@ to record the finalised transaction: :end-before: DOCEND SimpleNewResponderFlow :dedent: 4 -.. note:: All the nodes in your business network will need the new CorDapp, otherwise they won't know how to receive the transaction. **This +.. note:: As described above, all the nodes in your business network will need the new CorDapp, otherwise they won't know how to receive the transaction. **This includes nodes which previously didn't have the old CorDapp.** If a node is sent a transaction and it doesn't have the new CorDapp loaded then simply restart it with the CorDapp and the transaction will be recorded. From f6ba9a0819ea8009ad443af0cbb5218031227105 Mon Sep 17 00:00:00 2001 From: josecoll Date: Thu, 7 Mar 2019 09:32:16 +0000 Subject: [PATCH 064/159] CORDA-2684 Added new guide on CorDapp Constraints Migration procedures. (#4837) * Added new guide on CorDapp Constraints Migration procedures. * Apply formatting and upper/lowercase changes. * Updated following PR review feedback from RGB and MH. * Minor clarification and cleanup. * Clarify step to ensure there is only one version ("signed") of the same Contracts CorDapp in the nodes /cordapp folder * Incorporating feedback from SS. * Replaced "propagate" with "transition". Adjust terminology to be consistent. * Removed confusing statement. --- docs/source/api-contract-constraints.rst | 67 ++----- docs/source/app-upgrade-notes.rst | 3 + docs/source/cordapp-constraint-migration.rst | 185 +++++++++++++++++++ docs/source/cordapp-upgradeability.rst | 4 +- docs/source/network-bootstrapper.rst | 4 + docs/source/upgrading-cordapps.rst | 12 +- 6 files changed, 212 insertions(+), 63 deletions(-) create mode 100644 docs/source/cordapp-constraint-migration.rst diff --git a/docs/source/api-contract-constraints.rst b/docs/source/api-contract-constraints.rst index b92cb94978..2f93d77c0f 100644 --- a/docs/source/api-contract-constraints.rst +++ b/docs/source/api-contract-constraints.rst @@ -24,7 +24,7 @@ to it. There are several types of constraint: 1. Hash constraint: exactly one version of the app can be used with this state. -2. Zone whitelist constraint: the compatibility zone operator lists the hashes of the versions that can be used with this contract class name. +2. Compatibility zone whitelisted (or CZ whitelisted) constraint: the compatibility zone operator lists the hashes of the versions that can be used with this contract class name. 3. Signature constraint: any version of the app signed by the given ``CompositeKey`` can be used. 4. Always accept constraint: any app can be used at all. This is insecure but convenient for testing. @@ -46,6 +46,8 @@ to issue the states was signed by Alice and Bob, every transaction must use an a the constraint used by equivalent output states (i.e. output states that use the same contract class name) must match the input state, so it can't be changed and you can't combine states with incompatible constraints together in the same transaction. +.. _implicit_vs_explicit_upgrades: + **Implicit vs explicit.** Constraints are not the only way to manage upgrades to transactions. There are two ways of handling upgrades to a smart contract in Corda: @@ -136,15 +138,15 @@ From there it's suspended waiting to be retried on node restart. This gives the node operator the opportunity to recover from those errors, which in the case of constraint violations means adding the right cordapp jar to the ``cordapps`` folder. +.. _relax_hash_constraints_checking_ref: + Hash constrained states in private networks ------------------------------------------- Where private networks started life using CorDapps with hash constrained states, we have introduced a mechanism to relax the checking of these hash constrained states when upgrading to signed CorDapps using signature constraints. -The following java system property may be set to relax the hash constraint checking behaviour: - - -Dnet.corda.node.disableHashConstraints="true" +The Java system property ``-Dnet.corda.node.disableHashConstraints="true"`` may be set to relax the hash constraint checking behaviour. This mode should only be used upon "out of band" agreement by all participants in a network. @@ -191,58 +193,11 @@ During transaction building the ``AutomaticPlaceholderConstraint`` for output st will be selected based on a variety of factors so that the above holds true. If it can't find attachments in storage or there are no possible constraints, the ``TransactionBuilder`` will throw an exception. +Constraints migration to Corda 4 +-------------------------------- -.. _constraints_whitelist_to_signature_ref: - -How to use the ``SignatureAttachmentConstraint`` if states were already created on the network with the ``WhitelistedByZoneAttachmentConstraint`` -------------------------------------------------------------------------------------------------------------------------------------------------- - -1. As the original developer of the corDapp, the first step is to sign the latest version of the JAR that was released (see :doc:`cordapp-build-systems`). -The key used for signing will be used to sign all subsequent releases, so it should be stored appropriately. The JAR can be signed by multiple keys owned -by different parties and it will be expressed as a ``CompositeKey`` in the ``SignatureAttachmentConstraint`` (See :doc:`api-core-types`). -Use `JAR signing and verification tool `_ to sign the existing JAR. -The signing capability of :ref:`corda-gradle-plugins ` cannot be used in this context as it signs the JAR while building it from source. - -2. Whitelist this newly signed JAR with the Zone operator. The Zone operator should check that the JAR is signed and not allow any -more versions of it to be whitelisted in the future. From now on the developer(s) who signed the JAR are responsible for new versions. - -3. Any flows that build transactions using this Cordapp will have the responsibility of transitioning states to the ``SignatureAttachmentConstraint``. -This is done explicitly in the code by setting the constraint of the output states to signers of the latest version of the whitelisted jar. -In the near future we will make this transition automatic if we detect that the previous 2 steps were executed. - -4. As a node operator you need to add the new signed version of the contracts cordapp to the "cordapps" folder together with the latest version of the flows jar -that will contain code like: - -.. container:: codeset - - .. sourcecode:: kotlin - - // This will read the signers for the deployed cordapp. - val attachment = this.serviceHub.cordappProvider.getContractAttachmentID(contractClass) - val signers = this.serviceHub.attachments.openAttachment(attachment!!)!!.signerKeys - - // Create the key that will have to pass for all future versions. - val ownersKey = signers.first() - - val txBuilder = TransactionBuilder(notary) - // Set the Signature constraint on the new state to migrate away from the WhitelistConstraint. - .addOutputState(outputState, constraint = SignatureAttachmentConstraint(ownersKey)) - ... - - .. sourcecode:: java - - // This will read the signers for the deployed cordapp. - SecureHash attachment = this.getServiceHub().getCordappProvider().getContractAttachmentID(contractClass); - List signers = this.getServiceHub().getAttachments().openAttachment(attachment).getSignerKeys(); - - // Create the key that will have to pass for all future versions. - PublicKey ownersKey = signers.get(0); - - TransactionBuilder txBuilder = new TransactionBuilder(notary) - // Set the Signature constraint on the new state to migrate away from the WhitelistConstraint. - .addOutputState(outputState, myContract, new SignatureAttachmentConstraint(ownersKey)) - ... - +Please read :doc:`cordapp-constraint-migration` to understand how to consume and evolve pre-Corda 4 issued hash or CZ whitelisted constrained states +using a Corda 4 signed CorDapp (using signature constraints). Debugging --------- @@ -278,7 +233,7 @@ The same example in Java: }); -Staring a node missing CorDapp(s) +Starting a node missing CorDapp(s) ********************************* When running the Corda node ensure all CordDapp JARs are placed in ``cordapps`` directory of each node. diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index 9f864b162e..96a7814736 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -376,6 +376,9 @@ automatically use them if your application JAR is signed. **We recommend all JAR with developer certificates is deployed to a production node, the node will refuse to start. Therefore to deploy apps built for Corda 4 to production you will need to generate signing keys and integrate them with the build process. +.. note:: Please read the :doc:`cordapp-constraint-migration` guide to understand how to upgrade CorDapps to use Corda 4 signature constraints and consume + existing states on ledger issued with older constraint types (e.g. Corda 3.x states issued with **hash** or **CZ whitelisted** constraints). + Step 10. Security: Package namespace handling --------------------------------------------- diff --git a/docs/source/cordapp-constraint-migration.rst b/docs/source/cordapp-constraint-migration.rst new file mode 100644 index 0000000000..370eba42ab --- /dev/null +++ b/docs/source/cordapp-constraint-migration.rst @@ -0,0 +1,185 @@ +.. highlight:: kotlin +.. role:: kotlin(code) + :language: kotlin +.. raw:: html + + + + + +CorDapp constraints migration +============================= + +.. note:: Before reading this page, you should be familiar with the key concepts of :doc:`Contract Constraints `. + +Corda 4 introduces and recommends building signed CorDapps that issue states with signature constraints. +Existing on ledger states issued before Corda 4 are not automatically transitioned to new signature constraints when building transactions in Corda 4. +This document explains how to modify existing CorDapp flows to explicitly consume and evolve pre Corda 4 states, and outlines a future mechanism +where such states will transition automatically (without explicit migration code). + +Faced with the exercise of upgrading an existing Corda 3.x CorDapp to Corda 4, you need to consider the following: + +* What existing unconsumed states have been issued on ledger by a previous version of this CorDapp and using other constraint types? + + If you have existing **hash** constrained states see :ref:`Migrating hash constraints`. + + If you have existing **CZ whitelisted** constrained states see :ref:`Migrating CZ whitelisted constraints`. + + If you have existing **always accept** constrained states these are not consumable nor evolvable as they offer no security and should only + be used in test environments. + +* What type of contract states does my CorDapp use? + + **Linear states** typically evolve over an extended period of time (defined by the lifecycle of the associated business use case), and + thus are prime candidates for constraints migration. + + **Fungible states** are created by an issuer and transferred around a Corda network until explicitly exited (by the same issuer). + They do not evolve as linear states, but are transferred between participants on a network. Their consumption may produce additional new + output states to represent adjustments to the original state (e.g. change when spending cash). For the purposes of constraints migration, + it is desirable that any new output states are produced using the new Corda 4 signature constraint types. + + Where you have long transaction chains of fungible states, it may be advisable to send them back to the issuer for re-issuance (this is + called "chain snipping" and has performance advantages as well as simplifying constraints type migration). + +* Should I use the **implicit** or **explicit** upgrade path? + + The general recommendation for Corda 4 is to use **implicit** upgrades for the reasons described :ref:`here `. + + **Implicit** upgrades allow pre-authorising multiple implementations of the contract ahead of time. + They do not require additional coding and do not incur a complex choreographed operational upgrade process. + +.. warning:: The steps outlined in this page assume you are using the same CorDapp Contract (eg. same state definition, commands and verification code) and + wish to use that CorDapp to leverage the upgradeability benefits of Corda 4 signature constraints. If you are looking to upgrade code within an existing + Contract CorDapp please read :ref:`Contract and state versioning` and :doc:`cordapp-upgradeability` to understand your options. + +Please also remember that *states are always consumable if the version of the CorDapp that issued (created) them is installed*. +In the simplest of scenarios it may be easier to re-issue existing hash or CZ whitelist constrained states (eg. exit them from the ledger using +the original unsigned CorDapp and re-issuing them using the new signed CorDapp). + +.. _hash_constraint_migration: + +Hash constraints migration +-------------------------- + +.. note:: These instructions only apply to CorDapp Contract JARs (unless otherwise stated). + +Corda 4.0 +~~~~~~~~~ + +Corda 4.0 requires some additional steps to consume and evolve pre-existing on-ledger **hash** constrained states: + +1. All Corda Nodes in the same CZ or business network that may encounter a transaction chain with a hash constrained state must be started using + relaxed hash constraint checking mode as described in :ref:`relax_hash_constraints_checking_ref`. + +2. CorDapp flows that build transactions using pre-existing *hash-constrained* states must explicitly set output states to use *signature constraints* + and specify the related public key(s) used in signing the associated CorDapp Contract JAR: + +.. container:: codeset + + .. sourcecode:: kotlin + + // This will read the signers for the deployed CorDapp. + val attachment = this.serviceHub.cordappProvider.getContractAttachmentID(contractClass) + val signers = this.serviceHub.attachments.openAttachment(attachment!!)!!.signerKeys + + // Create the key that will have to pass for all future versions. + val ownersKey = signers.first() + + val txBuilder = TransactionBuilder(notary) + // Set the Signature constraint on the new state to migrate away from the hash constraint. + .addOutputState(outputState, constraint = SignatureAttachmentConstraint(ownersKey)) + + .. sourcecode:: java + + // This will read the signers for the deployed CorDapp. + SecureHash attachment = this.getServiceHub().getCordappProvider().getContractAttachmentID(contractClass); + List signers = this.getServiceHub().getAttachments().openAttachment(attachment).getSignerKeys(); + + // Create the key that will have to pass for all future versions. + PublicKey ownersKey = signers.get(0); + + TransactionBuilder txBuilder = new TransactionBuilder(notary) + // Set the Signature constraint on the new state to migrate away from the hash constraint. + .addOutputState(outputState, myContract, new SignatureAttachmentConstraint(ownersKey)) + +3. As a node operator you need to add the new signed version of the contracts CorDapp to the ``/cordapps`` folder together with the latest version of the flows jar. + Please also ensure that the original unsigned contracts CorDapp is removed from the ``/cordapps`` folder (this will already be present in the + nodes attachments store) to ensure the lookup code in step 2 retrieves the correct signed contract CorDapp JAR. + +Later releases +~~~~~~~~~~~~~~ + +The next version of Corda will provide automatic transition of *hash constrained* states. This means that signed CorDapps running on a Corda 4.x node will +automatically propagate any pre-existing on-ledger *hash-constrained* states (and generate *signature-constrained* outputs) when the system property +to break constraints is set. + +.. _cz_whitelisted_constraint_migration: + +CZ whitelisted constraints migration +------------------------------------- + +.. note:: These instructions only apply to CorDapp Contract JARs (unless otherwise stated). + +Corda 4.0 +~~~~~~~~~ + +Corda 4.0 requires some additional steps to consume and evolve pre-existing on-ledger **CZ whitelisted** constrained states: + +1. As the original developer of the CorDapp, the first step is to sign the latest version of the JAR that was released (see :doc:`cordapp-build-systems`). + The key used for signing will be used to sign all subsequent releases, so it should be stored appropriately. The JAR can be signed by multiple keys owned + by different parties and it will be expressed as a ``CompositeKey`` in the ``SignatureAttachmentConstraint`` (See :doc:`api-core-types`). + +2. The new Corda 4 signed CorDapp JAR must be registered with the CZ network operator (as whitelisted in the network parameters which are distributed + to all nodes in that CZ). The CZ network operator should check that the JAR is signed and not allow any more versions of it to be whitelisted in the future. + From now on the development organisation that signed the JAR is responsible for signing new versions. + + The process of CZ network CorDapp whitelisting depends on how the Corda network is configured: + + - if using a hosted CZ network (such as `The Corda Network `_ or + `UAT Environment `_ ) running an Identity Operator (formerly known as Doorman) and + Network Map Service, you should manually send the hashes of the two JARs to the CZ network operator and request these be added using + their network parameter update process. + + - if using a local network created using the Network Bootstrapper tool, please follow the instructions in + :ref:`Updating the contract whitelist for bootstrapped networks ` to can add both CorDapp Contract JAR hashes. + +3. Any flows that build transactions using this CorDapp will have the responsibility of transitioning states to the ``SignatureAttachmentConstraint``. + This is done explicitly in the code by setting the constraint of the output states to signers of the latest version of the whitelisted jar: + +.. container:: codeset + + .. sourcecode:: kotlin + + // This will read the signers for the deployed CorDapp. + val attachment = this.serviceHub.cordappProvider.getContractAttachmentID(contractClass) + val signers = this.serviceHub.attachments.openAttachment(attachment!!)!!.signerKeys + + // Create the key that will have to pass for all future versions. + val ownersKey = signers.first() + + val txBuilder = TransactionBuilder(notary) + // Set the Signature constraint on the new state to migrate away from the WhitelistConstraint. + .addOutputState(outputState, constraint = SignatureAttachmentConstraint(ownersKey)) + + .. sourcecode:: java + + // This will read the signers for the deployed CorDapp. + SecureHash attachment = this.getServiceHub().getCordappProvider().getContractAttachmentID(contractClass); + List signers = this.getServiceHub().getAttachments().openAttachment(attachment).getSignerKeys(); + + // Create the key that will have to pass for all future versions. + PublicKey ownersKey = signers.get(0); + + TransactionBuilder txBuilder = new TransactionBuilder(notary) + // Set the Signature constraint on the new state to migrate away from the WhitelistConstraint. + .addOutputState(outputState, myContract, new SignatureAttachmentConstraint(ownersKey)) + +4. As a node operator you need to add the new signed version of the contracts CorDapp to the ``/cordapps`` folder together with the latest version of the flows jar. + Please also ensure that the original unsigned contracts CorDapp is removed from the ``/cordapps`` folder (this will already be present in the + nodes attachments store) to ensure the lookup code in step 3 retrieves the correct signed contract CorDapp JAR. + +Later releases +~~~~~~~~~~~~~~ + +The next version of Corda will provide automatic transition of *CZ whitelisted* constrained states. This means that signed CorDapps running on a Corda 4.x node will +automatically propagate any pre-existing on-ledger *CZ whitelisted* constrained states (and generate *signature* constrained outputs). diff --git a/docs/source/cordapp-upgradeability.rst b/docs/source/cordapp-upgradeability.rst index 1e7dc56eb7..a56d3d6a56 100644 --- a/docs/source/cordapp-upgradeability.rst +++ b/docs/source/cordapp-upgradeability.rst @@ -20,10 +20,10 @@ The following guarantees are made for CorDapps running on Corda 4.0 - CorDapp Contract states generated on ledger using hash constraints are not directly migratable to signature constraints in this release. Your compatibility zone operator may whitelist a JAR previously used to issue hash constrained states, and then you can follow the manual - process described in the paragraph below to migrate these to signature constraints. + process described in the paragraph below to migrate these to signature constraints. See :doc:`cordapp-constraint-migration` for more information. - CorDapp Contract states generated on ledger using CZ whitelisted constraints are migratable to signature constraints using a manual process - that requires programmatic code changes. See :ref:`constraints_whitelist_to_signature_ref` for more information. + that requires programmatic code changes. See :ref:`cz_whitelisted_constraint_migration` for more information. - Explicit Contract Upgrades are only supported for hash and CZ whitelisted constraint types. See :ref:`explicit_contract_upgrades_ref` for more information. diff --git a/docs/source/network-bootstrapper.rst b/docs/source/network-bootstrapper.rst index 451d1af768..5bb02f7d2b 100644 --- a/docs/source/network-bootstrapper.rst +++ b/docs/source/network-bootstrapper.rst @@ -76,6 +76,8 @@ alongside the config files. For example, if your directory has this structure: The ``cordapp-a.jar`` and ``cordapp-b.jar`` will be installed in each node directory, and any contracts within them will be added to the Contract Whitelist (see below). +.. _bootstrapper_whitelisting_contracts: + Whitelisting contracts ---------------------- @@ -191,6 +193,8 @@ such a generated keys, will be unaffected. the same machine. If a network needs to be updated using the Bootstrapper once deployed, the nodes will need collecting back together. +.. _bootstrapper_updating_whitelisted_contracts: + Updating the contract whitelist for bootstrapped networks --------------------------------------------------------- diff --git a/docs/source/upgrading-cordapps.rst b/docs/source/upgrading-cordapps.rst index 6d885c029b..6859dffb4c 100644 --- a/docs/source/upgrading-cordapps.rst +++ b/docs/source/upgrading-cordapps.rst @@ -259,7 +259,7 @@ a drain is complete there should be no outstanding checkpoints or running flows. A node can be drained or undrained via RPC using the ``setFlowsDrainingModeEnabled`` method, and via the shell using the standard ``run`` command to invoke the RPC. See :doc:`shell` to learn more. -.. _explicit_contract_upgrades_ref: +.. _contract_upgrading_ref: Contract and state versioning ----------------------------- @@ -271,7 +271,12 @@ There are two types of contract/state upgrade: 2. *Explicit:* By creating a special *contract upgrade transaction* and getting all participants of a state to sign it using the contract upgrade flows -This section of the documentation focuses only on *explicit* upgrades. +The general recommendation for Corda 4 is to use **implicit** upgrades for the reasons described :ref:`here `. + +.. _explicit_contract_upgrades_ref: + +Performing explicit contract and state upgrades +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In an explicit upgrade, contracts and states can be changed in arbitrary ways, if and only if all of the state's participants agree to the proposed upgrade. The following combinations of upgrades are possible: @@ -280,9 +285,6 @@ participants agree to the proposed upgrade. The following combinations of upgrad * A state is upgraded while the contract stays the same * The state and the contract are updated simultaneously -Performing explicit contract and state upgrades -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 1. Preserve the existing state and contract definitions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Currently, all nodes must **permanently** keep **all** old state and contract definitions on their node's classpath From 96b23eea6f60d071b0631f624f7b866d5439355f Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 7 Mar 2019 11:40:43 +0000 Subject: [PATCH 065/159] ENT-3266: Do not attempt to overrwite an existing CorDapp jar in tests (#1906) (#4858) There's a bug with the ServiceLoader which leaks a file handle to the app jar on shutdown. This causes an issue if a mock node is restarted in Windows. To avoid the problem completely we no longer overwrite any existing jars, as the jar to be copied will be same anyway. (cherry picked from commit 0038a864817d331d7e48d741770b7acc40af2574) --- .../cordapp/JarScanningCordappLoader.kt | 34 +++++++++++++++---- .../node/internal/TestCordappInternal.kt | 8 +++-- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt index 7a92d6184d..345876741e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt @@ -25,12 +25,14 @@ import net.corda.nodeapi.internal.coreContractClasses import net.corda.serialization.internal.DefaultWhitelist import org.apache.commons.collections4.map.LRUMap import java.lang.reflect.Modifier +import java.math.BigInteger import java.net.URL import java.net.URLClassLoader import java.nio.file.Path import java.util.* import java.util.jar.JarInputStream import java.util.jar.Manifest +import java.util.zip.ZipInputStream import kotlin.reflect.KClass import kotlin.streams.toList @@ -142,7 +144,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: findServiceFlows(this), findSchedulableFlows(this), findServices(this), - findPlugins(url), + findWhitelists(url), findSerializers(this), findCustomSchemas(this), findAllFlows(this), @@ -265,7 +267,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: return contractClasses } - private fun findPlugins(cordappJarPath: RestrictedURL): List { + private fun findWhitelists(cordappJarPath: RestrictedURL): List { val whitelists = URLClassLoader(arrayOf(cordappJarPath.url)).use { ServiceLoader.load(SerializationWhitelist::class.java, it).toList() } @@ -292,8 +294,6 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: } } - - private fun loadClass(className: String, type: KClass): Class? { return try { appClassLoader.loadClass(className).asSubclass(type.java) @@ -306,6 +306,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: } } + // TODO Remove this class as rootPackageName is never non-null. /** @property rootPackageName only this package and subpackages may be extracted from [url], or null to allow all packages. */ private data class RestrictedURL(val url: URL, val rootPackageName: String?) { val qualifiedNamePrefix: String get() = rootPackageName?.let { "$it." } ?: "" @@ -359,14 +360,14 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: fun getAllStandardClasses(): List { return scanResult - .getAllStandardClasses() + .allStandardClasses .names .filter { it.startsWith(qualifiedNamePrefix) } } fun getAllInterfaces(): List { return scanResult - .getAllInterfaces() + .allInterfaces .names .filter { it.startsWith(qualifiedNamePrefix) } } @@ -386,13 +387,32 @@ class MultipleCordappsForFlowException(message: String) : Exception(message) class CordappInvalidVersionException(msg: String) : Exception(msg) abstract class CordappLoaderTemplate : CordappLoader { + + companion object { + + private val logger = contextLogger() + } + override val flowCordappMap: Map>, Cordapp> by lazy { cordapps.flatMap { corDapp -> corDapp.allFlows.map { flow -> flow to corDapp } } .groupBy { it.first } .mapValues { entry -> if (entry.value.size > 1) { + logger.error("There are multiple CorDapp JARs on the classpath for flow " + + "${entry.value.first().first.name}: [ ${entry.value.joinToString { it.second.jarPath.toString() }} ].") + entry.value.forEach { (_, cordapp) -> + ZipInputStream(cordapp.jarPath.openStream()).use { zip -> + val ident = BigInteger(64, Random()).toString(36) + logger.error("Contents of: ${cordapp.jarPath} will be prefaced with: $ident") + var e = zip.nextEntry + while (e != null) { + logger.error("$ident\t ${e.name}") + e = zip.nextEntry + } + } + } throw MultipleCordappsForFlowException("There are multiple CorDapp JARs on the classpath for flow " + - "${entry.value.first().first.name}: [ ${entry.value.joinToString { it.second.name }} ].") + "${entry.value.first().first.name}: [ ${entry.value.joinToString { it.second.jarPath.toString() }} ].") } entry.value.single().second } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappInternal.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappInternal.kt index 1a14b28000..3380c37b5d 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappInternal.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappInternal.kt @@ -6,8 +6,8 @@ import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.writeText import net.corda.testing.node.TestCordapp +import java.nio.file.FileAlreadyExistsException import java.nio.file.Path -import java.nio.file.StandardCopyOption.REPLACE_EXISTING /** * Extends the public [TestCordapp] API with internal extensions for use within the testing framework and for internal testing of the platform. @@ -36,7 +36,11 @@ abstract class TestCordappInternal : TestCordapp() { val configDir = (cordappsDir / "config").createDirectories() jarToCordapp.forEach { jar, cordapp -> - jar.copyToDirectory(cordappsDir, REPLACE_EXISTING) + try { + jar.copyToDirectory(cordappsDir) + } catch (e: FileAlreadyExistsException) { + // Ignore if the node already has the same CorDapp jar. This can happen if the node is being restarted. + } val configString = ConfigValueFactory.fromMap(cordapp.config).toConfig().root().render() (configDir / "${jar.fileName.toString().removeSuffix(".jar")}.conf").writeText(configString) } From 1c38ecee7bef1ed3e08acef84dadcd98943043f1 Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Thu, 7 Mar 2019 14:47:51 +0000 Subject: [PATCH 066/159] ENT-3256 Small performance enhancement and OS preparation for ENT changes (#4857) --- .../kotlin/net/corda/core/crypto/Crypto.kt | 4 +-- .../corda/core/crypto/internal/Instances.kt | 12 ++++++++ .../net/corda/core/internal/InternalUtils.kt | 12 +++++++- .../corda/core/internal/StatePointerSearch.kt | 4 +-- .../internal/crypto/ContentSignerBuilder.kt | 3 +- .../net/corda/node/internal/AbstractNode.kt | 4 +-- .../statemachine/FlowLogicRefFactoryImpl.kt | 30 +++++++++++-------- 7 files changed, 48 insertions(+), 21 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt index 227f5c069b..f4d4a9c2b8 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt @@ -436,7 +436,7 @@ object Crypto { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" } - val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName]) + val signature = Instances.getSignatureInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName]) // Note that deterministic signature schemes, such as EdDSA, original SPHINCS-256 and RSA PKCS#1, do not require // extra randomness, but we have to ensure that non-deterministic algorithms (i.e., ECDSA) use non-blocking // SecureRandom implementation. Also, SPHINCS-256 implementation in BouncyCastle 1.60 fails with @@ -640,7 +640,7 @@ object Crypto { require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } - val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName]) + val signature = Instances.getSignatureInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName]) signature.initVerify(publicKey) signature.update(clearData) return signature.verify(signatureData) diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt new file mode 100644 index 0000000000..f1dee21331 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt @@ -0,0 +1,12 @@ +package net.corda.core.crypto.internal + +import java.security.Provider +import java.security.Signature + +/** + * This is a collection of crypto related getInstance methods that tend to be quite inefficient and we want to be able to + * optimise them en masse. + */ +object Instances { + fun getSignatureInstance(algorithm: String, provider: Provider?) = Signature.getInstance(algorithm, provider) +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index 65184d6547..2f1b3d5667 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -387,7 +387,17 @@ val Class<*>.location: URL get() = protectionDomain.codeSource.location /** Convenience method to get the package name of a class literal. */ val KClass<*>.packageName: String get() = java.packageName -val Class<*>.packageName: String get() = requireNotNull(`package`?.name) { "$this not defined inside a package" } +val Class<*>.packageName: String get() = requireNotNull(this.packageNameOrNull) { "$this not defined inside a package" } +val Class<*>.packageNameOrNull: String? // This intentionally does not go via `package` as that code path is slow and contended and just ends up doing this. + get() { + val name = this.getName() + val i = name.lastIndexOf('.') + if (i != -1) { + return name.substring(0, i) + } else { + return null + } + } inline val Class<*>.isAbstractClass: Boolean get() = Modifier.isAbstract(modifiers) diff --git a/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt b/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt index 0ae69ff7ae..5e6c3f9baf 100644 --- a/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt +++ b/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt @@ -43,7 +43,7 @@ class StatePointerSearch(val state: ContractState) { val fieldsWithObjects = fields.mapNotNull { field -> // Ignore classes which have not been loaded. // Assumption: all required state classes are already loaded. - val packageName = field.type.`package`?.name + val packageName = field.type.packageNameOrNull if (packageName == null) { null } else { @@ -72,7 +72,7 @@ class StatePointerSearch(val state: ContractState) { is StatePointer<*> -> statePointers.add(obj) is Iterable<*> -> handleIterable(obj) else -> { - val packageName = obj.javaClass.`package`.name + val packageName = obj.javaClass.packageNameOrNull ?: "" val isBlackListed = blackListedPackages.any { packageName.startsWith(it) } if (isBlackListed.not()) fieldQueue.addAllFields(obj) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt index 37e99e9146..a1ac31cc9c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt @@ -2,6 +2,7 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.crypto.Crypto.SPHINCS256_SHA256 import net.corda.core.crypto.SignatureScheme +import net.corda.core.crypto.internal.Instances import org.bouncycastle.asn1.x509.AlgorithmIdentifier import org.bouncycastle.operator.ContentSigner import java.io.OutputStream @@ -17,7 +18,7 @@ import java.security.Signature object ContentSignerBuilder { fun build(signatureScheme: SignatureScheme, privateKey: PrivateKey, provider: Provider, random: SecureRandom? = null): ContentSigner { val sigAlgId = signatureScheme.signatureOID - val sig = Signature.getInstance(signatureScheme.signatureName, provider).apply { + val sig = Instances.getSignatureInstance(signatureScheme.signatureName, provider).apply { // TODO special handling for Sphincs due to a known BouncyCastle's Sphincs bug we reported. // It is fixed in BC 161b12, so consider updating the below if-statement after updating BouncyCastle. if (random != null && signatureScheme != SPHINCS256_SHA256) { diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 27345da35f..562f17cdfa 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -35,7 +35,6 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.minutes import net.corda.node.CordaClock import net.corda.node.VersionInfo -import net.corda.nodeapi.internal.cordapp.CordappLoader import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.internal.cordapp.* import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy @@ -74,6 +73,7 @@ import net.corda.node.utilities.* import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.config.CertificateStore +import net.corda.nodeapi.internal.cordapp.CordappLoader import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA @@ -184,7 +184,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, @Suppress("LeakingThis") val vaultService = makeVaultService(keyManagementService, servicesForResolution, database, cordappLoader).tokenize() val nodeProperties = NodePropertiesPersistentStore(StubbedNodeUniqueIdProvider::value, database, cacheFactory) - val flowLogicRefFactory = FlowLogicRefFactoryImpl(cordappLoader.appClassLoader) + open val flowLogicRefFactory = FlowLogicRefFactoryImpl(cordappLoader.appClassLoader) // TODO Cancelling parameters updates - if we do that, how we ensure that no one uses cancelled parameters in the transactions? val networkMapUpdater = NetworkMapUpdater( networkMapCache, diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt index bf493a45b9..d9357e0668 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt @@ -1,8 +1,8 @@ package net.corda.node.services.statemachine -import net.corda.core.internal.VisibleForTesting import com.google.common.primitives.Primitives import net.corda.core.flows.* +import net.corda.core.internal.VisibleForTesting import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SingletonSerializeAsToken import java.lang.reflect.ParameterizedType @@ -32,7 +32,7 @@ data class FlowLogicRefImpl internal constructor(val flowLogicClassName: String, * in response to a potential malicious use or buggy update to an app etc. */ // TODO: Replace with a per app classloader/cordapp provider/cordapp loader - this will do for now -class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonSerializeAsToken(), FlowLogicRefFactory { +open class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonSerializeAsToken(), FlowLogicRefFactory { override fun create(flowClass: Class>, vararg args: Any?): FlowLogicRef { if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) { throw IllegalFlowLogicException(flowClass, "because it's not a schedulable flow") @@ -63,17 +63,7 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS // to avoid requiring only a single constructor. val argTypes = args.map { it?.javaClass } val constructor = try { - flowClass.kotlin.constructors.single { ctor -> - // Get the types of the arguments, always boxed (as that's what we get in the invocation). - val ctorTypes = ctor.javaConstructor!!.parameterTypes.map { Primitives.wrap(it) } - if (argTypes.size != ctorTypes.size) - return@single false - for ((argType, ctorType) in argTypes.zip(ctorTypes)) { - if (argType == null) continue // Try and find a match based on the other arguments. - if (!ctorType.isAssignableFrom(argType)) return@single false - } - true - } + findConstructor(flowClass, argTypes) } catch (e: IllegalArgumentException) { throw IllegalFlowLogicException(flowClass, "due to ambiguous match against the constructors: $argTypes") } catch (e: NoSuchElementException) { @@ -85,6 +75,20 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS return createKotlin(flowClass, argsMap) } + protected open fun findConstructor(flowClass: Class>, argTypes: List?>): KFunction> { + return flowClass.kotlin.constructors.single { ctor -> + // Get the types of the arguments, always boxed (as that's what we get in the invocation). + val ctorTypes = ctor.javaConstructor!!.parameterTypes.map { Primitives.wrap(it) } + if (argTypes.size != ctorTypes.size) + return@single false + for ((argType, ctorType) in argTypes.zip(ctorTypes)) { + if (argType == null) continue // Try and find a match based on the other arguments. + if (!ctorType.isAssignableFrom(argType)) return@single false + } + true + } + } + /** * Create a [FlowLogicRef] by trying to find a Kotlin constructor that matches the given args. * From 785755067a3260d7837364f3529fd03c816e78e8 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Thu, 7 Mar 2019 13:13:18 +0100 Subject: [PATCH 067/159] Docs: use version numbers read from the build system, not duplicated. --- build.gradle | 6 +++--- constants.properties | 7 +++++++ docs/source/app-upgrade-notes.rst | 10 ++++----- docs/source/conf.py | 17 +++++++++++++--- docs/source/cordapp-build-systems.rst | 29 ++++++++++----------------- docs/source/getting-set-up.rst | 2 +- node/build.gradle | 2 +- 7 files changed, 42 insertions(+), 31 deletions(-) diff --git a/build.gradle b/build.gradle index 29dc5d0533..f25bd52f8e 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { file("$projectDir/constants.properties").withInputStream { constants.load(it) } // Our version: bump this on release. - ext.corda_release_version = "5.0-SNAPSHOT" + ext.corda_release_version = constants.getProperty("cordaVersion") ext.corda_platform_version = constants.getProperty("platformVersion") ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion") @@ -14,7 +14,7 @@ buildscript { ext.kotlin_version = constants.getProperty("kotlinVersion") ext.quasar_group = 'co.paralleluniverse' - ext.quasar_version = '0.7.10' + ext.quasar_version = constants.getProperty("quasarVersion") // gradle-capsule-plugin:1.0.2 contains capsule:1.0.1 by default. // We must configure it manually to use the latest capsule version. @@ -81,7 +81,7 @@ buildscript { // Update 121 is required for ObjectInputFilter. // Updates [131, 161] also have zip compression bugs on MacOS (High Sierra). // when the java version in NodeStartup.hasMinimumJavaVersion() changes, so must this check - ext.java8_minUpdateVersion = '171' + ext.java8_minUpdateVersion = constants.getProperty('java8MinUpdateVersion') repositories { mavenLocal() diff --git a/constants.properties b/constants.properties index fe57487de0..869bc9cc21 100644 --- a/constants.properties +++ b/constants.properties @@ -1,11 +1,18 @@ +# This file is parsed from Python in the docs/source/conf.py file +# because some versions here need to be matched by app authors in +# their own projects. So don't get fancy with syntax! + +cordaVersion=5.0-SNAPSHOT gradlePluginsVersion=4.0.42 kotlinVersion=1.2.71 +java8MinUpdateVersion=171 # ***************************************************************# # When incrementing platformVersion make sure to update # # net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. # # ***************************************************************# platformVersion=5 guavaVersion=25.1-jre +quasarVersion=0.7.10 proguardVersion=6.0.3 bouncycastleVersion=1.60 disruptorVersion=3.4.2 diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index 96a7814736..ddd9d9aa6b 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -37,12 +37,12 @@ Step 2. Adjust the version numbers in your Gradle build files Alter the versions you depend on in your Gradle file like so: -.. sourcecode:: groovy +.. parsed-literal:: - ext.corda_release_version = '4.0' - ext.corda_gradle_plugins_version = '4.0.38' - ext.kotlin_version = '1.2.71' - ext.quasar_version = '0.7.10' + ext.corda_release_version = '|corda_version|' + ext.corda_gradle_plugins_version = '|gradle_plugins_version|' + ext.kotlin_version = '|kotlin_version|' + ext.quasar_version = '|quasar_version|' .. note:: You may wish to update your kotlinOptions to use language level 1.2, to benefit from the new features. Apps targeting Corda 4 may not at this time use Kotlin 1.3, as it was released too late in the development cycle diff --git a/docs/source/conf.py b/docs/source/conf.py index 0516f279ed..95e9626abb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -7,10 +7,21 @@ import sys, os # # TEXT SUBSTITUTIONS +with open("../../constants.properties", "r") as f: + constants_properties_lines = f.readlines() +constants_properties_dict = dict([l.strip().split('=') for l in constants_properties_lines if not l.startswith("#") and not l.strip() == ""]) + rst_epilog = """ -.. |java_version| replace:: 8u171 -.. |kotlin_version| replace:: 1.2.71 -""" +.. |java_version| replace:: 8u%s +.. |kotlin_version| replace:: %s +.. |gradle_plugins_version| replace:: %s +.. |quasar_version| replace:: %s +.. |corda_version| replace:: %s +""" % (constants_properties_dict["java8MinUpdateVersion"], + constants_properties_dict["kotlinVersion"], + constants_properties_dict["gradlePluginsVersion"], + constants_properties_dict["quasarVersion"], + constants_properties_dict["cordaVersion"]) ############################################################################ diff --git a/docs/source/cordapp-build-systems.rst b/docs/source/cordapp-build-systems.rst index 07da3debc7..19d9cada6e 100644 --- a/docs/source/cordapp-build-systems.rst +++ b/docs/source/cordapp-build-systems.rst @@ -9,7 +9,7 @@ Building and installing a CorDapp .. contents:: -Cordapps run on the Corda platform and integrate with it and each other. This article explains how to build CorDapps. +CorDapps run on the Corda platform and integrate with it and each other. This article explains how to build CorDapps. To learn what a CorDapp is, please read :doc:`cordapp-overview`. CorDapp format @@ -45,29 +45,22 @@ Setting your dependencies Choosing your Corda, Quasar and Kotlin versions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Several ``ext`` variables are used in a CorDapp's ``build.gradle`` file to define which versions are used to build your CorDapp: +Several ``ext`` variables are used in a CorDapp's ``build.gradle`` file to define version numbers that should match the version of +Corda you're developing against: -* ``ext.corda_release_version`` defines the version of Corda +* ``ext.corda_release_version`` defines the version of Corda itself * ``ext.corda_gradle_plugins_version`` defines the version of the Corda Gradle Plugins -* ``ext.quasar_version`` defines the version of Quasar +* ``ext.quasar_version`` defines the version of Quasar, a library that we use to implement the flow framework * ``ext.kotlin_version`` defines the version of Kotlin (if using Kotlin to write your CorDapp) -``corda_gradle_plugins_versions`` are given in the form ``major.minor.patch``. You should use the same ``major`` and -``minor`` versions as the Corda version you are using, and the latest ``patch`` version. A list of all the available -versions can be found here: https://bintray.com/r3/corda/cordapp. If in doubt, you should base yourself on the version -numbers used in the ``build.gradle`` file of the -`Kotlin CorDapp Template `_ and the -`Java CorDapp Template `_. +The current versions used are as follows: -For example, to use version 3.0 of Corda, version 3.0.8 of the Corda gradle plugins, version 0.7.9 of Quasar, and -version 1.1.60 of Kotlin, you'd write: +.. parsed-literal:: -.. sourcecode:: groovy - - ext.corda_release_version = 'corda-3.0' - ext.corda_gradle_plugins_version = '3.0.8' - ext.quasar_version = '0.7.9' - ext.kotlin_version = '1.1.60' + ext.corda_release_version = '|corda_version|' + ext.corda_gradle_plugins_version = '|gradle_plugins_version|' + ext.quasar_version = '|quasar_version|' + ext.kotlin_version = '|kotlin_version|' In certain cases, you may also wish to build against the unstable Master branch. See :doc:`building-against-master`. diff --git a/docs/source/getting-set-up.rst b/docs/source/getting-set-up.rst index 7e727046fb..bd397b3cf0 100644 --- a/docs/source/getting-set-up.rst +++ b/docs/source/getting-set-up.rst @@ -6,7 +6,7 @@ Software requirements Corda uses industry-standard tools: -* **Java 8 JVM** - we require at least version **|java_version|**, but do not currently support Java 9 or higher. +* **Java 8 JVM** - we require at least version |java_version|, but do not currently support Java 9 or higher. We have tested with Oracle JDK, Amazon Corretto, and Red Hat's OpenJDK builds. Please note that OpenJDK builds usually exclude JavaFX, which our GUI tools require. * **IntelliJ IDEA** - supported versions **2017.x** and **2018.x** (with Kotlin plugin version |kotlin_version|) diff --git a/node/build.gradle b/node/build.gradle index 56dc13295a..a536c9f275 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -217,4 +217,4 @@ jar { publish { name jar.baseName -} +} \ No newline at end of file From 65e7886168ed12a1beec860c7601af9ceed17679 Mon Sep 17 00:00:00 2001 From: Florian Friemel Date: Fri, 8 Mar 2019 15:15:40 +0000 Subject: [PATCH 068/159] Fix flaky test. (#4851) --- .../net/corda/node/services/vault/VaultQueryTests.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 29d10e9a16..09d6f7e39b 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -47,6 +47,7 @@ import org.junit.Test import org.junit.rules.ExpectedException import org.junit.rules.ExternalResource import java.time.Duration +import java.time.Instant import java.time.LocalDate import java.time.ZoneOffset import java.time.temporal.ChronoUnit @@ -1609,7 +1610,12 @@ abstract class VaultQueryTestsBase : VaultQueryParties { println("$index : $any") } assertThat(results.otherResults.size).isEqualTo(402) - assertThat(results.otherResults.last()).isEqualTo(200L) + val instants = results.otherResults.filter { it is Instant }.map { it as Instant } + assertThat(instants).isSorted + val longs = results.otherResults.filter { it is Long }.map { it as Long } + assertThat(longs.size).isEqualTo(201) + assertThat(instants.size).isEqualTo(201) + assertThat(longs.sum()).isEqualTo(20100L) } } From b3b184c93e8d066ac87918230ed7881be76c1004 Mon Sep 17 00:00:00 2001 From: JamesHR3 <45565019+JamesHR3@users.noreply.github.com> Date: Fri, 8 Mar 2019 16:23:07 +0000 Subject: [PATCH 069/159] [CORDA-2701] Ensure crlCheckSoftFail config option is respected (#4854) * Plumb through the crlCheckSoftFail configuration option to bridge manager * Add crlCheckSoftFail test to bridge manager and fix equivalent proton wrapper test * Update documentation and set the node configuration default to true * Revert default change and clarify consequences of setting option to false * Remove NodeConfiguration default to leave only AMQPConfiguration default --- docs/source/corda-configuration-file.rst | 8 +++--- .../internal/bridging/AMQPBridgeManager.kt | 16 +++++++---- .../bridging/BridgeControlListener.kt | 12 ++++++--- .../net/corda/node/amqp/AMQPBridgeTest.kt | 27 ++++++++++++++++--- .../net/corda/node/amqp/ProtonWrapperTests.kt | 6 +++-- .../kotlin/net/corda/node/internal/Node.kt | 6 ++++- 6 files changed, 58 insertions(+), 17 deletions(-) diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 32032f9673..a0a669ad86 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -88,9 +88,11 @@ cordappSignerKeyFingerprintBlacklist *Default:* not defined crlCheckSoftFail - This is a boolean flag that when enabled (i.e. ``true`` value is set) causes the certificate revocation list (CRL) checking will use the soft fail mode. - The soft fail mode allows the revocation check to succeed if the revocation status cannot be determined because of a network error. - If this parameter is set to ``false`` rigorous CRL checking takes place, involving each certificate in the certificate path being checked needs to have the CRL distribution point extension set and pointing to a URL serving a valid CRL. + This is a boolean flag that when enabled (i.e. ``true`` value is set) causes certificate revocation list (CRL) checking to use soft fail mode. + Soft fail mode allows the revocation check to succeed if the revocation status cannot be determined because of a network error. + If this parameter is set to ``false`` rigorous CRL checking takes place. This involves each certificate in the certificate path being checked for a CRL distribution point extension, and that this extension points to a URL serving a valid CRL. + This means that if any CRL URL in the certificate path is inaccessible, the connection with the other party will fail and be marked as bad. + Additionally, if any certificate in the hierarchy, including the self-generated node SSL certificate, is missing a valid CRL URL, then the certificate path will be marked as invalid. *Default:* true diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt index d60a50bf76..7427a344e7 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt @@ -34,7 +34,9 @@ import kotlin.concurrent.withLock * The Netty thread pool used by the AMQPBridges is also shared and managed by the AMQPBridgeManager. */ @VisibleForTesting -class AMQPBridgeManager(config: MutualSslConfiguration, maxMessageSize: Int, +class AMQPBridgeManager(config: MutualSslConfiguration, + maxMessageSize: Int, + crlCheckSoftFail: Boolean, private val artemisMessageClientFactory: () -> ArtemisSessionProvider, private val bridgeMetricsService: BridgeMetricsService? = null) : BridgeManager { @@ -43,15 +45,19 @@ class AMQPBridgeManager(config: MutualSslConfiguration, maxMessageSize: Int, private class AMQPConfigurationImpl private constructor(override val keyStore: CertificateStore, override val trustStore: CertificateStore, - override val maxMessageSize: Int) : AMQPConfiguration { - constructor(config: MutualSslConfiguration, maxMessageSize: Int) : this(config.keyStore.get(), config.trustStore.get(), maxMessageSize) + override val maxMessageSize: Int, + override val crlCheckSoftFail: Boolean) : AMQPConfiguration { + constructor(config: MutualSslConfiguration, maxMessageSize: Int, crlCheckSoftFail: Boolean) : this(config.keyStore.get(), config.trustStore.get(), maxMessageSize, crlCheckSoftFail) } - private val amqpConfig: AMQPConfiguration = AMQPConfigurationImpl(config, maxMessageSize) + private val amqpConfig: AMQPConfiguration = AMQPConfigurationImpl(config, maxMessageSize, crlCheckSoftFail) private var sharedEventLoopGroup: EventLoopGroup? = null private var artemis: ArtemisSessionProvider? = null - constructor(config: MutualSslConfiguration, p2pAddress: NetworkHostAndPort, maxMessageSize: Int) : this(config, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) }) + constructor(config: MutualSslConfiguration, + p2pAddress: NetworkHostAndPort, + maxMessageSize: Int, + crlCheckSoftFail: Boolean) : this(config, maxMessageSize, crlCheckSoftFail, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) }) companion object { private const val NUM_BRIDGE_THREADS = 0 // Default sized pool diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt index c1fb05a20a..46e5062c66 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt @@ -20,18 +20,24 @@ import java.util.* class BridgeControlListener(val config: MutualSslConfiguration, maxMessageSize: Int, + crlCheckSoftFail: Boolean, private val artemisMessageClientFactory: () -> ArtemisSessionProvider, bridgeMetricsService: BridgeMetricsService? = null) : AutoCloseable { private val bridgeId: String = UUID.randomUUID().toString() - private val bridgeManager: BridgeManager = AMQPBridgeManager(config, maxMessageSize, - artemisMessageClientFactory, bridgeMetricsService) + private val bridgeManager: BridgeManager = AMQPBridgeManager( + config, + maxMessageSize, + crlCheckSoftFail, + artemisMessageClientFactory, + bridgeMetricsService) private val validInboundQueues = mutableSetOf() private var artemis: ArtemisSessionProvider? = null private var controlConsumer: ClientConsumer? = null constructor(config: MutualSslConfiguration, p2pAddress: NetworkHostAndPort, - maxMessageSize: Int) : this(config, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) }) + maxMessageSize: Int, + crlCheckSoftFail: Boolean) : this(config, maxMessageSize, crlCheckSoftFail, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) }) companion object { private val log = contextLogger() diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt index 02350108f0..9b4f89cae7 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt @@ -4,6 +4,7 @@ import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever import net.corda.core.crypto.toStringShort import net.corda.core.internal.div +import net.corda.core.toFuture import net.corda.core.utilities.loggerFor import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.configureWithDevSSLCertificate @@ -167,7 +168,25 @@ class AMQPBridgeTest { artemisServer.stop() } - private fun createArtemis(sourceQueueName: String?): Triple { + @Test + fun `bridge with strict CRL checking does not connect to server with invalid certificates`() { + // Note that the opposite of this test (that a connection is established if strict checking is disabled) is carried out by the + // ack/nack test above. "Strict CRL checking" means that soft fail mode is disabled. + val sourceQueueName = "internal.peers." + BOB.publicKey.toStringShort() + val (artemisServer, artemisClient, bridge) = createArtemis(sourceQueueName, crlCheckSoftFail = false) + + createAMQPServer().use { + val connectedFuture = it.onConnection.toFuture() + it.start() + val connectedResult = connectedFuture.get() + assertEquals(false, connectedResult.connected) + } + bridge.stop() + artemisClient.stop() + artemisServer.stop() + } + + private fun createArtemis(sourceQueueName: String?, crlCheckSoftFail: Boolean = true): Triple { val baseDir = temporaryFolder.root.toPath() / "artemis" val certificatesDirectory = baseDir / "certificates" val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) @@ -178,7 +197,7 @@ class AMQPBridgeTest { doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(signingCertificateStore).whenever(it).signingCertificateStore doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions - doReturn(true).whenever(it).crlCheckSoftFail + doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail doReturn(artemisAddress).whenever(it).p2pAddress doReturn(null).whenever(it).jmxMonitoringHttpPort } @@ -187,7 +206,7 @@ class AMQPBridgeTest { val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, artemisAddress, MAX_MESSAGE_SIZE) artemisServer.start() artemisClient.start() - val bridgeManager = AMQPBridgeManager(artemisConfig.p2pSslOptions, artemisAddress, MAX_MESSAGE_SIZE) + val bridgeManager = AMQPBridgeManager(artemisConfig.p2pSslOptions, artemisAddress, MAX_MESSAGE_SIZE, artemisConfig.crlCheckSoftFail) bridgeManager.start() val artemis = artemisClient.started!! if (sourceQueueName != null) { @@ -209,6 +228,7 @@ class AMQPBridgeTest { doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(signingCertificateStore).whenever(it).signingCertificateStore doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions + doReturn(true).whenever(it).crlCheckSoftFail } serverConfig.configureWithDevSSLCertificate() @@ -218,6 +238,7 @@ class AMQPBridgeTest { override val trustStore = serverConfig.p2pSslOptions.trustStore.get() override val trace: Boolean = true override val maxMessageSize: Int = maxMessageSize + override val crlCheckSoftFail: Boolean = serverConfig.crlCheckSoftFail } return AMQPServer("0.0.0.0", amqpAddress.port, diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt index 7eee3af489..f9336d6b19 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt @@ -91,8 +91,7 @@ class ProtonWrapperTests { @Test fun `AMPQ Client fails to connect when crl soft fail check is disabled`() { - val amqpServer = createServer(serverPort, CordaX500Name("Rogue 1", "London", "GB"), - maxMessageSize = MAX_MESSAGE_SIZE, crlCheckSoftFail = false) + val amqpServer = createServer(serverPort, maxMessageSize = MAX_MESSAGE_SIZE, crlCheckSoftFail = false) amqpServer.use { amqpServer.start() val amqpClient = createClient() @@ -448,6 +447,7 @@ class ProtonWrapperTests { override val trustStore = clientTruststore override val trace: Boolean = true override val maxMessageSize: Int = maxMessageSize + override val crlCheckSoftFail: Boolean = clientConfig.crlCheckSoftFail } return AMQPClient( listOf(NetworkHostAndPort("localhost", serverPort), @@ -479,6 +479,7 @@ class ProtonWrapperTests { override val trustStore = clientTruststore override val trace: Boolean = true override val maxMessageSize: Int = maxMessageSize + override val crlCheckSoftFail: Boolean = clientConfig.crlCheckSoftFail } return AMQPClient( listOf(NetworkHostAndPort("localhost", serverPort)), @@ -512,6 +513,7 @@ class ProtonWrapperTests { override val trustStore = serverTruststore override val trace: Boolean = true override val maxMessageSize: Int = maxMessageSize + override val crlCheckSoftFail: Boolean = serverConfig.crlCheckSoftFail } return AMQPServer( "0.0.0.0", diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index fa550bd233..d009c80738 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -256,7 +256,11 @@ open class Node(configuration: NodeConfiguration, startLocalRpcBroker(securityManager) } - val bridgeControlListener = BridgeControlListener(configuration.p2pSslOptions, network.serverAddress, networkParameters.maxMessageSize) + val bridgeControlListener = BridgeControlListener( + configuration.p2pSslOptions, + network.serverAddress, + networkParameters.maxMessageSize, + configuration.crlCheckSoftFail) printBasicNodeInfo("Advertised P2P messaging addresses", nodeInfo.addresses.joinToString()) val rpcServerConfiguration = RPCServerConfiguration.DEFAULT From ea263b3e54b695f88f84e151299bebf26cb23226 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Fri, 8 Mar 2019 16:39:22 +0000 Subject: [PATCH 070/159] CORDA-2569: Add "flow kill " command to Corda's shell. (#4861) * CORDA-2569: Add "flow kill " command to Corda's shell. * Add testing and documentation for RPC killFlow operation. --- .../client/jackson/internal/CordaModule.kt | 28 +++++++++++- .../client/jackson/StateMachineRunIdTest.kt | 29 ++++++++++++ .../rpc/StandaloneCordaRPCJavaClientTest.java | 16 +++---- .../kotlin/rpc/StandaloneCordaRPClientTest.kt | 44 ++++++++++++++----- .../net/corda/core/NodeVersioningTest.kt | 8 ++-- .../corda/core/cordapp/CordappSmokeTest.kt | 8 ++-- docs/source/shell.rst | 7 ++- .../rpc/proxies/AuthenticatedRpcOpsProxy.kt | 4 +- .../security/RPCSecurityManagerImpl.kt | 33 +++++++------- .../node/services/RPCSecurityManagerTest.kt | 29 +++++++----- .../net/corda/testing/node/MockServices.kt | 2 - .../testing/node/internal/DriverDSLImpl.kt | 3 +- .../net/corda/smoketesting/NodeProcess.kt | 4 +- tools/shell/build.gradle | 5 --- .../corda/tools/shell/FlowShellCommand.java | 21 ++++++--- .../corda/tools/shell/RunShellCommand.java | 6 +-- .../net/corda/tools/shell/InteractiveShell.kt | 26 +++++++++++ .../corda/tools/shell/InteractiveShellTest.kt | 33 +++++++++++++- 18 files changed, 226 insertions(+), 80 deletions(-) create mode 100644 client/jackson/src/test/kotlin/net/corda/client/jackson/StateMachineRunIdTest.kt diff --git a/client/jackson/src/main/kotlin/net/corda/client/jackson/internal/CordaModule.kt b/client/jackson/src/main/kotlin/net/corda/client/jackson/internal/CordaModule.kt index 094294fd60..14d82e2ab4 100644 --- a/client/jackson/src/main/kotlin/net/corda/client/jackson/internal/CordaModule.kt +++ b/client/jackson/src/main/kotlin/net/corda/client/jackson/internal/CordaModule.kt @@ -15,16 +15,20 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier import com.fasterxml.jackson.databind.deser.ContextualDeserializer import com.fasterxml.jackson.databind.deser.std.DelegatingDeserializer +import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.databind.node.IntNode import com.fasterxml.jackson.databind.node.ObjectNode import com.fasterxml.jackson.databind.ser.BeanPropertyWriter import com.fasterxml.jackson.databind.ser.BeanSerializerModifier +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer +import com.fasterxml.jackson.databind.ser.std.UUIDSerializer import com.google.common.primitives.Booleans import net.corda.client.jackson.JacksonSupport import net.corda.core.contracts.* import net.corda.core.crypto.* import net.corda.core.crypto.PartialMerkleTree.PartialTree +import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.* import net.corda.core.internal.DigitalSignatureWithCert import net.corda.core.internal.createComponentGroups @@ -40,7 +44,6 @@ import net.corda.core.utilities.parseAsHex import net.corda.core.utilities.toHexString import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.amqp.* -import net.corda.serialization.internal.model.LocalTypeInformation import java.math.BigDecimal import java.security.PublicKey import java.security.cert.CertPath @@ -79,6 +82,7 @@ class CordaModule : SimpleModule("corda-core") { context.setMixInAnnotations(SignatureMetadata::class.java, SignatureMetadataMixin::class.java) context.setMixInAnnotations(PartialTree::class.java, PartialTreeMixin::class.java) context.setMixInAnnotations(NodeInfo::class.java, NodeInfoMixin::class.java) + context.setMixInAnnotations(StateMachineRunId::class.java, StateMachineRunIdMixin::class.java) } } @@ -418,6 +422,28 @@ private interface SecureHashSHA256Mixin @JsonDeserialize(using = JacksonSupport.PublicKeyDeserializer::class) private interface PublicKeyMixin +@JsonSerialize(using = StateMachineRunIdSerializer::class) +@JsonDeserialize(using = StateMachineRunIdDeserializer::class) +private interface StateMachineRunIdMixin + +private class StateMachineRunIdSerializer : StdScalarSerializer(StateMachineRunId::class.java) { + private val uuidSerializer = UUIDSerializer() + + override fun isEmpty(provider: SerializerProvider?, value: StateMachineRunId): Boolean { + return uuidSerializer.isEmpty(provider, value.uuid) + } + + override fun serialize(value: StateMachineRunId, gen: JsonGenerator?, provider: SerializerProvider?) { + uuidSerializer.serialize(value.uuid, gen, provider) + } +} + +private class StateMachineRunIdDeserializer : FromStringDeserializer(StateMachineRunId::class.java) { + override fun _deserialize(value: String, ctxt: DeserializationContext?): StateMachineRunId { + return StateMachineRunId(UUID.fromString(value)) + } +} + @Suppress("unused_parameter") @ToStringSerialize private abstract class AmountMixin @JsonCreator(mode = DISABLED) constructor( diff --git a/client/jackson/src/test/kotlin/net/corda/client/jackson/StateMachineRunIdTest.kt b/client/jackson/src/test/kotlin/net/corda/client/jackson/StateMachineRunIdTest.kt new file mode 100644 index 0000000000..e91f8fd870 --- /dev/null +++ b/client/jackson/src/test/kotlin/net/corda/client/jackson/StateMachineRunIdTest.kt @@ -0,0 +1,29 @@ +package net.corda.client.jackson + +import com.fasterxml.jackson.databind.ObjectMapper +import net.corda.client.jackson.internal.CordaModule +import net.corda.core.flows.StateMachineRunId +import org.junit.Assert.assertEquals +import org.junit.Test +import java.util.* + +class StateMachineRunIdTest { + private companion object { + private const val ID = "a9da3d32-a08d-4add-a633-66bc6bf6183d" + private val jsonMapper: ObjectMapper = ObjectMapper().registerModule(CordaModule()) + } + + @Test + fun `state machine run ID deserialise`() { + val str = """"$ID"""" + val runID = jsonMapper.readValue(str, StateMachineRunId::class.java) + assertEquals(StateMachineRunId(UUID.fromString(ID)), runID) + } + + @Test + fun `state machine run ID serialise`() { + val runId = StateMachineRunId(UUID.fromString(ID)) + val str = jsonMapper.writeValueAsString(runId) + assertEquals(""""$ID"""", str) + } +} \ No newline at end of file diff --git a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java index 77fee26039..2c17ac72e3 100644 --- a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java +++ b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java @@ -25,6 +25,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; +import static java.util.Collections.singletonList; import static kotlin.test.AssertionsKt.assertEquals; import static kotlin.test.AssertionsKt.fail; import static net.corda.finance.workflows.GetBalances.getCashBalance; @@ -51,13 +52,12 @@ public class StandaloneCordaRPCJavaClientTest { } } - private List perms = Collections.singletonList("ALL"); + private List perms = singletonList("ALL"); private Set permSet = new HashSet<>(perms); - private User rpcUser = new User("user1", "test", permSet); + private User superUser = new User("superUser", "test", permSet); private AtomicInteger port = new AtomicInteger(15000); - private NodeProcess.Factory factory; private NodeProcess notary; private CordaRPCOps rpcProxy; private CordaRPCConnection connection; @@ -69,16 +69,16 @@ public class StandaloneCordaRPCJavaClientTest { port.getAndIncrement(), port.getAndIncrement(), true, - Collections.singletonList(rpcUser), + singletonList(superUser), true ); @Before public void setUp() { - factory = new NodeProcess.Factory(); + NodeProcess.Factory factory = new NodeProcess.Factory(); copyCordapps(factory, notaryConfig); notary = factory.create(notaryConfig); - connection = notary.connect(); + connection = notary.connect(superUser); rpcProxy = connection.getProxy(); notaryNodeIdentity = rpcProxy.nodeInfo().getLegalIdentities().get(0); } @@ -95,7 +95,7 @@ public class StandaloneCordaRPCJavaClientTest { } @Test - public void testCashBalances() throws NoSuchFieldException, ExecutionException, InterruptedException { + public void testCashBalances() throws ExecutionException, InterruptedException { Amount dollars123 = new Amount<>(123, Currency.getInstance("USD")); FlowHandle flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class, @@ -105,7 +105,7 @@ public class StandaloneCordaRPCJavaClientTest { flowHandle.getReturnValue().get(); Amount balance = getCashBalance(rpcProxy, Currency.getInstance("USD")); - System.out.print("Balance: " + balance + "\n"); + System.out.println("Balance: " + balance); assertEquals(dollars123, balance, "matching"); } diff --git a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt index 5731368884..7cd3d6ac46 100644 --- a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt +++ b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt @@ -3,6 +3,7 @@ package net.corda.kotlin.rpc import com.google.common.hash.Hashing import com.google.common.hash.HashingInputStream import net.corda.client.rpc.CordaRPCConnection +import net.corda.client.rpc.PermissionException import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -29,10 +30,8 @@ import net.corda.nodeapi.internal.config.User import net.corda.smoketesting.NodeConfig import net.corda.smoketesting.NodeProcess import org.apache.commons.io.output.NullOutputStream -import org.junit.After -import org.junit.Before -import org.junit.Ignore -import org.junit.Test +import org.junit.* +import org.junit.rules.ExpectedException import java.io.FilterInputStream import java.io.InputStream import java.util.* @@ -46,7 +45,10 @@ import kotlin.test.assertTrue class StandaloneCordaRPClientTest { private companion object { private val log = contextLogger() - val user = User("user1", "test", permissions = setOf("ALL")) + val superUser = User("superUser", "test", permissions = setOf("ALL")) + val nonUser = User("nonUser", "test", permissions = emptySet()) + val rpcUser = User("rpcUser", "test", permissions = setOf("InvokeRpc.startFlow", "InvokeRpc.killFlow")) + val flowUser = User("flowUser", "test", permissions = setOf("StartFlow.net.corda.finance.flows.CashIssueFlow")) val port = AtomicInteger(15200) const val attachmentSize = 2116 val timeout = 60.seconds @@ -65,15 +67,18 @@ class StandaloneCordaRPClientTest { rpcPort = port.andIncrement, rpcAdminPort = port.andIncrement, isNotary = true, - users = listOf(user) + users = listOf(superUser, nonUser, rpcUser, flowUser) ) + @get:Rule + val exception: ExpectedException = ExpectedException.none() + @Before fun setUp() { factory = NodeProcess.Factory() StandaloneCordaRPCJavaClientTest.copyCordapps(factory, notaryConfig) notary = factory.create(notaryConfig) - connection = notary.connect() + connection = notary.connect(superUser) rpcProxy = connection.proxy notaryNode = fetchNotaryIdentity() notaryNodeIdentity = rpcProxy.nodeInfo().legalIdentitiesAndCerts.first().party @@ -81,9 +86,7 @@ class StandaloneCordaRPClientTest { @After fun done() { - try { - connection.close() - } finally { + connection.use { notary.close() } } @@ -232,6 +235,27 @@ class StandaloneCordaRPClientTest { assertEquals(629.DOLLARS, balance) } + @Test + fun `test kill flow without killFlow permission`() { + exception.expect(PermissionException::class.java) + exception.expectMessage("User not authorized to perform RPC call killFlow") + + val flowHandle = rpcProxy.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), notaryNodeIdentity) + notary.connect(nonUser).use { connection -> + val rpcProxy = connection.proxy + rpcProxy.killFlow(flowHandle.id) + } + } + + @Test + fun `test kill flow with killFlow permission`() { + val flowHandle = rpcProxy.startFlow(::CashIssueFlow, 83.DOLLARS, OpaqueBytes.of(0), notaryNodeIdentity) + notary.connect(rpcUser).use { connection -> + val rpcProxy = connection.proxy + assertTrue(rpcProxy.killFlow(flowHandle.id)) + } + } + private fun fetchNotaryIdentity(): NodeInfo { val nodeInfo = rpcProxy.networkMapSnapshot() assertEquals(1, nodeInfo.size) diff --git a/core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt b/core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt index 33216034da..9cca6d959a 100644 --- a/core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt +++ b/core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt @@ -21,7 +21,7 @@ import kotlin.streams.toList class NodeVersioningTest { private companion object { - val user = User("user1", "test", permissions = setOf("ALL")) + val superUser = User("superUser", "test", permissions = setOf("ALL")) val port = AtomicInteger(15100) } @@ -33,7 +33,7 @@ class NodeVersioningTest { rpcPort = port.andIncrement, rpcAdminPort = port.andIncrement, isNotary = true, - users = listOf(user) + users = listOf(superUser) ) private val aliceConfig = NodeConfig( @@ -42,7 +42,7 @@ class NodeVersioningTest { rpcPort = port.andIncrement, rpcAdminPort = port.andIncrement, isNotary = false, - users = listOf(user) + users = listOf(superUser) ) private lateinit var notary: NodeProcess @@ -73,7 +73,7 @@ class NodeVersioningTest { selfCordapp.copyToDirectory(cordappsDir) factory.create(aliceConfig).use { alice -> - alice.connect().use { + alice.connect(superUser).use { val rpc = it.proxy assertThat(rpc.protocolVersion).isEqualTo(PLATFORM_VERSION) assertThat(rpc.nodeInfo().platformVersion).isEqualTo(PLATFORM_VERSION) diff --git a/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt b/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt index 1d51f74194..6e9cf72bff 100644 --- a/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt +++ b/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt @@ -38,7 +38,7 @@ import kotlin.streams.toList class CordappSmokeTest { private companion object { - val user = User("user1", "test", permissions = setOf("ALL")) + val superUser = User("superUser", "test", permissions = setOf("ALL")) val port = AtomicInteger(15100) } @@ -50,7 +50,7 @@ class CordappSmokeTest { rpcPort = port.andIncrement, rpcAdminPort = port.andIncrement, isNotary = true, - users = listOf(user) + users = listOf(superUser) ) private val aliceConfig = NodeConfig( @@ -59,7 +59,7 @@ class CordappSmokeTest { rpcPort = port.andIncrement, rpcAdminPort = port.andIncrement, isNotary = false, - users = listOf(user) + users = listOf(superUser) ) private lateinit var notary: NodeProcess @@ -92,7 +92,7 @@ class CordappSmokeTest { createDummyNodeInfo(additionalNodeInfoDir) factory.create(aliceConfig).use { alice -> - alice.connect().use { connectionToAlice -> + alice.connect(superUser).use { connectionToAlice -> val aliceIdentity = connectionToAlice.proxy.nodeInfo().legalIdentitiesAndCerts.first().party val future = connectionToAlice.proxy.startFlow(::GatherContextsFlow, aliceIdentity).returnValue val (sessionInitContext, sessionConfirmContext) = future.getOrThrow() diff --git a/docs/source/shell.rst b/docs/source/shell.rst index c6326b2c9f..c0cd525c37 100644 --- a/docs/source/shell.rst +++ b/docs/source/shell.rst @@ -26,9 +26,11 @@ Permissions When accessing the shell (embedded, standalone, via SSH) RPC permissions are required. This is because the shell actually communicates with the node using RPC calls. -* Watching flows (``flow watch``) requires ``InvokeRpc.stateMachinesFeed`` +* Watching flows (``flow watch``) requires ``InvokeRpc.stateMachinesFeed``. * Starting flows requires ``InvokeRpc.startTrackedFlowDynamic``, ``InvokeRpc.registeredFlows`` and ``InvokeRpc.wellKnownPartyFromX500Name``, as well as a - permission for the flow being started + permission for the flow being started. +* Killing flows (``flow kill``) requires ``InvokeRpc.killFlow``. This currently + allows the user to kill *any* flow, so please be careful when granting it! The shell via the local terminal -------------------------------- @@ -231,6 +233,7 @@ The shell also has special commands for working with flows: names and types of the arguments will be used to try and automatically determine which one to use. If the match against available constructors is unclear, the reasons each available constructor failed to match will be printed out. In the case of an ambiguous match, the first applicable constructor will be used +* ``flow kill`` kills a single flow, as identified by its UUID. Parameter syntax **************** diff --git a/node/src/main/kotlin/net/corda/node/internal/rpc/proxies/AuthenticatedRpcOpsProxy.kt b/node/src/main/kotlin/net/corda/node/internal/rpc/proxies/AuthenticatedRpcOpsProxy.kt index 2c1e7af222..6b908086f8 100644 --- a/node/src/main/kotlin/net/corda/node/internal/rpc/proxies/AuthenticatedRpcOpsProxy.kt +++ b/node/src/main/kotlin/net/corda/node/internal/rpc/proxies/AuthenticatedRpcOpsProxy.kt @@ -39,14 +39,14 @@ internal class AuthenticatedRpcOpsProxy(private val delegate: CordaRPCOps) : Cor } private class PermissionsEnforcingInvocationHandler(override val delegate: CordaRPCOps, private val context: () -> RpcAuthContext) : InvocationHandlerTemplate { - override fun invoke(proxy: Any, method: Method, arguments: Array?) = guard(method.name, context, { super.invoke(proxy, method, arguments) }) + override fun invoke(proxy: Any, method: Method, arguments: Array?) = guard(method.name, context) { super.invoke(proxy, method, arguments) } } } private fun guard(methodName: String, context: () -> RpcAuthContext, action: () -> RESULT) = guard(methodName, emptyList(), context, action) private fun guard(methodName: String, args: List>, context: () -> RpcAuthContext, action: () -> RESULT): RESULT { - if (!context().isPermitted(methodName, *(args.map { it.name }.toTypedArray()))) { + if (!context().isPermitted(methodName, *(args.map(Class<*>::getName).toTypedArray()))) { throw PermissionException("User not authorized to perform RPC call $methodName with target $args") } else { return action() diff --git a/node/src/main/kotlin/net/corda/node/internal/security/RPCSecurityManagerImpl.kt b/node/src/main/kotlin/net/corda/node/internal/security/RPCSecurityManagerImpl.kt index 5186e234b7..8088c8b4ec 100644 --- a/node/src/main/kotlin/net/corda/node/internal/security/RPCSecurityManagerImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/security/RPCSecurityManagerImpl.kt @@ -26,7 +26,6 @@ import org.apache.shiro.realm.AuthorizingRealm import org.apache.shiro.realm.jdbc.JdbcRealm import org.apache.shiro.subject.PrincipalCollection import org.apache.shiro.subject.SimplePrincipalCollection -import java.io.Closeable import java.util.concurrent.ConcurrentHashMap import javax.security.auth.login.FailedLoginException @@ -66,7 +65,7 @@ class RPCSecurityManagerImpl(config: AuthServiceConfig, cacheFactory: NamedCache manager = manager) override fun close() { - manager.realms?.filterIsInstance()?.forEach { it.close() } + manager.realms?.filterIsInstance()?.forEach(AutoCloseable::close) manager.destroy() } @@ -197,31 +196,31 @@ private fun buildCredentialMatcher(type: PasswordEncryption) = when (type) { } class InMemoryRealm(users: List, - realmId: String, - passwordEncryption: PasswordEncryption = PasswordEncryption.NONE) : AuthorizingRealm() { + realmId: String, + passwordEncryption: PasswordEncryption = PasswordEncryption.NONE) : AuthorizingRealm() { private val authorizationInfoByUser: Map private val authenticationInfoByUser: Map init { permissionResolver = RPCPermissionResolver - users.forEach { - require(it.username.matches("\\w+".toRegex())) { - "Username ${it.username} contains invalid characters" + users.forEach { user -> + require(user.username.matches("\\w+".toRegex())) { + "Username ${user.username} contains invalid characters" } } - val resolvePermission = { s: String -> permissionResolver.resolvePermission(s) } - authorizationInfoByUser = users.associate { - it.username to SimpleAuthorizationInfo().apply { - objectPermissions = it.permissions.map { resolvePermission(it) }.toSet() + val resolvePermission = permissionResolver::resolvePermission + authorizationInfoByUser = users.associate { user -> + user.username to SimpleAuthorizationInfo().apply { + objectPermissions = user.permissions.map(resolvePermission).toSet() roles = emptySet() stringPermissions = emptySet() } } - authenticationInfoByUser = users.associate { - it.username to SimpleAuthenticationInfo().apply { - credentials = it.password - principals = SimplePrincipalCollection(it.username, realmId) + authenticationInfoByUser = users.associate { user -> + user.username to SimpleAuthenticationInfo().apply { + credentials = user.password + principals = SimplePrincipalCollection(user.username, realmId) } } credentialsMatcher = buildCredentialMatcher(passwordEncryption) @@ -236,7 +235,7 @@ class InMemoryRealm(users: List, authorizationInfoByUser[principals.primaryPrincipal as String] } -private class NodeJdbcRealm(config: SecurityConfiguration.AuthService.DataSource) : JdbcRealm(), Closeable { +private class NodeJdbcRealm(config: SecurityConfiguration.AuthService.DataSource) : JdbcRealm(), AutoCloseable { init { credentialsMatcher = buildCredentialMatcher(config.passwordEncryption) @@ -246,7 +245,7 @@ private class NodeJdbcRealm(config: SecurityConfiguration.AuthService.DataSource } override fun close() { - (dataSource as? Closeable)?.close() + (dataSource as? AutoCloseable)?.close() } } diff --git a/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt b/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt index 64ed341993..972272370a 100644 --- a/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt @@ -7,6 +7,7 @@ import net.corda.node.internal.security.Password import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.security.tryAuthenticate import net.corda.node.services.Permissions.Companion.invokeRpc +import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.config.SecurityConfiguration import net.corda.nodeapi.internal.config.User import net.corda.testing.internal.TestingNamedCacheFactory @@ -31,7 +32,7 @@ class RPCSecurityManagerTest { @Test fun `Generic RPC call authorization`() { checkUserActions( - permitted = setOf(arrayListOf("nodeInfo"), arrayListOf("notaryIdentities")), + permitted = setOf(listOf("nodeInfo"), listOf("notaryIdentities")), permissions = setOf( invokeRpc(CordaRPCOps::nodeInfo), invokeRpc(CordaRPCOps::notaryIdentities))) @@ -40,41 +41,49 @@ class RPCSecurityManagerTest { @Test fun `Flow invocation authorization`() { checkUserActions( - permissions = setOf(Permissions.startFlow()), + permissions = setOf(startFlow()), permitted = setOf( - arrayListOf("startTrackedFlowDynamic", "net.corda.node.services.RPCSecurityManagerTest\$DummyFlow"), - arrayListOf("startFlowDynamic", "net.corda.node.services.RPCSecurityManagerTest\$DummyFlow"))) + listOf("startTrackedFlowDynamic", DummyFlow::class.java.name), + listOf("startFlowDynamic", DummyFlow::class.java.name))) } @Test fun `Check startFlow RPC permission implies startFlowDynamic`() { checkUserActions( permissions = setOf(invokeRpc("startFlow")), - permitted = setOf(arrayListOf("startFlow"), arrayListOf("startFlowDynamic"))) + permitted = setOf(listOf("startFlow"), listOf("startFlowDynamic"))) } @Test fun `Check startTrackedFlow RPC permission implies startTrackedFlowDynamic`() { checkUserActions( - permitted = setOf(arrayListOf("startTrackedFlow"), arrayListOf("startTrackedFlowDynamic")), + permitted = setOf(listOf("startTrackedFlow"), listOf("startTrackedFlowDynamic")), permissions = setOf(invokeRpc("startTrackedFlow"))) } + @Test + fun `check killFlow RPC permission accepted`() { + checkUserActions( + permitted = setOf(listOf("killFlow")), + permissions = setOf(invokeRpc(CordaRPCOps::killFlow)) + ) + } + @Test fun `Admin authorization`() { checkUserActions( permissions = setOf("all"), - permitted = allActions.map { arrayListOf(it) }.toSet()) + permitted = allActions.map { listOf(it) }.toSet()) } @Test fun `flows draining mode permissions`() { checkUserActions( - permitted = setOf(arrayListOf("setFlowsDrainingModeEnabled")), + permitted = setOf(listOf("setFlowsDrainingModeEnabled")), permissions = setOf(invokeRpc(CordaRPCOps::setFlowsDrainingModeEnabled)) ) checkUserActions( - permitted = setOf(arrayListOf("isFlowsDrainingModeEnabled")), + permitted = setOf(listOf("isFlowsDrainingModeEnabled")), permissions = setOf(invokeRpc(CordaRPCOps::isFlowsDrainingModeEnabled)) ) } @@ -134,7 +143,7 @@ class RPCSecurityManagerTest { users = listOf(User(username, "password", setOf())), id = AuthServiceId("TEST")) } - private fun checkUserActions(permissions: Set, permitted: Set>) { + private fun checkUserActions(permissions: Set, permitted: Set>) { val user = User(username = "user", password = "password", permissions = permissions) val userRealms = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(listOf(user)), TestingNamedCacheFactory()) val disabled = allActions.filter { !permitted.contains(listOf(it)) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index 1a0a0a9dff..bda6cbe7c5 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -314,12 +314,10 @@ open class MockServices private constructor( constructor(initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService()) : this(listOf(getCallerPackage(MockServices::class)!!), TestIdentity(initialIdentityName), identityService) - @JvmOverloads constructor(cordappPackages: List, initialIdentityName: CordaX500Name, identityService: IdentityService, networkParameters: NetworkParameters) : this(cordappPackages, TestIdentity(initialIdentityName), identityService, networkParameters) - @JvmOverloads constructor(cordappPackages: List, initialIdentityName: CordaX500Name, identityService: IdentityService, networkParameters: NetworkParameters, key: KeyPair) : this(cordappPackages, TestIdentity(initialIdentityName, key), identityService, networkParameters) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index b080857285..14bbe399f8 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -695,7 +695,8 @@ class DriverDSLImpl( Permissions.invokeRpc(CordaRPCOps::internalFindVerifiedTransaction), Permissions.invokeRpc("vaultQueryBy"), Permissions.invokeRpc("vaultTrackBy"), - Permissions.invokeRpc(CordaRPCOps::registeredFlows) + Permissions.invokeRpc(CordaRPCOps::registeredFlows), + Permissions.invokeRpc(CordaRPCOps::killFlow) ) private fun oneOf(array: Array) = array[Random().nextInt(array.size)] diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt index 7f4034ce0f..79603dbefa 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt @@ -9,6 +9,7 @@ import net.corda.core.node.NotaryInfo import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.nodeapi.internal.DevIdentityGenerator +import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.common.internal.asContextEnv import net.corda.testing.common.internal.checkNotOnClasspath @@ -33,8 +34,7 @@ class NodeProcess( private val log = contextLogger() } - fun connect(): CordaRPCConnection { - val user = config.users[0] + fun connect(user: User): CordaRPCConnection { return client.start(user.username, user.password) } diff --git a/tools/shell/build.gradle b/tools/shell/build.gradle index 36ee942432..8fef472421 100644 --- a/tools/shell/build.gradle +++ b/tools/shell/build.gradle @@ -22,11 +22,6 @@ sourceSets { srcDir file('src/integration-test/resources') } } - test { - resources { - srcDir file('src/test/resources') - } - } } dependencies { diff --git a/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java b/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java index 53bb921d3d..c5bbffe9e4 100644 --- a/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java +++ b/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java @@ -15,12 +15,12 @@ import org.slf4j.LoggerFactory; import java.util.*; -import static java.util.stream.Collectors.joining; +import static net.corda.tools.shell.InteractiveShell.killFlowById; import static net.corda.tools.shell.InteractiveShell.runFlowByNameFragment; import static net.corda.tools.shell.InteractiveShell.runStateMachinesView; @Man( - "Allows you to start flows, list the ones available and to watch flows currently running on the node.\n\n" + + "Allows you to start and kill flows, list the ones available and to watch flows currently running on the node.\n\n" + "Starting flow is the primary way in which you command the node to change the ledger.\n\n" + "This command is generic, so the right way to use it depends on the flow you wish to start. You can use the 'flow start'\n" + "command with either a full class name, or a substring of the class name that's unambiguous. The parameters to the \n" + @@ -28,7 +28,7 @@ import static net.corda.tools.shell.InteractiveShell.runStateMachinesView; ) public class FlowShellCommand extends InteractiveShellCommand { - private static Logger logger = LoggerFactory.getLogger(FlowShellCommand.class); + private static final Logger logger = LoggerFactory.getLogger(FlowShellCommand.class); @Command @Usage("Start a (work)flow on the node. This is how you can change the ledger.") @@ -36,13 +36,13 @@ public class FlowShellCommand extends InteractiveShellCommand { @Usage("The class name of the flow to run, or an unambiguous substring") @Argument String name, @Usage("The data to pass as input") @Argument(unquote = false) List input ) { - logger.info("Executing command \"flow start {} {}\",", name, (input != null) ? input.stream().collect(joining(" ")) : ""); + logger.info("Executing command \"flow start {} {}\",", name, (input != null) ? String.join(" ", input) : ""); startFlow(name, input, out, ops(), ansiProgressRenderer(), objectMapper(null)); } // TODO Limit number of flows shown option? @Command - @Usage("watch information about state machines running on the node with result information") + @Usage("Watch information about state machines running on the node with result information.") public void watch(InvocationContext context) throws Exception { logger.info("Executing command \"flow watch\"."); runStateMachinesView(out, ops()); @@ -63,11 +63,20 @@ public class FlowShellCommand extends InteractiveShellCommand { } @Command - @Usage("list flows that user can start") + @Usage("List flows that user can start.") public void list(InvocationContext context) throws Exception { logger.info("Executing command \"flow list\"."); for (String name : ops().registeredFlows()) { context.provide(name + System.lineSeparator()); } } + + @Command + @Usage("Kill a flow that is running on this node.") + public void kill( + @Usage("The UUID for the flow that we wish to kill") @Argument String id + ) { + logger.info("Executing command \"flow kill {}\".", id); + killFlowById(id, out, ops(), objectMapper(null)); + } } \ No newline at end of file diff --git a/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java b/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java index 87f471092e..c5ac48399b 100644 --- a/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java +++ b/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java @@ -1,6 +1,5 @@ package net.corda.tools.shell; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import net.corda.client.jackson.StringToMethodCallParser; @@ -20,14 +19,13 @@ import java.util.Map; import java.util.Set; import static java.util.Comparator.comparing; -import static java.util.stream.Collectors.joining; // Note that this class cannot be converted to Kotlin because CRaSH does not understand InvocationContext> which // is the closest you can get in Kotlin to raw types. public class RunShellCommand extends InteractiveShellCommand { - private static Logger logger = LoggerFactory.getLogger(RunShellCommand.class); + private static final Logger logger = LoggerFactory.getLogger(RunShellCommand.class); @Command @Man( @@ -39,7 +37,7 @@ public class RunShellCommand extends InteractiveShellCommand { @Usage("runs a method from the CordaRPCOps interface on the node.") public Object main(InvocationContext context, @Usage("The command to run") @Argument(unquote = false) List command) { - logger.info("Executing command \"run {}\",", (command != null) ? command.stream().collect(joining(" ")) : ""); + logger.info("Executing command \"run {}\",", (command != null) ? String.join(" ", command) : ""); StringToMethodCallParser parser = new StringToMethodCallParser<>(CordaRPCOps.class, objectMapper(InteractiveShell.getCordappsClassloader())); if (command == null) { diff --git a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt index bff6a27ece..86444f179f 100644 --- a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt +++ b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt @@ -1,6 +1,7 @@ package net.corda.tools.shell import com.fasterxml.jackson.core.JsonFactory +import com.fasterxml.jackson.databind.JsonMappingException import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.module.SimpleModule @@ -16,6 +17,7 @@ import net.corda.core.CordaException import net.corda.core.concurrent.CordaFuture import net.corda.core.contracts.UniqueIdentifier import net.corda.core.flows.FlowLogic +import net.corda.core.flows.StateMachineRunId import net.corda.core.internal.* import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.openFuture @@ -348,6 +350,30 @@ object InteractiveShell { return innerLoop(type) } + @JvmStatic + fun killFlowById(id: String, + output: RenderPrintWriter, + rpcOps: CordaRPCOps, + inputObjectMapper: ObjectMapper = createYamlInputMapper(rpcOps)) { + try { + val runId = try { + inputObjectMapper.readValue(id, StateMachineRunId::class.java) + } catch (e: JsonMappingException) { + output.println("Cannot parse flow ID of '$id' - expecting a UUID.", Color.red) + log.error("Failed to parse flow ID", e) + return + } + + if (rpcOps.killFlow(runId)) { + output.println("Killed flow $runId", Color.yellow) + } else { + output.println("Failed to kill flow $runId", Color.red) + } + } finally { + output.flush() + } + } + // TODO: This utility is generally useful and might be better moved to the node class, or an RPC, if we can commit to making it stable API. /** * Given a [FlowLogic] class and a string in one-line Yaml form, finds an applicable constructor and starts diff --git a/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt b/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt index 045e3add54..df52553a74 100644 --- a/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt +++ b/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.type.TypeFactory import com.fasterxml.jackson.dataformat.yaml.YAMLFactory -import com.nhaarman.mockito_kotlin.eq +import com.nhaarman.mockito_kotlin.any import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.verify import com.nhaarman.mockito_kotlin.whenever @@ -30,6 +30,7 @@ import net.corda.testing.core.TestIdentity import net.corda.testing.core.getTestPartyAndCertificate import net.corda.testing.internal.DEV_ROOT_CA import org.crsh.command.InvocationContext +import org.crsh.text.Color import org.crsh.text.RenderPrintWriter import org.junit.Before import org.junit.Test @@ -128,7 +129,7 @@ class InteractiveShellTest { } private fun objectMapperWithClassLoader(classLoader: ClassLoader?): ObjectMapper { - val objectMapper = ObjectMapper() + val objectMapper = JacksonSupport.createNonRpcMapper() val tf = TypeFactory.defaultInstance().withClassLoader(classLoader) objectMapper.typeFactory = tf @@ -231,6 +232,34 @@ class InteractiveShellTest { verify(printWriter).println(NETWORK_MAP_JSON_PAYLOAD.replace("\n", System.lineSeparator())) } + @Test + fun killFlowWithNonsenseID() { + InteractiveShell.killFlowById("nonsense", printWriter, cordaRpcOps, om) + verify(printWriter).println("Cannot parse flow ID of 'nonsense' - expecting a UUID.", Color.red) + verify(printWriter).flush() + } + + @Test + fun killFlowFailure() { + val runId = StateMachineRunId.createRandom() + whenever(cordaRpcOps.killFlow(any())).thenReturn(false) + + InteractiveShell.killFlowById(runId.uuid.toString(), printWriter, cordaRpcOps, om) + verify(cordaRpcOps).killFlow(runId) + verify(printWriter).println("Failed to kill flow $runId", Color.red) + verify(printWriter).flush() + } + + @Test + fun killFlowSuccess() { + val runId = StateMachineRunId.createRandom() + whenever(cordaRpcOps.killFlow(any())).thenReturn(true) + + InteractiveShell.killFlowById(runId.uuid.toString(), printWriter, cordaRpcOps, om) + verify(cordaRpcOps).killFlow(runId) + verify(printWriter).println("Killed flow $runId", Color.yellow) + verify(printWriter).flush() + } } @ToStringSerialize From 25851c78a36ae6f70802c6e92d2df67d17dc0ea5 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Fri, 8 Mar 2019 16:42:03 +0000 Subject: [PATCH 071/159] CORDA-2703: Fix use of commas in node.conf examples. (#4852) --- docs/source/clientrpc.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/source/clientrpc.rst b/docs/source/clientrpc.rst index bc198f51f7..e63b28d61e 100644 --- a/docs/source/clientrpc.rst +++ b/docs/source/clientrpc.rst @@ -70,7 +70,7 @@ RPC users are created by adding them to the ``rpcUsers`` list in the node's ``no username=exampleUser password=examplePass permissions=[] - } + }, ... ] @@ -93,7 +93,7 @@ You provide an RPC user with the permission to start a specific flow using the s "StartFlow.net.corda.flows.ExampleFlow1", "StartFlow.net.corda.flows.ExampleFlow2" ] - } + }, ... ] @@ -111,7 +111,7 @@ You can also provide an RPC user with the permission to start any flow using the permissions=[ "InvokeRpc.startFlow" ] - } + }, ... ] @@ -132,7 +132,7 @@ You provide an RPC user with the permission to perform a specific RPC operation "InvokeRpc.nodeInfo", "InvokeRpc.networkMapSnapshot" ] - } + }, ... ] @@ -152,7 +152,7 @@ You can provide an RPC user with the permission to perform any RPC operation (in permissions=[ "ALL" ] - } + }, ... ] @@ -182,8 +182,8 @@ passwords in hash-encrypted format and enable in-memory caching of users data: security = { authService = { dataSource = { - type = "DB", - passwordEncryption = "SHIRO_1_CRYPT", + type = "DB" + passwordEncryption = "SHIRO_1_CRYPT" connection = { jdbcUrl = "" username = "" @@ -210,11 +210,11 @@ of ``INMEMORY`` type: security = { authService = { dataSource = { - type = "INMEMORY", + type = "INMEMORY" users = [ { - username = "", - password = "", + username = "" + password = "" permissions = ["", "", ...] }, ... From 061db8b1a1ac1fa9f1a063caf7ce4f009aa283db Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Fri, 8 Mar 2019 17:14:14 +0000 Subject: [PATCH 072/159] ENT-3256 Cleaner way to override (#4862) --- node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 562f17cdfa..8a1bc980c8 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -184,7 +184,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, @Suppress("LeakingThis") val vaultService = makeVaultService(keyManagementService, servicesForResolution, database, cordappLoader).tokenize() val nodeProperties = NodePropertiesPersistentStore(StubbedNodeUniqueIdProvider::value, database, cacheFactory) - open val flowLogicRefFactory = FlowLogicRefFactoryImpl(cordappLoader.appClassLoader) + val flowLogicRefFactory = makeFlowLogicRefFactoryImpl() // TODO Cancelling parameters updates - if we do that, how we ensure that no one uses cancelled parameters in the transactions? val networkMapUpdater = NetworkMapUpdater( networkMapCache, @@ -541,6 +541,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration, ) } + // Extracted into a function to allow overriding in subclasses. + protected open fun makeFlowLogicRefFactoryImpl() = FlowLogicRefFactoryImpl(cordappLoader.appClassLoader) + private fun makeCordappLoader(configuration: NodeConfiguration, versionInfo: VersionInfo): CordappLoader { val generatedCordapps = mutableListOf(VirtualCordapp.generateCore(versionInfo)) notaryLoader?.builtInNotary?.let { notaryImpl -> From 9648b16ff10567aeb00afc77df217b6a80539394 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Sat, 9 Mar 2019 18:26:21 +0000 Subject: [PATCH 073/159] CORDA-2725: Set DJVM CLI logging threshold to "TRACE". (#4867) - Configure root logging level programmatically. - Support DEBUG logging level. --- .../kotlin/net/corda/djvm/tools/cli/CommandBase.kt | 14 ++++++++++++++ djvm/cli/src/main/resources/log4j2.xml | 2 +- .../kotlin/net/corda/djvm/messages/Severity.kt | 7 ++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt index d4a3c4afb8..13c337acb3 100644 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt +++ b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt @@ -8,6 +8,8 @@ import net.corda.djvm.references.ClassReference import net.corda.djvm.references.EntityReference import net.corda.djvm.references.MemberReference import net.corda.djvm.rewiring.SandboxClassLoadingException +import org.apache.logging.log4j.Level +import org.apache.logging.log4j.core.config.Configurator import picocli.CommandLine import picocli.CommandLine.Help.Ansi import picocli.CommandLine.Option @@ -102,6 +104,7 @@ abstract class CommandBase : Callable { printError("Error: Cannot set verbose and quiet modes at the same time") return false } + configureLogging() return try { handleCommand() } catch (exception: Throwable) { @@ -110,6 +113,17 @@ abstract class CommandBase : Callable { } } + private fun configureLogging() { + val logLevel = when(level) { + Severity.ERROR -> Level.ERROR + Severity.WARNING -> Level.WARN + Severity.INFORMATIONAL -> Level.INFO + Severity.DEBUG -> Level.DEBUG + Severity.TRACE -> Level.TRACE + } + Configurator.setRootLevel(logLevel) + } + protected fun printException(exception: Throwable) = when (exception) { is SandboxClassLoadingException -> { printMessages(exception.messages, exception.classOrigins) diff --git a/djvm/cli/src/main/resources/log4j2.xml b/djvm/cli/src/main/resources/log4j2.xml index 93e84b6252..648a657dc3 100644 --- a/djvm/cli/src/main/resources/log4j2.xml +++ b/djvm/cli/src/main/resources/log4j2.xml @@ -1,7 +1,7 @@ - + diff --git a/djvm/src/main/kotlin/net/corda/djvm/messages/Severity.kt b/djvm/src/main/kotlin/net/corda/djvm/messages/Severity.kt index 1d067b950b..9b04b920ef 100644 --- a/djvm/src/main/kotlin/net/corda/djvm/messages/Severity.kt +++ b/djvm/src/main/kotlin/net/corda/djvm/messages/Severity.kt @@ -12,7 +12,12 @@ enum class Severity(val shortName: String, val precedence: Int, val color: Strin /** * Trace message. */ - TRACE("TRACE", 3, null), + TRACE("TRACE", 4, null), + + /** + * Debug message. + */ + DEBUG("DEBUG", 3, null), /** * Informational message. From ec5cbc29719eb652aeba36d07a0648732b9b57d2 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Sat, 9 Mar 2019 18:27:45 +0000 Subject: [PATCH 074/159] CORDA-2721: Update `djvm check` documentation, and fix CLI tool installation. (#4865) * CORDA-2721: Fix DJVM CLI installation and runtime scripts. * Update DJVM documentation to explain about `RuleViolationError`. * CORDA-2721: Add comment about constants.properties being parsed by DJVM CLI scripts. --- constants.properties | 2 ++ djvm/shell/djvm | 2 +- djvm/shell/install | 2 +- docs/source/key-concepts-djvm.rst | 8 ++++++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/constants.properties b/constants.properties index 869bc9cc21..3d170ec880 100644 --- a/constants.properties +++ b/constants.properties @@ -2,6 +2,8 @@ # because some versions here need to be matched by app authors in # their own projects. So don't get fancy with syntax! +# It is also parsed by the scripts in djvm/shell/. + cordaVersion=5.0-SNAPSHOT gradlePluginsVersion=4.0.42 kotlinVersion=1.2.71 diff --git a/djvm/shell/djvm b/djvm/shell/djvm index ec3c19e5a7..645a50b0ba 100755 --- a/djvm/shell/djvm +++ b/djvm/shell/djvm @@ -3,7 +3,7 @@ file="${BASH_SOURCE[0]}" linked_file="$(test -L "$file" && readlink "$file" || echo "$file")" base_dir="$(cd "$(dirname "$linked_file")/../" && pwd)" -version="$(cat $base_dir/../build.gradle | sed -n 's/^[ ]*ext\.corda_release_version[ =]*"\([^"]*\)".*$/\1/p')" +version="$(cat $base_dir/../constants.properties | sed -n 's/^[ ]*cordaVersion[ =]*\(.*\).*$/\1/p')" jar_file="$base_dir/cli/build/libs/corda-djvm-$version-cli.jar" CLASSPATH="${CLASSPATH:-}" diff --git a/djvm/shell/install b/djvm/shell/install index 7e458e0bb4..5dba7ecd02 100755 --- a/djvm/shell/install +++ b/djvm/shell/install @@ -2,7 +2,7 @@ file="${BASH_SOURCE[0]}" base_dir="$(cd "$(dirname "$file")/" && pwd)" -version="$(cat $base_dir/../../build.gradle | sed -n 's/^[ ]*ext\.corda_release_version[ =]*"\([^"]*\)".*$/\1/p')" +version="$(cat $base_dir/../../constants.properties | sed -n 's/^[ ]*cordaVersion[ =]*\(.*\).*$/\1/p')" # Build DJVM module and CLI cd "$base_dir/.." diff --git a/docs/source/key-concepts-djvm.rst b/docs/source/key-concepts-djvm.rst index a3c5755f1f..4566c27b9c 100644 --- a/docs/source/key-concepts-djvm.rst +++ b/docs/source/key-concepts-djvm.rst @@ -93,7 +93,9 @@ type's members. This is what controls things like ensuring that all methods impl and normalisation of synchronised methods. Lastly, there is a set of emitters. These are used to instrument the byte code for cost accounting purposes, and also -to inject code for checks that we want to perform at runtime or modifications to out-of-the-box behaviour. +to inject code for checks that we want to perform at runtime or modifications to out-of-the-box behaviour. Many of +these emitters will rewrite non-deterministic operations to throw ``RuleViolationError`` exceptions instead, which +means that the ultimate proof that a function is *truly* deterministic is that it executes successfully inside the DJVM. Static Byte Code Analysis @@ -342,7 +344,9 @@ The output should be pretty self-explanatory, but just to summarise: Other commands to be aware of are: - * ``djvm check`` which allows you to perform the up-front static analysis without running the code. + * ``djvm check`` which allows you to perform some up-front static analysis without running the code. However, be aware + that the DJVM also transforms some non-deterministic operations into ``RuleViolationError`` exceptions. A successful + ``check`` therefore does *not* guarantee that the code will behave correctly at runtime. * ``djvm inspect`` which allows you to inspect what byte code modifications will be applied to a class. From b6fe3b2a81c50092ac82cd74bee225185da0716f Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Sun, 10 Mar 2019 14:39:22 +0000 Subject: [PATCH 075/159] CORDA-2725: Include DEBUG in DJVM CLI help's list of logging levels. (#4868) --- .../src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt index 13c337acb3..603c480af4 100644 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt +++ b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt @@ -21,7 +21,7 @@ abstract class CommandBase : Callable { @Option( names = ["-l", "--level"], - description = ["The minimum severity level to log (TRACE, INFO, WARNING or ERROR."], + description = ["The minimum severity level to log (TRACE, DEBUG, INFO, WARNING or ERROR."], converter = [SeverityConverter::class] ) protected var level: Severity = Severity.WARNING @@ -276,4 +276,4 @@ abstract class CommandBase : Callable { } } -} \ No newline at end of file +} From dc83afb4de943580b059430c8dede6750b1d7d33 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 11 Mar 2019 16:48:35 +0000 Subject: [PATCH 076/159] CORDA-2672: Tidy up CorDapp deployments in samples. (#4815) * CORDA-2672: Tidy up CorDapp deployments in samples. * CORDA-2672: Refactor Attachment Demo. * Remove Bank of Corda from Trader Demo. * Configure SLF4J simple loggers, fix comments and documentation. --- docs/source/cordapp-build-systems.rst | 24 ++++--- docs/source/tutorial-attachments.rst | 4 +- samples/attachment-demo/build.gradle | 60 +++++++++++++++-- .../attachmentdemo/AttachmentDemoTest.kt | 2 - .../corda/attachmentdemo}/AttachmentDemo.kt | 60 +---------------- .../src/main/resources/bank-of-london-cp.jar | Bin .../main/resources/simplelogger.properties | 3 + .../attachment-demo/workflows/build.gradle | 24 +------ .../workflows/AttachmentDemoFlow.kt | 57 ++++++++++++++++ samples/cordapp-configuration/build.gradle | 2 +- .../src/main/resources/log4j2.xml | 8 ++- .../workflows/build.gradle | 2 - .../corda/configsample/GetStringConfigFlow.kt | 6 -- .../workflows/src/main/resources/log4j2.xml | 13 ---- samples/irs-demo/cordapp/build.gradle | 12 +++- .../main/resources/simplelogger.properties | 3 + samples/network-verifier/build.gradle | 19 +++++- .../main/resources/simplelogger.properties | 3 + samples/notary-demo/build.gradle | 22 ++++-- .../net/corda/notarydemo/client}/Notarise.kt | 2 +- .../main/resources/simplelogger.properties | 3 + samples/notary-demo/workflows/build.gradle | 6 +- samples/simm-valuation-demo/build.gradle | 5 ++ .../contracts-states/build.gradle | 2 +- .../net/corda/vega/SimmValuationTest.kt | 8 --- .../src/main/resources/log4j2.xml | 4 +- samples/trader-demo/build.gradle | 63 +++++++++++++++--- .../net/corda/traderdemo/TraderDemoTest.kt | 17 +++-- .../kotlin/net/corda/traderdemo/TraderDemo.kt | 0 .../corda/traderdemo/TraderDemoClientApi.kt | 0 .../src/main/resources/bank-of-london-cp.jar | Bin .../main/resources/simplelogger.properties | 3 + .../trader-demo/workflows-trader/build.gradle | 35 ++-------- .../corda/traderdemo/flow/LoggingBuyerFlow.kt | 1 - .../{ => flow}/TransactionGraphSearch.kt | 2 +- .../{ => flow}/TransactionGraphSearchTests.kt | 2 +- .../src/test/resources/log4j2-test.xml | 17 +++++ 37 files changed, 296 insertions(+), 198 deletions(-) rename samples/attachment-demo/{workflows => }/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt (95%) rename samples/attachment-demo/{workflows/src/main/kotlin/net/corda/attachmentdemo/workflows => src/main/kotlin/net/corda/attachmentdemo}/AttachmentDemo.kt (70%) rename samples/attachment-demo/{workflows => }/src/main/resources/bank-of-london-cp.jar (100%) create mode 100644 samples/attachment-demo/src/main/resources/simplelogger.properties create mode 100644 samples/attachment-demo/workflows/src/main/kotlin/net/corda/attachmentdemo/workflows/AttachmentDemoFlow.kt rename samples/{simm-valuation-demo/flows => cordapp-configuration}/src/main/resources/log4j2.xml (89%) delete mode 100644 samples/cordapp-configuration/workflows/src/main/resources/log4j2.xml create mode 100644 samples/irs-demo/cordapp/src/main/resources/simplelogger.properties create mode 100644 samples/network-verifier/src/main/resources/simplelogger.properties rename samples/notary-demo/{workflows/src/main/kotlin/net/corda/notarydemo => src/main/kotlin/net/corda/notarydemo/client}/Notarise.kt (98%) create mode 100644 samples/notary-demo/src/main/resources/simplelogger.properties rename samples/trader-demo/{workflows-trader => }/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt (90%) rename samples/trader-demo/{workflows-trader => }/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt (100%) rename samples/trader-demo/{workflows-trader => }/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt (100%) rename samples/trader-demo/{workflows-trader => }/src/main/resources/bank-of-london-cp.jar (100%) create mode 100644 samples/trader-demo/src/main/resources/simplelogger.properties rename samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/{ => flow}/TransactionGraphSearch.kt (99%) rename samples/trader-demo/workflows-trader/src/test/kotlin/net/corda/traderdemo/{ => flow}/TransactionGraphSearchTests.kt (99%) create mode 100644 samples/trader-demo/workflows-trader/src/test/resources/log4j2-test.xml diff --git a/docs/source/cordapp-build-systems.rst b/docs/source/cordapp-build-systems.rst index 19d9cada6e..79cbad9d96 100644 --- a/docs/source/cordapp-build-systems.rst +++ b/docs/source/cordapp-build-systems.rst @@ -25,7 +25,7 @@ JAR will contain: Build tools ----------- -In the instructions that follow, we assume you are using Gradle and the ``cordformation`` plugin to build your +In the instructions that follow, we assume you are using Gradle and the ``cordapp`` plugin to build your CorDapp. You can find examples of building a CorDapp using these tools in the `Kotlin CorDapp Template `_ and the `Java CorDapp Template `_. @@ -66,14 +66,17 @@ In certain cases, you may also wish to build against the unstable Master branch. Corda dependencies ^^^^^^^^^^^^^^^^^^ -The ``cordformation`` plugin adds two new gradle configurations: +The ``cordapp`` plugin adds three new gradle configurations: * ``cordaCompile``, which extends ``compile`` * ``cordaRuntime``, which extends ``runtime`` +* ``cordapp``, which extends ``compile`` ``cordaCompile`` and ``cordaRuntime`` indicate dependencies that should not be included in the CorDapp JAR. These configurations should be used for any Corda dependency (e.g. ``corda-core``, ``corda-node``) in order to prevent a -dependency from being included twice (once in the CorDapp JAR and once in the Corda JARs). +dependency from being included twice (once in the CorDapp JAR and once in the Corda JARs). The ``cordapp`` dependency +is for declaring a compile-time dependency on a "semi-fat" CorDapp JAR in the same way as ``cordaCompile``, except +that ``Cordformation`` will only deploy CorDapps contained within the ``cordapp`` configuration. Here are some guidelines for Corda dependencies: @@ -81,15 +84,17 @@ Here are some guidelines for Corda dependencies: ``cordaCompile`` dependency, and ``net.corda:corda:$corda_release_version`` as a ``cordaRuntime`` dependency * When building an RPC client that communicates with a node (e.g. a webserver), you should include - ``net.corda:corda-rpc:$corda_release_version`` as a ``cordaCompile`` dependency + ``net.corda:corda-rpc:$corda_release_version`` as a ``cordaCompile`` dependency. * When you need to use the network bootstrapper to bootstrap a local network (e.g. when using ``Cordformation``), you - should include ``net.corda:corda-node-api:$corda_release_version`` as a ``cordaCompile`` dependency + should include ``net.corda:corda-node-api:$corda_release_version`` as either a ``cordaRuntime`` or a ``runtimeOnly`` + dependency. You may also wish to include an implementation of SLF4J as a ``runtimeOnly`` dependency for the network + bootstrapper to use. * To use Corda's test frameworks, add ``net.corda:corda-test-utils:$corda_release_version`` as a ``testCompile`` - dependency. Never include ``corda-test-utils`` as a ``compile`` or ``cordaCompile`` dependency + dependency. Never include ``corda-test-utils`` as a ``compile`` or ``cordaCompile`` dependency. -* Any other Corda dependencies you need should be included as ``cordaCompile`` dependencies +* Any other Corda dependencies you need should be included as ``cordaCompile`` dependencies. Here is an overview of the various Corda dependencies: @@ -107,7 +112,8 @@ Here is an overview of the various Corda dependencies: * ``corda-jfx`` - JavaFX utilities with some Corda-specific models and utilities. Only use with JavaFX apps * ``corda-mock`` - A small library of useful mocks. Use if the classes are useful to you * ``corda-node`` - The Corda node. Do not depend on. Used only by the Corda fat JAR and indirectly in testing - frameworks + frameworks. (If your CorDapp _must_ depend on this for some reason then it should use the ``compileOnly`` + configuration here - but please _don't_ do this if you can possibly avoid it!) * ``corda-node-api`` - The node API. Required to bootstrap a local network * ``corda-node-driver`` - Testing utility for programmatically starting nodes from JVM languages. Use for tests * ``corda-rpc`` - The Corda RPC client library. Used when writing an RPC client @@ -312,7 +318,7 @@ Below is a sample CorDapp Gradle dependencies block. When building your own CorD cordapp "net.corda:bank-of-corda-demo:1.0" // Some other dependencies - compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" testCompile "junit:junit:$junit_version" diff --git a/docs/source/tutorial-attachments.rst b/docs/source/tutorial-attachments.rst index 60c1056fbc..871231afb0 100644 --- a/docs/source/tutorial-attachments.rst +++ b/docs/source/tutorial-attachments.rst @@ -105,7 +105,7 @@ and if so, printed out. .. container:: codeset - .. literalinclude:: ../../samples/attachment-demo/workflows/src/main/kotlin/net/corda/attachmentdemo/workflows/AttachmentDemo.kt + .. literalinclude:: ../../samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 @@ -115,7 +115,7 @@ transaction and send it to the recipient node: .. container:: codeset - .. literalinclude:: ../../samples/attachment-demo/workflows/src/main/kotlin/net/corda/attachmentdemo/workflows/AttachmentDemo.kt + .. literalinclude:: ../../samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 diff --git a/samples/attachment-demo/build.gradle b/samples/attachment-demo/build.gradle index f558c2619a..076d78848b 100644 --- a/samples/attachment-demo/build.gradle +++ b/samples/attachment-demo/build.gradle @@ -1,17 +1,68 @@ apply plugin: 'kotlin' apply plugin: 'idea' apply plugin: 'net.corda.plugins.quasar-utils' +apply plugin: 'net.corda.plugins.cordapp' apply plugin: 'net.corda.plugins.cordformation' description 'Corda attachment demo' +cordapp { + info { + name "Corda Attachment Demo" + vendor "R3" + targetPlatformVersion corda_platform_version.toInteger() + minimumPlatformVersion 1 + } +} + +sourceSets { + integrationTest { + kotlin { + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + srcDir file('src/integration-test/kotlin') + } + } +} + +configurations { + integrationTestCompile.extendsFrom testCompile + integrationTestRuntime.extendsFrom testRuntime +} + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version" + compile "javax.servlet:javax.servlet-api:3.1.0" + compile "javax.ws.rs:javax.ws.rs-api:2.0.1" + cordaCompile project(':client:rpc') + + // Cordformation needs a SLF4J implementation when executing the Network + // Bootstrapper, but Log4J doesn't shutdown completely from within Gradle. + // Use a much simpler SLF4J implementation here instead. + runtimeOnly "org.slf4j:slf4j-simple:$slf4j_version" + // Corda integration dependencies cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaRuntime project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') cordapp project(':samples:attachment-demo:contracts') cordapp project(':samples:attachment-demo:workflows') + + testCompile(project(':node-driver')) { + // We already have a SLF4J implementation on our runtime classpath, + // and we don't need another one. + exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl' + } + testCompile "junit:junit:$junit_version" + testCompile "org.assertj:assertj-core:$assertj_version" + + integrationTestCompile project(':webserver') +} + +task integrationTest(type: Test, dependsOn: []) { + testClassesDirs = sourceSets.integrationTest.output.classesDirs + classpath = sourceSets.integrationTest.runtimeClasspath } def nodeTask = tasks.getByPath(':node:capsule:assemble') @@ -25,7 +76,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, "InvokeRpc.internalVerifiedTransactionsFeed", "InvokeRpc.startTrackedFlowDynamic"]]] - directory "./build/nodes" nodeDefaults { projectCordapp { deploy = false @@ -70,16 +120,16 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, } } -task runSender(type: JavaExec) { +task runSender(type: JavaExec, dependsOn: jar) { classpath = sourceSets.main.runtimeClasspath - main = 'net.corda.attachmentdemo.workflows.AttachmentDemoKt' + main = 'net.corda.attachmentdemo.AttachmentDemoKt' args '--role' args 'SENDER' } -task runRecipient(type: JavaExec) { +task runRecipient(type: JavaExec, dependsOn: jar) { classpath = sourceSets.main.runtimeClasspath - main = 'net.corda.attachmentdemo.workflows.AttachmentDemoKt' + main = 'net.corda.attachmentdemo.AttachmentDemoKt' args '--role' args 'RECIPIENT' } diff --git a/samples/attachment-demo/workflows/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt similarity index 95% rename from samples/attachment-demo/workflows/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt rename to samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt index 4bbdef6e77..be9e74528b 100644 --- a/samples/attachment-demo/workflows/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt +++ b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt @@ -1,7 +1,5 @@ package net.corda.attachmentdemo -import net.corda.attachmentdemo.workflows.recipient -import net.corda.attachmentdemo.workflows.sender import net.corda.client.rpc.CordaRPCClient import net.corda.core.utilities.getOrThrow import net.corda.node.services.Permissions.Companion.all diff --git a/samples/attachment-demo/workflows/src/main/kotlin/net/corda/attachmentdemo/workflows/AttachmentDemo.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt similarity index 70% rename from samples/attachment-demo/workflows/src/main/kotlin/net/corda/attachmentdemo/workflows/AttachmentDemo.kt rename to samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt index 2c47a3cafe..ebe4d8e1e8 100644 --- a/samples/attachment-demo/workflows/src/main/kotlin/net/corda/attachmentdemo/workflows/AttachmentDemo.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt @@ -1,28 +1,19 @@ -package net.corda.attachmentdemo.workflows +package net.corda.attachmentdemo -import co.paralleluniverse.fibers.Suspendable import joptsimple.OptionParser -import net.corda.attachmentdemo.contracts.ATTACHMENT_PROGRAM_ID import net.corda.attachmentdemo.contracts.AttachmentContract +import net.corda.attachmentdemo.workflows.AttachmentDemoFlow import net.corda.client.rpc.CordaRPCClient import net.corda.core.crypto.SecureHash -import net.corda.core.flows.* -import net.corda.core.identity.Party import net.corda.core.internal.Emoji import net.corda.core.internal.InputStreamAndHash import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startTrackedFlow -import net.corda.core.node.StatesToRecord -import net.corda.core.transactions.SignedTransaction -import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow import java.io.InputStream import java.net.HttpURLConnection import java.net.URL -import java.util.concurrent.Executors -import java.util.concurrent.ScheduledExecutorService import java.util.jar.JarInputStream import javax.servlet.http.HttpServletResponse.SC_OK import javax.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION @@ -92,51 +83,6 @@ private fun sender(rpc: CordaRPCOps, inputStream: InputStream, hash: SecureHash. } // DOCEND 2 -@InitiatingFlow -@StartableByRPC -class AttachmentDemoFlow(private val otherSide: Party, - private val notary: Party, - private val attachId: SecureHash.SHA256) : FlowLogic() { - - object SIGNING : ProgressTracker.Step("Signing transaction") - - override val progressTracker: ProgressTracker = ProgressTracker(SIGNING) - - @Suspendable - override fun call(): SignedTransaction { - // Create a trivial transaction with an output that describes the attachment, and the attachment itself - val ptx = TransactionBuilder(notary) - .addOutputState(AttachmentContract.State(attachId), ATTACHMENT_PROGRAM_ID) - .addCommand(AttachmentContract.Command, ourIdentity.owningKey) - .addAttachment(attachId) - - progressTracker.currentStep = SIGNING - - val stx = serviceHub.signInitialTransaction(ptx) - - // Send the transaction to the other recipient - return subFlow(FinalityFlow(stx, initiateFlow(otherSide))) - } -} - -@InitiatedBy(AttachmentDemoFlow::class) -class StoreAttachmentFlow(private val otherSide: FlowSession) : FlowLogic() { - @Suspendable - override fun call() { - // As a non-participant to the transaction we need to record all states - subFlow(ReceiveFinalityFlow(otherSide, statesToRecord = StatesToRecord.ALL_VISIBLE)) - } -} - -@StartableByRPC -@StartableByService -class NoProgressTrackerShellDemo : FlowLogic() { - @Suspendable - override fun call(): String { - return "You Called me!" - } -} - @Suppress("DEPRECATION") // DOCSTART 1 fun recipient(rpc: CordaRPCOps, webPort: Int) { @@ -159,7 +105,7 @@ fun recipient(rpc: CordaRPCOps, webPort: Int) { // Write out the entries inside this jar. println("Attachment JAR contains these entries:") - JarInputStream(connection.inputStream).use { it -> + JarInputStream(connection.inputStream).use { while (true) { val e = it.nextJarEntry ?: break println("Entry> ${e.name}") diff --git a/samples/attachment-demo/workflows/src/main/resources/bank-of-london-cp.jar b/samples/attachment-demo/src/main/resources/bank-of-london-cp.jar similarity index 100% rename from samples/attachment-demo/workflows/src/main/resources/bank-of-london-cp.jar rename to samples/attachment-demo/src/main/resources/bank-of-london-cp.jar diff --git a/samples/attachment-demo/src/main/resources/simplelogger.properties b/samples/attachment-demo/src/main/resources/simplelogger.properties new file mode 100644 index 0000000000..8ce4c88201 --- /dev/null +++ b/samples/attachment-demo/src/main/resources/simplelogger.properties @@ -0,0 +1,3 @@ +org.slf4j.simpleLogger.defaultLogLevel=info +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z diff --git a/samples/attachment-demo/workflows/build.gradle b/samples/attachment-demo/workflows/build.gradle index 132c345fbd..09d4f45d55 100644 --- a/samples/attachment-demo/workflows/build.gradle +++ b/samples/attachment-demo/workflows/build.gradle @@ -5,34 +5,12 @@ apply plugin: 'net.corda.plugins.cordapp' description 'Corda attachment demo - workflows' -sourceSets { - integrationTest { - kotlin { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDir file('src/integration-test/kotlin') - } - } -} - -configurations { - integrationTestCompile.extendsFrom testCompile - integrationTestRuntime.extendsFrom testRuntime -} - dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + // Corda integration dependencies cordaCompile project(':core') - cordaCompile project(':webserver') cordapp project(':samples:attachment-demo:contracts') - testCompile project(':node-driver') - testCompile "junit:junit:$junit_version" -} - -task integrationTest(type: Test, dependsOn: []) { - testClassesDirs = sourceSets.integrationTest.output.classesDirs - classpath = sourceSets.integrationTest.runtimeClasspath } idea { diff --git a/samples/attachment-demo/workflows/src/main/kotlin/net/corda/attachmentdemo/workflows/AttachmentDemoFlow.kt b/samples/attachment-demo/workflows/src/main/kotlin/net/corda/attachmentdemo/workflows/AttachmentDemoFlow.kt new file mode 100644 index 0000000000..20b8a1afb5 --- /dev/null +++ b/samples/attachment-demo/workflows/src/main/kotlin/net/corda/attachmentdemo/workflows/AttachmentDemoFlow.kt @@ -0,0 +1,57 @@ +package net.corda.attachmentdemo.workflows + +import co.paralleluniverse.fibers.Suspendable +import net.corda.attachmentdemo.contracts.ATTACHMENT_PROGRAM_ID +import net.corda.attachmentdemo.contracts.AttachmentContract +import net.corda.core.crypto.SecureHash +import net.corda.core.flows.* +import net.corda.core.identity.Party +import net.corda.core.node.StatesToRecord +import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.ProgressTracker + +@InitiatingFlow +@StartableByRPC +class AttachmentDemoFlow(private val otherSide: Party, + private val notary: Party, + private val attachId: SecureHash.SHA256) : FlowLogic() { + + object SIGNING : ProgressTracker.Step("Signing transaction") + + override val progressTracker: ProgressTracker = ProgressTracker(SIGNING) + + @Suspendable + override fun call(): SignedTransaction { + // Create a trivial transaction with an output that describes the attachment, and the attachment itself + val ptx = TransactionBuilder(notary) + .addOutputState(AttachmentContract.State(attachId), ATTACHMENT_PROGRAM_ID) + .addCommand(AttachmentContract.Command, ourIdentity.owningKey) + .addAttachment(attachId) + + progressTracker.currentStep = SIGNING + + val stx = serviceHub.signInitialTransaction(ptx) + + // Send the transaction to the other recipient + return subFlow(FinalityFlow(stx, initiateFlow(otherSide))) + } +} + +@InitiatedBy(AttachmentDemoFlow::class) +class StoreAttachmentFlow(private val otherSide: FlowSession) : FlowLogic() { + @Suspendable + override fun call() { + // As a non-participant to the transaction we need to record all states + subFlow(ReceiveFinalityFlow(otherSide, statesToRecord = StatesToRecord.ALL_VISIBLE)) + } +} + +@StartableByRPC +@StartableByService +class NoProgressTrackerShellDemo : FlowLogic() { + @Suspendable + override fun call(): String { + return "You Called me!" + } +} diff --git a/samples/cordapp-configuration/build.gradle b/samples/cordapp-configuration/build.gradle index bbdaca4a0f..ea2def65c5 100644 --- a/samples/cordapp-configuration/build.gradle +++ b/samples/cordapp-configuration/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'idea' apply plugin: 'net.corda.plugins.cordformation' dependencies { - compile project(':node-api') + runtimeOnly project(':node-api') runtimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" // Corda integration dependencies diff --git a/samples/simm-valuation-demo/flows/src/main/resources/log4j2.xml b/samples/cordapp-configuration/src/main/resources/log4j2.xml similarity index 89% rename from samples/simm-valuation-demo/flows/src/main/resources/log4j2.xml rename to samples/cordapp-configuration/src/main/resources/log4j2.xml index 1c4164a050..986f04813e 100644 --- a/samples/simm-valuation-demo/flows/src/main/resources/log4j2.xml +++ b/samples/cordapp-configuration/src/main/resources/log4j2.xml @@ -1,9 +1,11 @@ - + + + - logs - node-${hostName} + build/logs + cordapp-${hostName} ${log-path}/archive diff --git a/samples/cordapp-configuration/workflows/build.gradle b/samples/cordapp-configuration/workflows/build.gradle index 01addff34c..4e9f698afc 100644 --- a/samples/cordapp-configuration/workflows/build.gradle +++ b/samples/cordapp-configuration/workflows/build.gradle @@ -1,7 +1,5 @@ apply plugin: 'kotlin' -apply plugin: 'idea' apply plugin: 'net.corda.plugins.cordapp' -apply plugin: 'net.corda.plugins.cordformation' dependencies { cordaCompile project(':core') diff --git a/samples/cordapp-configuration/workflows/src/main/kotlin/net/corda/configsample/GetStringConfigFlow.kt b/samples/cordapp-configuration/workflows/src/main/kotlin/net/corda/configsample/GetStringConfigFlow.kt index 9254df4343..192cad9347 100644 --- a/samples/cordapp-configuration/workflows/src/main/kotlin/net/corda/configsample/GetStringConfigFlow.kt +++ b/samples/cordapp-configuration/workflows/src/main/kotlin/net/corda/configsample/GetStringConfigFlow.kt @@ -1,14 +1,8 @@ package net.corda.configsample import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.CommandData -import net.corda.core.contracts.Contract -import net.corda.core.contracts.ContractState import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC -import net.corda.core.identity.AbstractParty -import net.corda.core.serialization.CordaSerializable -import net.corda.core.transactions.LedgerTransaction import net.corda.core.utilities.ProgressTracker // DOCSTART 1 diff --git a/samples/cordapp-configuration/workflows/src/main/resources/log4j2.xml b/samples/cordapp-configuration/workflows/src/main/resources/log4j2.xml deleted file mode 100644 index f82d09a079..0000000000 --- a/samples/cordapp-configuration/workflows/src/main/resources/log4j2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/samples/irs-demo/cordapp/build.gradle b/samples/irs-demo/cordapp/build.gradle index 3ffb92b4b4..fb7be3f848 100644 --- a/samples/irs-demo/cordapp/build.gradle +++ b/samples/irs-demo/cordapp/build.gradle @@ -17,14 +17,22 @@ sourceSets { } } +cordapp { + info { + name "Corda IRS Demo" + vendor "R3" + targetPlatformVersion corda_platform_version.toInteger() + minimumPlatformVersion 1 + } +} + dependencies { cordapp project(':finance:contracts') cordapp project(':finance:workflows') // Corda integration dependencies cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts') - cordaCompile project(':core') - cordaRuntime project(':node-api') + runtimeOnly "org.slf4j:slf4j-simple:$slf4j_version" cordapp project(':samples:irs-demo:cordapp:contracts-irs') cordapp project(':samples:irs-demo:cordapp:workflows-irs') diff --git a/samples/irs-demo/cordapp/src/main/resources/simplelogger.properties b/samples/irs-demo/cordapp/src/main/resources/simplelogger.properties new file mode 100644 index 0000000000..8ce4c88201 --- /dev/null +++ b/samples/irs-demo/cordapp/src/main/resources/simplelogger.properties @@ -0,0 +1,3 @@ +org.slf4j.simpleLogger.defaultLogLevel=info +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z diff --git a/samples/network-verifier/build.gradle b/samples/network-verifier/build.gradle index 51ecd18e05..2465cc78c5 100644 --- a/samples/network-verifier/build.gradle +++ b/samples/network-verifier/build.gradle @@ -2,9 +2,23 @@ apply plugin: 'kotlin' apply plugin: 'net.corda.plugins.cordapp' apply plugin: 'net.corda.plugins.cordformation' +cordapp { + info { + name "Corda Network Verifier" + vendor "R3" + targetPlatformVersion corda_platform_version.toInteger() + minimumPlatformVersion 1 + } +} + dependencies { - cordaCompile project(':core') - cordaCompile project(':node-api') + // Cordformation needs this for the Network Bootstrapper. + runtimeOnly project(':node-api') + + // Cordformation needs a SLF4J implementation when executing the Network + // Bootstrapper, but Log4J doesn't shutdown completely from within Gradle. + // Use a much simpler SLF4J implementation here instead. + runtimeOnly "org.slf4j:slf4j-simple:$slf4j_version" // Corda integration dependencies cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts') @@ -23,7 +37,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask]) cordapp project(':samples:network-verifier:contracts') cordapp project(':samples:network-verifier:workflows') } - directory "./build/nodes" node { name "O=Notary Service,L=Zurich,C=CH" notary = [validating : false] diff --git a/samples/network-verifier/src/main/resources/simplelogger.properties b/samples/network-verifier/src/main/resources/simplelogger.properties new file mode 100644 index 0000000000..8ce4c88201 --- /dev/null +++ b/samples/network-verifier/src/main/resources/simplelogger.properties @@ -0,0 +1,3 @@ +org.slf4j.simpleLogger.defaultLogLevel=info +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z diff --git a/samples/notary-demo/build.gradle b/samples/notary-demo/build.gradle index 51409be9c5..d74a1d3fa2 100644 --- a/samples/notary-demo/build.gradle +++ b/samples/notary-demo/build.gradle @@ -1,17 +1,29 @@ import net.corda.plugins.Cordform -apply plugin: 'java' apply plugin: 'kotlin' apply plugin: 'idea' -apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.cordapp' apply plugin: 'net.corda.plugins.cordformation' +cordapp { + info { + name "Corda Notary Demo" + vendor "R3" + targetPlatformVersion corda_platform_version.toInteger() + minimumPlatformVersion 1 + } +} + dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + cordaCompile project(':client:rpc') // Corda integration dependencies cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts') - cordaCompile project(':core') + + // Cordformation needs a SLF4J implementation when executing the Network + // Bootstrapper, but Log4J doesn't shutdown completely from within Gradle. + // Use a much simpler SLF4J implementation here instead. + runtimeOnly "org.slf4j:slf4j-simple:$slf4j_version" // Notary implementations cordapp project(':samples:notary-demo:contracts') @@ -240,7 +252,7 @@ task deployNodesBFT(type: Cordform, dependsOn: ['jar', nodeTask, webTask]) { } } -task notarise(type: JavaExec) { +task notarise(type: JavaExec, dependsOn: jar) { classpath = sourceSets.main.runtimeClasspath - main = 'net.corda.notarydemo.NotariseKt' + main = 'net.corda.notarydemo.client.NotariseKt' } diff --git a/samples/notary-demo/workflows/src/main/kotlin/net/corda/notarydemo/Notarise.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/client/Notarise.kt similarity index 98% rename from samples/notary-demo/workflows/src/main/kotlin/net/corda/notarydemo/Notarise.kt rename to samples/notary-demo/src/main/kotlin/net/corda/notarydemo/client/Notarise.kt index 93d71a8106..0f1648752d 100644 --- a/samples/notary-demo/workflows/src/main/kotlin/net/corda/notarydemo/Notarise.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/client/Notarise.kt @@ -1,4 +1,4 @@ -package net.corda.notarydemo +package net.corda.notarydemo.client import net.corda.client.rpc.CordaRPCClient import net.corda.core.crypto.CompositeKey diff --git a/samples/notary-demo/src/main/resources/simplelogger.properties b/samples/notary-demo/src/main/resources/simplelogger.properties new file mode 100644 index 0000000000..8ce4c88201 --- /dev/null +++ b/samples/notary-demo/src/main/resources/simplelogger.properties @@ -0,0 +1,3 @@ +org.slf4j.simpleLogger.defaultLogLevel=info +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z diff --git a/samples/notary-demo/workflows/build.gradle b/samples/notary-demo/workflows/build.gradle index 85ea6160df..4113819912 100644 --- a/samples/notary-demo/workflows/build.gradle +++ b/samples/notary-demo/workflows/build.gradle @@ -6,7 +6,11 @@ description 'Corda Notary Demo - Workflows' dependencies { cordaCompile project(':core') cordaCompile project(':client:rpc') - cordaCompile project(':node') + + // We need to compile against the Node, but also DO NOT + // want the Node bundled inside the CorDapp or added to + // Gradle's runtime classpath. + compileOnly project(':node') cordapp project(':samples:notary-demo:contracts') } diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index b7db45d7aa..73fb8d5fd3 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -62,6 +62,11 @@ dependencies { testCompile "org.assertj:assertj-core:$assertj_version" } +jar { + // A CorDapp does not configure the Node's logging! + exclude "**/log4j2*.xml" +} + def nodeTask = tasks.getByPath(':node:capsule:assemble') def webTask = tasks.getByPath(':webserver:webcapsule:assemble') task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, webTask]) { diff --git a/samples/simm-valuation-demo/contracts-states/build.gradle b/samples/simm-valuation-demo/contracts-states/build.gradle index 7a88d84003..620588c605 100644 --- a/samples/simm-valuation-demo/contracts-states/build.gradle +++ b/samples/simm-valuation-demo/contracts-states/build.gradle @@ -53,7 +53,7 @@ task shrink(type: ProGuardTask) { libraryjars "$javaHome/lib/rt.jar" libraryjars "$javaHome/lib/jce.jar" - configurations.runtime.forEach { + configurations.runtimeClasspath.forEach { libraryjars it.path, filter: '!META-INF/versions/**' } diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index 55cb488b4b..3e34a1a42a 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -3,28 +3,20 @@ package net.corda.vega import com.opengamma.strata.product.common.BuySell import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div -import net.corda.core.internal.packageName import net.corda.core.utilities.getOrThrow -import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.core.DUMMY_BANK_B_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.http.HttpApi -import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.FINANCE_CORDAPPS import net.corda.testing.node.internal.findCordapp import net.corda.vega.api.PortfolioApi import net.corda.vega.api.PortfolioApiUtils import net.corda.vega.api.SwapDataModel import net.corda.vega.api.SwapDataView -import net.corda.vega.plugin.customserializers.CurrencyParameterSensitivitiesSerializer import org.assertj.core.api.Assertions.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Ignore import org.junit.Test import java.math.BigDecimal import java.time.LocalDate diff --git a/samples/simm-valuation-demo/src/main/resources/log4j2.xml b/samples/simm-valuation-demo/src/main/resources/log4j2.xml index 1c4164a050..00795edc45 100644 --- a/samples/simm-valuation-demo/src/main/resources/log4j2.xml +++ b/samples/simm-valuation-demo/src/main/resources/log4j2.xml @@ -2,8 +2,8 @@ - logs - node-${hostName} + build/logs + simm-valuation-${hostName} ${log-path}/archive diff --git a/samples/trader-demo/build.gradle b/samples/trader-demo/build.gradle index 590f0c0483..1bb8e089b6 100644 --- a/samples/trader-demo/build.gradle +++ b/samples/trader-demo/build.gradle @@ -1,29 +1,71 @@ apply plugin: 'kotlin' apply plugin: 'idea' apply plugin: 'net.corda.plugins.quasar-utils' +apply plugin: 'net.corda.plugins.cordapp' apply plugin: 'net.corda.plugins.cordformation' +cordapp { + info { + name "Trader Demo" + vendor "R3" + targetPlatformVersion corda_platform_version.toInteger() + minimumPlatformVersion 1 + } +} + +sourceSets { + integrationTest { + kotlin { + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + srcDir file('src/integration-test/kotlin') + } + } +} + +configurations { + integrationTestCompile.extendsFrom testCompile + integrationTestRuntime.extendsFrom testRuntime +} + dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version" + cordaCompile project(':client:rpc') + + // Cordformation needs a SLF4J implementation when executing the Network + // Bootstrapper, but Log4J doesn't shutdown completely from within Gradle. + // Use a much simpler SLF4J implementation here instead. + runtimeOnly "org.slf4j:slf4j-simple:$slf4j_version" + + // We only need this for its DUMMY_BANK constants, and + // DO NOT want it added to Gradle's runtime classpath. + compileOnly project(':test-utils') // The trader demo CorDapp depends upon Cash CorDapp features cordapp project(':finance:contracts') cordapp project(':finance:workflows') - cordapp project(':samples:bank-of-corda-demo') cordapp project(':samples:trader-demo:workflows-trader') // Corda integration dependencies cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts') - cordaRuntime project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') - testCompile project(':test-utils') + testCompile(project(':node-driver')) { + // We already have a SLF4J implementation on our runtime classpath, + // and we don't need another one. + exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl' + } testCompile "junit:junit:$junit_version" - testCompile "org.assertj:assertj-core:${assertj_version}" + testCompile "org.assertj:assertj-core:$assertj_version" +} + +task integrationTest(type: Test, dependsOn: []) { + testClassesDirs = sourceSets.integrationTest.output.classesDirs + classpath = sourceSets.integrationTest.runtimeClasspath } def nodeTask = tasks.getByPath(':node:capsule:assemble') -def webTask = tasks.getByPath(':webserver:webcapsule:assemble') -task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, webTask]) { +task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask]) { ext.rpcUsers = [['username': "demo", 'password': "demo", 'permissions': ["ALL"]]] nodeDefaults { projectCordapp { @@ -33,7 +75,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, cordapp project(':finance:contracts') cordapp project(':samples:trader-demo:workflows-trader') } - directory "./build/nodes" node { name "O=Notary Service,L=Zurich,C=CH" notary = [validating : true] @@ -96,15 +137,15 @@ idea { } } -task runBank(type: JavaExec) { - classpath = sourceSets.main.runtimeClasspath +task runBank(type: JavaExec, dependsOn: jar) { + classpath = sourceSets.test.runtimeClasspath main = 'net.corda.traderdemo.TraderDemoKt' args '--role' args 'BANK' } -task runSeller(type: JavaExec) { - classpath = sourceSets.main.runtimeClasspath +task runSeller(type: JavaExec, dependsOn: jar) { + classpath = sourceSets.test.runtimeClasspath main = 'net.corda.traderdemo.TraderDemoKt' args '--role' args 'SELLER' diff --git a/samples/trader-demo/workflows-trader/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt similarity index 90% rename from samples/trader-demo/workflows-trader/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt rename to samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index cad856bb73..ea9dcdf086 100644 --- a/samples/trader-demo/workflows-trader/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -39,7 +39,7 @@ class TraderDemoTest { driver(DriverParameters( startNodesInProcess = true, inMemoryDB = false, - cordappsForAllNodes = FINANCE_CORDAPPS + TestCordapp.findCordapp("net.corda.traderdemo") + cordappsForAllNodes = FINANCE_CORDAPPS + TestCordapp.findCordapp("net.corda.traderdemo.flow") )) { val (nodeA, nodeB, bankNode) = listOf( startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser)), @@ -71,11 +71,14 @@ class TraderDemoTest { assertThat(clientB.cashCount).isEqualTo(expectedBCash) // Wait until A receives the commercial paper val executor = Executors.newScheduledThreadPool(1) - poll(executor, "A to be notified of the commercial paper", pollInterval = 100.millis) { - val actualPaper = listOf(clientA.commercialPaperCount, clientB.commercialPaperCount) - if (actualPaper == expectedPaper) Unit else null - }.getOrThrow() - executor.shutdown() + try { + poll(executor, "A to be notified of the commercial paper", pollInterval = 100.millis) { + val actualPaper = listOf(clientA.commercialPaperCount, clientB.commercialPaperCount) + if (actualPaper == expectedPaper) Unit else null + }.getOrThrow() + } finally { + executor.shutdownNow() + } assertThat(clientA.dollarCashBalance).isEqualTo(95.DOLLARS) assertThat(clientB.dollarCashBalance).isEqualTo(5.DOLLARS) } @@ -86,7 +89,7 @@ class TraderDemoTest { driver(DriverParameters( startNodesInProcess = false, inMemoryDB = false, - cordappsForAllNodes = FINANCE_CORDAPPS + TestCordapp.findCordapp("net.corda.traderdemo") + cordappsForAllNodes = FINANCE_CORDAPPS + TestCordapp.findCordapp("net.corda.traderdemo.flow") )) { val (buyer, seller, bank) = listOf( startNode(providedName = DUMMY_BANK_A_NAME), diff --git a/samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt similarity index 100% rename from samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt rename to samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt diff --git a/samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt similarity index 100% rename from samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt rename to samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt diff --git a/samples/trader-demo/workflows-trader/src/main/resources/bank-of-london-cp.jar b/samples/trader-demo/src/main/resources/bank-of-london-cp.jar similarity index 100% rename from samples/trader-demo/workflows-trader/src/main/resources/bank-of-london-cp.jar rename to samples/trader-demo/src/main/resources/bank-of-london-cp.jar diff --git a/samples/trader-demo/src/main/resources/simplelogger.properties b/samples/trader-demo/src/main/resources/simplelogger.properties new file mode 100644 index 0000000000..8ce4c88201 --- /dev/null +++ b/samples/trader-demo/src/main/resources/simplelogger.properties @@ -0,0 +1,3 @@ +org.slf4j.simpleLogger.defaultLogLevel=info +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z diff --git a/samples/trader-demo/workflows-trader/build.gradle b/samples/trader-demo/workflows-trader/build.gradle index 05f6cf8178..5207820e62 100644 --- a/samples/trader-demo/workflows-trader/build.gradle +++ b/samples/trader-demo/workflows-trader/build.gradle @@ -1,45 +1,18 @@ apply plugin: 'kotlin' -apply plugin: 'idea' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.cordapp' -sourceSets { - integrationTest { - kotlin { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDir file('src/integration-test/kotlin') - } - } -} - -configurations { - integrationTestCompile.extendsFrom testCompile - integrationTestRuntime.extendsFrom testRuntime -} - dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + cordaCompile project(':core') // The trader demo CorDapp depends upon Cash CorDapp features cordapp project(':finance:contracts') cordapp project(':finance:workflows') - cordapp project(':samples:bank-of-corda-demo') - - // Corda integration dependencies - cordaCompile project(':core') - - testCompile project(':test-utils') + + testCompile project(':node-driver') testCompile "junit:junit:$junit_version" - testCompile "org.assertj:assertj-core:${assertj_version}" - - integrationTestCompile project(':finance:workflows') - integrationTestCompile project(':finance:contracts') -} - -task integrationTest(type: Test, dependsOn: []) { - testClassesDirs = sourceSets.integrationTest.output.classesDirs - classpath = sourceSets.integrationTest.runtimeClasspath + testCompile "org.assertj:assertj-core:$assertj_version" } jar { diff --git a/samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/flow/LoggingBuyerFlow.kt b/samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/flow/LoggingBuyerFlow.kt index c32c3fe664..9734248e89 100644 --- a/samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/flow/LoggingBuyerFlow.kt +++ b/samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/flow/LoggingBuyerFlow.kt @@ -7,7 +7,6 @@ import net.corda.core.internal.Emoji import net.corda.core.transactions.SignedTransaction import net.corda.finance.contracts.CommercialPaper import net.corda.finance.workflows.getCashBalances -import net.corda.traderdemo.TransactionGraphSearch @InitiatedBy(SellerFlow::class) class LoggingBuyerFlow(private val otherSideSession: FlowSession) : BuyerFlow(otherSideSession) { diff --git a/samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/TransactionGraphSearch.kt b/samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/flow/TransactionGraphSearch.kt similarity index 99% rename from samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/TransactionGraphSearch.kt rename to samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/flow/TransactionGraphSearch.kt index 684cd1724e..9f2cbf8ffe 100644 --- a/samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/TransactionGraphSearch.kt +++ b/samples/trader-demo/workflows-trader/src/main/kotlin/net/corda/traderdemo/flow/TransactionGraphSearch.kt @@ -1,4 +1,4 @@ -package net.corda.traderdemo +package net.corda.traderdemo.flow import net.corda.core.contracts.CommandData import net.corda.core.contracts.ContractState diff --git a/samples/trader-demo/workflows-trader/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt b/samples/trader-demo/workflows-trader/src/test/kotlin/net/corda/traderdemo/flow/TransactionGraphSearchTests.kt similarity index 99% rename from samples/trader-demo/workflows-trader/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt rename to samples/trader-demo/workflows-trader/src/test/kotlin/net/corda/traderdemo/flow/TransactionGraphSearchTests.kt index 5cad922236..56f170ca41 100644 --- a/samples/trader-demo/workflows-trader/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt +++ b/samples/trader-demo/workflows-trader/src/test/kotlin/net/corda/traderdemo/flow/TransactionGraphSearchTests.kt @@ -1,4 +1,4 @@ -package net.corda.traderdemo +package net.corda.traderdemo.flow import net.corda.core.contracts.CommandData import net.corda.core.crypto.newSecureRandom diff --git a/samples/trader-demo/workflows-trader/src/test/resources/log4j2-test.xml b/samples/trader-demo/workflows-trader/src/test/resources/log4j2-test.xml new file mode 100644 index 0000000000..b12cea5b2d --- /dev/null +++ b/samples/trader-demo/workflows-trader/src/test/resources/log4j2-test.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file From b98ed6db2303db066add1ba0b5e488f6083b67d0 Mon Sep 17 00:00:00 2001 From: Ben Wyeth Date: Tue, 12 Mar 2019 10:25:39 +0000 Subject: [PATCH 077/159] CORDA-2729: Update integration testing instructions to explicitly mention node-driver dependency (#4873) * adding a note so that it's clear what you have to include in gradle in order to use the driver classes * responding to comments --- docs/source/tutorial-integration-testing.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/source/tutorial-integration-testing.rst b/docs/source/tutorial-integration-testing.rst index 5b2e9c8d98..da23ab2263 100644 --- a/docs/source/tutorial-integration-testing.rst +++ b/docs/source/tutorial-integration-testing.rst @@ -126,3 +126,12 @@ about ``CashIssueAndPaymentFlow`` and ``CashPaymentFlow``. You can find the complete test at ``example-code/src/integration-test/java/net/corda/docs/java/tutorial/test/JavaIntegrationTestingTutorial.java`` (Java) and ``example-code/src/integration-test/kotlin/net/corda/docs/kotlin/tutorial/test/KotlinIntegrationTestingTutorial.kt`` (Kotlin) in the `Corda repo `_. + +.. note:: + + To make sure the driver classes are included in your project you will need the following in your ``build.gradle`` file in the module in + which you want to test: + + .. sourcecode:: groovy + + testCompile "$corda_release_group:corda-node-driver:$corda_release_version" From 76b07967e246845fc36465f9ac8f63dcfb8c8a64 Mon Sep 17 00:00:00 2001 From: Ben Wyeth Date: Tue, 12 Mar 2019 10:26:17 +0000 Subject: [PATCH 078/159] CORDA-2715 - Update test runner instructions for CorDapp tutorial (#4863) * made consistent with template review * fixing nested bullet layouts * updating based on (awesome) comments * next iteration :-) * more fixes --- docs/source/tutorial-cordapp.rst | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/source/tutorial-cordapp.rst b/docs/source/tutorial-cordapp.rst index 17fa86e714..d67f2bbc26 100644 --- a/docs/source/tutorial-cordapp.rst +++ b/docs/source/tutorial-cordapp.rst @@ -481,6 +481,45 @@ Integration tests ~~~~~~~~~~~~~~~~~ You can run the CorDapp's integration tests by running the ``Run Integration Tests - Kotlin`` run configuration. +Running tests in IntelliJ +~~~~~~~~~~~~~~~~~~~~~~~~~ + +We recommend editing your IntelliJ preferences so that you use the Gradle runner - this means that the quasar utils +plugin will make sure that some flags (like ``-javaagent`` - see :ref:`below `) are +set for you. + +To switch to using the Gradle runner: + +* Navigate to ``Build, Execution, Deployment -> Build Tools -> Gradle -> Runner`` (or search for `runner`) + + * Windows: this is in "Settings" + * MacOS: this is in "Preferences" + +* Set "Delegate IDE build/run actions to gradle" to true +* Set "Run test using:" to "Gradle Test Runner" + +.. _tutorial_cordapp_alternative_test_runners: + +If you would prefer to use the built in IntelliJ JUnit test runner, you can add some code to your ``build.gradle`` file and +it will copy your quasar JAR file to the lib directory. You will also need to specify ``-javaagent:lib/quasar.jar`` +and set the run directory to the project root directory for each test. + +Add the following to your ``build.gradle`` file - ideally to a ``build.gradle`` that already contains the quasar-utils plugin line: + +.. sourcecode:: groovy + + apply plugin: 'net.corda.plugins.quasar-utils' + + task installQuasar(type: Copy) { + destinationDir rootProject.file("lib") + from(configurations.quasar) { + rename 'quasar-core(.*).jar', 'quasar.jar' + } + } + + +and then you can run ``gradlew installQuasar``. + Debugging your CorDapp ---------------------- From ed2fe13436a5004f2709d049dea3431a51b10c13 Mon Sep 17 00:00:00 2001 From: Jonathan Locke <36930160+lockathan@users.noreply.github.com> Date: Tue, 12 Mar 2019 13:45:25 +0000 Subject: [PATCH 079/159] CORDA-2629: Provide a better error msg when notary type misconfig (#4864) * CORDA-2629: Provide a better error msg when notary type misconfig If a notary service is misconfigured with the type (either validating or not validating) stored in the node's configuration not matching that advertised in the network map cache, the notary will throw an exception and fail on startup. Previously, this misconfiguration would result in an exception being thrown when attempting to notarise a transaction. This change results in the exception being thrown at node startup and the node operator being aware of the misconfiguration earlier. * Corrected exception message when the notary does not have a party * Changed exception message to include configured and advertised values. Fixed unit test that was failing because of the new check. --- .../net/corda/node/utilities/NotaryLoader.kt | 14 ++++++++++++++ .../net/corda/node/services/TimedFlowTests.kt | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/node/src/main/kotlin/net/corda/node/utilities/NotaryLoader.kt b/node/src/main/kotlin/net/corda/node/utilities/NotaryLoader.kt index 3476364c92..e1bd2e2a2a 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/NotaryLoader.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/NotaryLoader.kt @@ -55,6 +55,8 @@ class NotaryLoader( } fun loadService(myNotaryIdentity: PartyAndCertificate?, services: ServiceHubInternal, cordappLoader: CordappLoader): NotaryService { + validateNotaryType(myNotaryIdentity, services) + val serviceClass = builtInServiceClass ?: scanCorDapps(cordappLoader) log.info("Starting notary service: $serviceClass") @@ -70,6 +72,18 @@ class NotaryLoader( return constructor.newInstance(services, notaryKey) } + /** Validates that the notary is correctly configured by comparing the configured type against the type advertised in the network map cache */ + private fun validateNotaryType(myNotaryIdentity: PartyAndCertificate?, services: ServiceHubInternal) { + var configuredAsValidatingNotary = services.configuration.notary?.validating + val notaryParty = myNotaryIdentity?.party ?: throw IllegalStateException("Could not establish notary identity of this node") + var validatingNotaryInNetworkMapCache = services.networkMapCache.isValidatingNotary(notaryParty) + + if(configuredAsValidatingNotary != validatingNotaryInNetworkMapCache) { + throw IllegalStateException("There is a discrepancy in the configured notary type and the one advertised in the network parameters - shutting down. " + + "Configured as validating: ${configuredAsValidatingNotary}. Advertised as validating: ${validatingNotaryInNetworkMapCache}") + } + } + /** Looks for the config specified notary service implementation in loaded CorDapps. This mechanism is for internal use only. */ private fun scanCorDapps(cordappLoader: CordappLoader): Class { val loadedImplementations = cordappLoader.cordapps.mapNotNull { it.notaryService } diff --git a/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt index f06adb451f..ff7b206bd3 100644 --- a/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt @@ -101,7 +101,7 @@ class TimedFlowTests { val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryIdentity, false)))) val notaryConfig = mock { whenever(it.serviceLegalName).thenReturn(serviceLegalName) - whenever(it.validating).thenReturn(true) + whenever(it.validating).thenReturn(false) whenever(it.className).thenReturn(TestNotaryService::class.java.name) } From 1cd78a996f7be347177b8118aabb465de0ef05da Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 12 Mar 2019 14:18:22 +0000 Subject: [PATCH 080/159] BUILD - Apply Docker Remote API plugin using Gradle's plugins block. (#4874) --- docker/build.gradle | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/docker/build.gradle b/docker/build.gradle index 30fea9f79f..2891b251ab 100644 --- a/docker/build.gradle +++ b/docker/build.gradle @@ -1,17 +1,9 @@ -evaluationDependsOn(":node:capsule") -buildscript { - repositories { - mavenLocal() - mavenCentral() - jcenter() - } - dependencies { - classpath 'com.bmuschko:gradle-docker-plugin:3.4.4' - } +plugins { + id 'com.bmuschko.docker-remote-api' version '3.4.4' } +evaluationDependsOn(":node:capsule") -import com.bmuschko.gradle.docker.DockerRemoteApiPlugin import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage import com.bmuschko.gradle.docker.tasks.image.DockerPushImage @@ -19,7 +11,6 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter apply plugin: 'kotlin' -apply plugin: DockerRemoteApiPlugin apply plugin: 'application' // We need to set mainClassName before applying the shadow plugin. mainClassName = 'net.corda.core.ConfigExporterMain' @@ -96,4 +87,4 @@ task pushCorrettoLatestTag('type': DockerPushImage, dependsOn: [buildOfficialCor imageName = correttoBuildTags[1] } -task pushOfficialImages(dependsOn: [pushZuluTimeStampedTag, pushZuluLatestTag, pushCorrettoTimeStampedTag, pushCorrettoLatestTag]) \ No newline at end of file +task pushOfficialImages(dependsOn: [pushZuluTimeStampedTag, pushZuluLatestTag, pushCorrettoTimeStampedTag, pushCorrettoLatestTag]) From 5f7f80908441bff6aa39d82dc459447d1d63fe20 Mon Sep 17 00:00:00 2001 From: Dimos Raptis Date: Mon, 11 Mar 2019 10:36:33 +0000 Subject: [PATCH 081/159] CORDA-2705 - Prevent duplicates in cache and fix the mappings persisted for confidential identities --- .../identity/PersistentIdentityService.kt | 10 +-- .../node/utilities/AppendOnlyPersistentMap.kt | 31 ++++---- ...ppendOnlyPersistentMapNonConcurrentTest.kt | 79 +++++++++++++++++++ 3 files changed, 101 insertions(+), 19 deletions(-) create mode 100644 node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapNonConcurrentTest.kt diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index 712d96f5ca..2bd8c915e2 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -8,7 +8,6 @@ import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.MAX_HASH_HEX_SIZE import net.corda.core.utilities.contextLogger -import net.corda.core.utilities.debug import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.crypto.X509CertificateFactory @@ -120,7 +119,7 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri principalToParties.addWithDuplicatesAllowed(it.name, key, false) } confidentialIdentities.forEach { - principalToParties.addWithDuplicatesAllowed(it.name, mapToKey(it), false) + keyToParties.addWithDuplicatesAllowed(mapToKey(it), it, false) } log.debug("Identities loaded") } @@ -138,17 +137,18 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri } override fun registerIdentity(identity: PartyAndCertificate, isNewRandomIdentity: Boolean): PartyAndCertificate? { + log.debug("Registering identity $identity") val identityCertChain = identity.certPath.x509Certificates - log.debug { "Registering identity $identity" } val key = mapToKey(identity) + if (isNewRandomIdentity) { // Because this is supposed to be new and random, there's no way we have it in the database already, so skip the pessimistic check. keyToParties[key] = identity } else { keyToParties.addWithDuplicatesAllowed(key, identity) + principalToParties.addWithDuplicatesAllowed(identity.name, key, false) } - // Always keep the first party we registered, as that's the well known identity - principalToParties.addWithDuplicatesAllowed(identity.name, key, false) + val parentId = mapToKey(identityCertChain[1].publicKey) return keyToParties[parentId] } diff --git a/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt b/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt index 75d6623e1f..8a378d30c2 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt @@ -58,28 +58,31 @@ abstract class AppendOnlyPersistentMapBase( private fun set(key: K, value: V, logWarning: Boolean, store: (K, V) -> V?): Boolean { // Will be set to true if store says it isn't in the database. var isUnique = false - cache.asMap().compute(key) { _, oldValue -> + cache.asMap().compute(key) { _, oldValueInCache -> // Always write to the database, unless we can see it's already committed. - when (oldValue) { + when (oldValueInCache) { is Transactional.InFlight<*, V> -> { // Someone else is writing, so store away! - isUnique = (store(key, value) == null) - oldValue.apply { alsoWrite(value) } + val oldValueInDB = store(key, value) + isUnique = (oldValueInDB == null) + oldValueInCache.apply { alsoWrite(value) } } - is Transactional.Committed -> oldValue // The value is already globally visible and cached. So do nothing since the values are always the same. + is Transactional.Committed -> oldValueInCache // The value is already globally visible and cached. So do nothing since the values are always the same. is Transactional.Unknown<*, V> -> { - if (oldValue.isResolved && oldValue.isPresent) { - Transactional.Committed(oldValue.value) + if (oldValueInCache.isResolved && oldValueInCache.isPresent) { + Transactional.Committed(oldValueInCache.value) } else { // Unknown. Store away! - isUnique = (store(key, value) == null) - transactionalForStoreResult(isUnique, key, value) + val oldValueInDB = store(key, value) + isUnique = (oldValueInDB == null) + transactionalForStoreResult(key, value, oldValueInDB) } } else -> { // Missing or null. Store away! - isUnique = (store(key, value) == null) - transactionalForStoreResult(isUnique, key, value) + val oldValueInDB = store(key, value) + isUnique = (oldValueInDB == null) + transactionalForStoreResult(key, value, oldValueInDB) } } } @@ -89,10 +92,10 @@ abstract class AppendOnlyPersistentMapBase( return isUnique } - private fun transactionalForStoreResult(isUnique: Boolean, key: K, value: V): Transactional { - return if (!isUnique && !weAreWriting(key)) { + private fun transactionalForStoreResult(key: K, value: V, oldValue: V?): Transactional { + return if ( (oldValue != null) && !weAreWriting(key)) { // If we found a value already in the database, and we were not already writing, then it's already committed but got evicted. - Transactional.Committed(value) + Transactional.Committed(oldValue) } else { // Some database transactions, including us, writing, with readers seeing whatever is in the database and writers seeing the (in memory) value. Transactional.InFlight(this, key, _readerValueLoader = { loadValue(key) }).apply { alsoWrite(value) } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapNonConcurrentTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapNonConcurrentTest.kt new file mode 100644 index 0000000000..85851c5e8f --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapNonConcurrentTest.kt @@ -0,0 +1,79 @@ +package net.corda.node.services.persistence + +import net.corda.core.schemas.MappedSchema +import net.corda.node.services.schema.NodeSchemaService +import net.corda.node.utilities.AppendOnlyPersistentMap +import net.corda.nodeapi.internal.persistence.DatabaseConfig +import net.corda.testing.internal.TestingNamedCacheFactory +import net.corda.testing.internal.configureDatabase +import net.corda.testing.node.MockServices +import org.assertj.core.api.Assertions.assertThat +import org.junit.After +import org.junit.Test +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.Id + +class AppendOnlyPersistentMapNonConcurrentTest { + + private val database = configureDatabase(MockServices.makeTestDataSourceProperties(), + DatabaseConfig(), + { null }, { null }, + NodeSchemaService(setOf(MappedSchema(AppendOnlyPersistentMapTest::class.java, 1, listOf(AppendOnlyPersistentMapNonConcurrentTest.PersistentMapEntry::class.java))))) + + @Entity + @javax.persistence.Table(name = "persist_map_test") + class PersistentMapEntry( + @Id + @Column(name = "key") + var key: Long = -1, + + @Column(name = "value", length = 16) + var value: String = "" + ) + + class TestMap(cacheSize: Long) : AppendOnlyPersistentMap( + cacheFactory = TestingNamedCacheFactory(cacheSize), + name = "ApoendOnlyPersistentMap_test", + toPersistentEntityKey = { it }, + fromPersistentEntity = { Pair(it.key, it.value) }, + toPersistentEntity = { key: Long, value: String -> + PersistentMapEntry().apply { + this.key = key + this.value = value + } + }, + persistentEntityClass = PersistentMapEntry::class.java + ) + + private fun createMap(cacheSize: Long) = TestMap(cacheSize) + + @After + fun closeDatabase() { + database.close() + } + + @Test + fun `map prevents duplicates, when key has been evicted from cache, but present in database`() { + val map = database.transaction { + createMap(1) + } + + + database.transaction { + map.addWithDuplicatesAllowed(1, "1") + map.addWithDuplicatesAllowed(3, "3") + } + + database.transaction { + map.addWithDuplicatesAllowed(1, "2") + } + + val result = database.transaction { + map[1] + } + + assertThat(result).isEqualTo("1") + } + +} \ No newline at end of file From 3c1a8b3de1814d3dc3f49bd3f846255f0964c956 Mon Sep 17 00:00:00 2001 From: Dimos Raptis Date: Mon, 11 Mar 2019 11:56:57 +0000 Subject: [PATCH 082/159] Remove redundant transaction --- .../persistence/AppendOnlyPersistentMapNonConcurrentTest.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapNonConcurrentTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapNonConcurrentTest.kt index 85851c5e8f..47fe441bc4 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapNonConcurrentTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/AppendOnlyPersistentMapNonConcurrentTest.kt @@ -55,10 +55,7 @@ class AppendOnlyPersistentMapNonConcurrentTest { @Test fun `map prevents duplicates, when key has been evicted from cache, but present in database`() { - val map = database.transaction { - createMap(1) - } - + val map = createMap(1) database.transaction { map.addWithDuplicatesAllowed(1, "1") From 9a2c474996484cea012949f75d46e2d132f8e6e3 Mon Sep 17 00:00:00 2001 From: Dimos Raptis Date: Tue, 12 Mar 2019 13:54:15 +0000 Subject: [PATCH 083/159] Make debug lazy --- .../corda/node/services/identity/PersistentIdentityService.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index 2bd8c915e2..552ccd36dc 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -8,6 +8,7 @@ import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.MAX_HASH_HEX_SIZE import net.corda.core.utilities.contextLogger +import net.corda.core.utilities.debug import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.crypto.X509CertificateFactory @@ -137,7 +138,7 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri } override fun registerIdentity(identity: PartyAndCertificate, isNewRandomIdentity: Boolean): PartyAndCertificate? { - log.debug("Registering identity $identity") + log.debug { "Registering identity $identity" } val identityCertChain = identity.certPath.x509Certificates val key = mapToKey(identity) From 93869f0dd7cec277fffcdde4e8454ab8468b50b1 Mon Sep 17 00:00:00 2001 From: carolynequinn <44175553+carolynequinn@users.noreply.github.com> Date: Tue, 12 Mar 2019 16:03:12 +0000 Subject: [PATCH 084/159] Update UAT.md Added in wording to reflect the existing UAT joiner guide, in shortened form, onto the docs site. This will be made better, but is an interim solution. Since we don't have another website suitable for this, our team has agreed with Marketing that this is the place this should live (given it is separate from the Foundation). Will try to edit the toctree so this 'pops out' in the left-hand menu. --- docs/source/corda-network/UAT.md | 144 ++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/docs/source/corda-network/UAT.md b/docs/source/corda-network/UAT.md index a45103a7b1..260b1d7168 100644 --- a/docs/source/corda-network/UAT.md +++ b/docs/source/corda-network/UAT.md @@ -1,10 +1,152 @@ Corda Network: UAT Environment ============================= -For owners of tested CorDapps with a firm plan to take them into production, a bespoke UAT environment is provided by R3 (generally, a commercal agreement will need to be in place). Here, such CorDapps can be further tested in the network configuration they will experience in production, utilising relevant Corda Network Services (including the Identity Operator, and trusted notaries). +For owners of tested CorDapps with a firm plan to take them into production, a bespoke UAT environment can be provided by R3. Here, such CorDapps can be further tested in the network configuration they will experience in production, utilising relevant Corda Network Services (including the Identity Operator, and trusted notaries). Corda UAT is not intended for customers' full test cycles, as it is expected that the bulk of CorDapp testing will occur in simpler network configurations run by the CorDapp provider, but is available for testing of functionally complete and tested CorDapps in realistic network settings to simulate the real-world business environment, including the production settings of network parameters, Corda network services and supported Corda versions. UAT is therefore more aligned to the testing of the operational characteristics of networked CorDapps rather than their specific functional features, although we recognise there can be overlap between the two. Realistic test data is therefore expected to be used and may include data copied from production environments and hence representing real world entities and business activities. It will be up to the introducer of such data to ensure that all relevant data protection legislation is complied with and, in particular, that the terms and conditions under which Corda Network Services processes such data is suitable for their needs. All test data will be cleared down from Corda Network Services on the completion of testing. More information about UAT will continue to be uploaded on this site or related sub-sites. + + +Joining the UAT environment +--------------------------- + +*The below joining steps assume the potential participant is joining the UAT environment directly, and as such is not “sponsoring” or onboarding other participants. If this is the case, please contact your Corda representative for how to ‘sponsor’ end-participants onto UAT.* + +**Pre-requisites:** + +*Technical* +* One or more physical or virtual machines upon which to deploy Corda, with compatible operating system and a compatible Java version (e.g. Oracle JDK 8u131+) +* Corda software - either Open Source or Corda Enterprise (license from R3) +* A static external IP addresses must be available for each machine on which Corda will be run. + +*Business* +* Appropriate contractual terms have been agreed for access to the Services +* Access to the appropriate environment has been agreed with your project representative with sufficient advance notice (4 weeks standard but may be longer if you have special service requirements) to ensure appropriate SLAs can be in place. Your project representative will be able to supply the booking template. + +**Note**: +*Corda Network UAT is an R3 owned and operated environment and service designed to support parties intending to join Corda Network proper with realistic network test facilities. In contrast, Corda Network is a production network governed by an [independent Foundation](https://corda.network/governance/index.html) and has no responsibility for Corda Network UAT. Corda Network UAT seeks to provide a test environment which is as close as possible to Corda Network in its make-up and operation.* + +Steps to join UAT environment +----------------------------- + +**Step 1.** Obtain Corda software - either: +* Open Source, through [github](https://github.com/corda) under an Apache 2 license. +* Corda Enterprise, available via a Corda representative. +There is further guidance available on Corda docs for getting set up on Corda. + +**Step 2.** Request the Trust Root from R3's Identity Operator by mailing uatdoorman@r3.com which will be sent back as a truststore.jks file. In future, the Trust Root will be packaged in the software distribution. + +**Step 3.** [Deploy the node](https://docs.corda.net/deploying-a-node.html) - where applicable, with help from a Corda representative. + +**Step 4.** [Configure the node](https://docs.corda.net/corda-configuration-file.html) – a node.conf file must be included in the root directory of every Corda node. + +Configuring the node includes: + +4.1. **Choosing an email address.** The email address should belong to a suitably authorised employee of the node operator organisation. The email address is only retained by the Operator for the purposes of contact in relation to identity checks and any administrative issues. It is not included in the certificate. + +4.2. **Choosing a Distinguished Name** A DN must be unique within Corda Network. The DN is comprised of separate fields as per the table below. Only O and OU are used for the identity uniqueness check, and the other fields are considered as attributes of the identity. + +All data fields must adhere to the following constraints: +* Only uses Latin, common and inherited unicode scripts +* Upper-case first letter +* At least two letters +* No leading or trailing whitespace +* Does not include the following characters: , , = , $ , " , ' , \ +* Is in NFKC normalization form +* Does not contain the null character + +| | Mandatory | Length (chars) | Validation | Purpose | +| --- | --- | --- | --- | --- | +| **Organisation (O)** | Y | 128 | As per above, and additionally:No double-spacing. May not contain the words "node" or "server". | The O field for the legal entity defines the beneficial owner of states on the ledger. This should be set to the **legal name** of the organisation, as it appears on the official trade register within the jurisdiction in which the entity is registered. This is used to define the owning organisation of the node / certificate. | +| **Organisation Unit (OU)** | N | 64 | As per above | This field is generally used to denote sub-divisions or units of the organisation (legal entity). It may be used by node operators for internal purposes to separate nodes used for different purposes by the same legal entity. | +| **Locality (L)** | Y | 64 | As per above | The city or town in which the registered head-office of the legal entity is located. If the company operates from New York City but is registered in Wilmington, Delaware then please use Wilmington | +| **Country (C)** | Y | 2 | 2-digit ISO code | The country in which the registered head-office of the legal entity is located. | +| **State (S)** | N | 64 | As per above | If your country operates a State or Province system (e.g. USA and Canada) please add the State in which the registered head-office of the legal entity is located. Do not abbreviate. For example, "CA" is not a valid state name. "California" is correct. If the company operates from New York but is registered in Delaware, please use Delaware | +| **Common Name (CN)** | N | 64 | As per above | Available for use by the node operator for their own internal purposes. Often used for home website urls in www. | + +The above fields must be populated accurately with respect to the legal status of the entity being registered. As part of standard onboarding checks for Corda Network, the Identity Operator may verify that these details have been accurately populated and reject requests where the population of these fields does not appear to be correct. + +**4.3. Specify URLs For Initial Registration** +The settings below must be added to the node.conf at the end of the file: + +``` +networkServices { +doormanURL=“https://prod-doorman2-01.corda.network/ED5D077E-F970-428B-8091-F7FCBDA06F8C” +networkMapURL=“https://prod-netmap2-01.corda.network/ED5D077E-F970-428B-8091-F7FCBDA06F8C” +} +devMode = false + +tlsCertCrlDistPoint : “http://crl.corda.network/nodetls.crl” +tlsCertCrlIssuer : “CN=Corda TLS CRL Authority,OU=Corda Network,O=R3 HoldCo LLC,L=New York,C=US” +``` + +**Step 5.** Run the initial registration. +Once the node.conf file is configured, the following should be typed to the command line +"java -jar --initial-registration". This will send a Certificate Signing Request (with the relevant +name and email) to the Identity Operator. + +Once the node.conf file is configured, the following should be typed to the command line "java -jar --initial-registration --network-root-truststore-password trustpass". This will send a CSR (with the relevant DN and email) to the Network Manager service (Identity Operator / Network Map). + +A message similar to the below will be printed to the console: + +``` +Legal Name: O=ABC LIMITED, L=New York, C=US +Email: john.smith@abc.com + +Public Key: EC Public Key + X: d14bc17e650f2a317cbcb95e554f1e26808ca80f67ab804bbc911ec16673abbd + Y: 1978b02a8e693ecd534ceef835091c376cfc4e506decc69b91a872fc13ad1aeb + +-----BEGIN CERTIFICATE REQUEST----- +MIIBLTCBywIBADBMMQswCQYDVQQGEwJVUzERMA8GA1UEBwwITmV3IFlvcmsxFjAU +BgNVBAoMDVIzIEhvbGRDbyBMTEMxEjAQBgNVBAsMCUM4MTUyOTE2NzBZMBMGByqG +SM49AgEGCCqGSM49AwEHA0IABNFLwX5lDyoxfLy5XlVPHiaAjKgPZ6uAS7yRHsFm +c6u9GXiwKo5pPs1TTO74NQkcN2z8TlBt7Mabkahy/BOtGuugHTAbBgkqhkiG9w0B +CQExDgwMYWRtaW5AcjMuY29tMBQGCCqGSM49BAMCBggqhkjOPQMBBwNHADBEAiBA +KLF4NLrleNZPKMoxBrr/80fE3kVbFnYtkB2h0JhX1gIgPcV0X0xZQug+njKCyKgf +DkNUdQJPqhkBBEpgVqyZmE8= +-----END CERTIFICATE REQUEST----- +Submitting certificate signing request to Corda certificate signing server. +Successfully submitted request to Corda certificate signing server, request ID: 6CBB63558B4B2D9C94F8C14AB713432F60AF692EB30F2E12E628B089C517F3CF. +Start polling server for certificate signing approval. +``` + +Important: the Request ID given in the above should be noted and kept safe for future reference. + +**Step 6.** Sign the [UAT Terms of Use](https://fs22.formsite.com/r3cev/CordaUATAgreement2019/index.html) legal document + +*Sponsored Model* +Business Network Operators need to ensure their participants have signed the UAT Terms of Use before they can receive a participation certificate. The Terms of Use are available as a click-through agreement which will provide direct confirmation of acceptance to the Corda Network Operator. If BNOs prefer to organise acceptance themselves, then they must forward appropriate documentary evidence for each participant (either a signed hard copy with wet signature or a scan of such hard copy). You must specify the precise Distinguished Names in order to confirm that the correct entity has signed and an accurate certificate can be issued. + +*Direct Model* +Direct participants should email the Identity Operator indicating acceptance of the in-force Terms of Use (prior to availability of click-through agreements either attach the relevant document or refer to the document by date, name and version number). + +**Step 7. Identity Checks.** +The Identity Operator does verification checks – upon receipt of a CSR, a number of identity-related checks will be conducted, before issuing a certificate. + +**Identity checks do not constitute formal Know Your Customer (KYC) or Enhanced Due Diligence (EDD) checks. Node operators and their users are responsible for carrying out appropriate due diligence on any participant in relation to transactions performed via Corda Network.** + +Upon receipt of a CSR, the Identity Operator will conduct a number of identity-related checks before issuing a certificate: +1. The DN accurately reflects a real-world legal entity, as registered with an appropriate trade register +2. The node operator (participating entity) has signed the Corda Network Terms of Use +3. The contact email address provided is valid +4. The owner of the email address and an independent and suitably qualified person in the same organisation is aware of / approves the CSR + +*Email contact* +The Corda Network Operator will contact the owner of the email address provided in the CSR and it is important that the owner of this email address is aware of and prepared to respond to contact from the Corda Network Operator in relation to the CSR submission, and that they are able to do so on a timely basis. +Issuance of the certificate cannot proceed until contact has been made and so any delay will add to the elapsed time to issue the certificate and enable the node to join the network. Communications will be sent from 'Corda Network UAT Onboarding' (uatdoorman@r3.com). The email owner should ensure that this address is whitelisted by their email provider. + +**Step 8.** Once identity checks have been completed, a signed node CA certificate will be released by the Operator to the node. A node in polling mode will automatically download and install the certificate in its local trust store. It will also automatically generate additional identity and TLS certificates from the node CA certificate, which are required for subsequent operation of the node. + +At this point, the node will terminate and will need to be restarted. Type "java -jar " into the command line. Once restarted, the node will then proceed to download the network map and discover other nodes within Corda Network. By the end of this process, joiners will be a participant in Corda Network and Corda Network Foundation. + +**Confirming your implementation** + +Installation and configuration of your Corda applications must be undertaken by the node operator. Instructions to install CorDapps can be found on https://docs.corda.net. Specifics on application usage or installation should be available from your CorDapp provider. + +Business Network Operators should co-ordinate any post-install tests that may involve a small number of low value transactions on the business network to assure themselves of the correct setup of their node. Node operators should co-ordinate with their Business Network Operator in this regard. All node-initiated activity on the network from the point of connection is the responsibility of the node operator. + +For further questions on this process, please contact us - preferably on the mailing list: https://groups.io/g/corda-network From 56beaf1be509f86dc51c601c5630a3efba74ea5b Mon Sep 17 00:00:00 2001 From: Dimos Raptis Date: Tue, 12 Mar 2019 17:25:47 +0000 Subject: [PATCH 085/159] Docs style fix (#4871) --- docs/source/tutorial-test-dsl.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/tutorial-test-dsl.rst b/docs/source/tutorial-test-dsl.rst index d29125d5ae..4401fa54b0 100644 --- a/docs/source/tutorial-test-dsl.rst +++ b/docs/source/tutorial-test-dsl.rst @@ -99,7 +99,7 @@ Let's add a ``CommercialPaper`` transaction: } We can add a transaction to the ledger using the ``transaction`` primitive. The transaction in turn may be defined by -specifying ``input``s, ``output``s, ``command``s and ``attachment``s. +specifying ``input``\s, ``output``\s, ``command``\s and ``attachment``\s. The above ``input`` call is a bit special; transactions don't actually contain input states, just references to output states of other transactions. Under the hood the above ``input`` call creates a dummy transaction in the From dae560e02580bb726d2ed39c096b3c7ca8027a39 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Tue, 12 Mar 2019 18:20:50 +0000 Subject: [PATCH 086/159] RELEASE - Add password reset, cope with all git tickets marked in Jira (#4883) * RELEASE - Add password reset, cope with all git tickets marked in Jira * Update jiraReleaseChecker.py --- release-tools/jiraReleaseChecker.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/release-tools/jiraReleaseChecker.py b/release-tools/jiraReleaseChecker.py index 47e1e82fb3..342fbb7958 100755 --- a/release-tools/jiraReleaseChecker.py +++ b/release-tools/jiraReleaseChecker.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/local/bin/python #------------------------------------------------------------------------------- # @@ -95,16 +95,20 @@ JIRA_MAX_RESULTS = 50 # For a given user (provide via the command line) authenticate with Jira and # return an interface object instance # -def jiraLogin(user) : - password = keyring.get_password ('jira', user) if not disableKeyring else None +def jiraLogin(args_) : + if not args_.resetPassword : + password = keyring.get_password ('jira', args_.jiraUser) if not disableKeyring else None + else : + password = None if not password: password = getpass.getpass("Please enter your JIRA password, " + "it will be stored in your OS Keyring: ") - if not disableKeyring : - keyring.set_password ('jira', user, password) - return JIRA(R3_JIRA_ADDR, auth=(user, password)) + if not disableKeyring : + keyring.set_password ('jira', args_.jiraUser, password) + + return JIRA(R3_JIRA_ADDR, auth=(args_.jiraUser, password)) #------------------------------------------------------------------------------- @@ -148,7 +152,7 @@ def issueToRST(issue) : # is returned for reuse. # def getJirasFromJira(args_, jira_ = None) : - jira = jiraLogin(args_.jiraUser) if jira_ == None else jira_ + jira = jiraLogin(args_) if jira_ == None else jira_ return jiraQuery(jira, \ 'project in (Corda, Ent) And fixVersion in ("%s") and status in (Done)' % (args_.jiraTag)) \ @@ -157,7 +161,7 @@ def getJirasFromJira(args_, jira_ = None) : #------------------------------------------------------------------------------- def getJiraIdsFromJira(args_, jira_ = None) : - jira = jiraLogin(args_.jiraUser) if jira_ == None else jira_ + jira = jiraLogin(args_) if jira_ == None else jira_ jirasFromJira, _ = jiraQuery(jira, \ 'project in (Corda, Ent) And fixVersion in ("%s") and status in (Done)' % (args_.jiraTag)) \ @@ -196,7 +200,9 @@ def rst (args_) : # to get access to their summary # extraJiras = set(jiraIdsFromCommits).difference(jiraIdsFromJira) - jirasFromJira += jiraQuery(jiraObj, "key in (%s)" % (", ".join(extraJiras))) + + if extraJiras != set() : + jirasFromJira += jiraQuery(jiraObj, "key in (%s)" % (", ".join(extraJiras))) for jira in jirasFromJira : print issueToRST(jira) @@ -232,6 +238,8 @@ if __name__ == "__main__" : parser.add_argument("oldTag", help="The previous release tag") parser.add_argument("jiraTag", help="The current Jira release") parser.add_argument("jiraUser", help="Who to authenticate with Jira as") + parser.add_argument("--resetPassword", help="Set flag to allow resetting of password in the keyring", + action='store_true') args = parser.parse_args() From 92308021bf5943d8669358ccdf69bab04a816688 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 12 Mar 2019 18:32:07 +0000 Subject: [PATCH 087/159] CORDA-2629: Configure smoke-test nodes to have validating notaries. (#4881) --- .../src/main/kotlin/net/corda/smoketesting/NodeProcess.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt index 79603dbefa..0d4bc8af8f 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt @@ -101,7 +101,7 @@ class NodeProcess( } (nodeDir / "node.conf").writeText(config.toText()) - createNetworkParameters(NotaryInfo(notaryParty!!, false), nodeDir) + createNetworkParameters(NotaryInfo(notaryParty!!, true), nodeDir) val process = startNode(nodeDir) val client = CordaRPCClient(NetworkHostAndPort("localhost", config.rpcPort)) From 0551ba992baa3298ea17b004d566001ebe1c298e Mon Sep 17 00:00:00 2001 From: Ben Wyeth Date: Wed, 13 Mar 2019 16:23:31 +0000 Subject: [PATCH 088/159] CORDA-2728: Add Quasar lib update step to applicataion upgrade notes (#4877) * updated README so that it has a reference to the rst docs we care about * adding some upgrade nodes for cordapps that have a lib/quasar.jar --- docs/README.md | 6 ++++++ docs/source/app-upgrade-notes.rst | 16 +++++++++++++++- docs/source/tutorial-cordapp.rst | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 648273ec2b..67931ccfb2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,3 +6,9 @@ Note: In order to run the documentation build you will need Docker installed. Windows users: If this task fails because Docker can't find make-docsite.sh, go to Settings > Shared Drives in the Docker system tray agent, make sure the relevant drive is shared, and click 'Reset credentials'. + +## rst style guide + +It's probably worth reading [this](http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) +to get your head around the rst syntax we're using. + diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index ddd9d9aa6b..67b714c1e9 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -434,4 +434,18 @@ Corda 4 adds several new APIs that help you build applications. Why not explore: * :ref:`reference_states`, that let you use an input state without consuming it. * :ref:`state_pointers`, that make it easier to 'point' to one state from another and follow the latest version of a linear state. -Please also read the :doc:`CorDapp Upgradeability Guarantees ` associated with CorDapp upgrading. \ No newline at end of file +Please also read the :doc:`CorDapp Upgradeability Guarantees ` associated with CorDapp upgrading. + +Step 14. Possibly update your checked in quasar.jar +--------------------------------------------------- + +If your project is based on one of the official cordapp templates, it is likely you have a ``lib/quasar.jar`` checked in. It is worth noting +that you only use this if you use the JUnit runner in IntelliJ. In the latest release of the cordapp templates, this directory has +been removed. + +You have some choices here: + +* Upgrade your ``quasar.jar`` to the version consistent with your Corda version +* Delete your ``lib`` directory and switch to using the Gradle test runner + +Instructions for both options can be found in :ref:`Running tests in Intellij `. diff --git a/docs/source/tutorial-cordapp.rst b/docs/source/tutorial-cordapp.rst index d67f2bbc26..b0a7678b94 100644 --- a/docs/source/tutorial-cordapp.rst +++ b/docs/source/tutorial-cordapp.rst @@ -481,6 +481,8 @@ Integration tests ~~~~~~~~~~~~~~~~~ You can run the CorDapp's integration tests by running the ``Run Integration Tests - Kotlin`` run configuration. +.. _tutorial_cordapp_running_tests_intellij: + Running tests in IntelliJ ~~~~~~~~~~~~~~~~~~~~~~~~~ From 94d827ebe4ed16526cf2fe901139f05c7635b5fc Mon Sep 17 00:00:00 2001 From: josecoll Date: Wed, 13 Mar 2019 16:31:28 +0000 Subject: [PATCH 089/159] CORDA-2741 RPC client connection management section not fully working (#4870) * RPC Client using HA addresses. * Fix incorrect document code snippets by referencing working, compilable code. * Minor updates following PR review. --- docs/source/clientrpc.rst | 85 ++++-------------- .../main/kotlin/net/corda/bank/IssueCash.kt | 4 +- .../corda/bank/api/BankOfCordaClientApi.kt | 88 +++++++++++++++++-- 3 files changed, 100 insertions(+), 77 deletions(-) diff --git a/docs/source/clientrpc.rst b/docs/source/clientrpc.rst index e63b28d61e..670768c50a 100644 --- a/docs/source/clientrpc.rst +++ b/docs/source/clientrpc.rst @@ -358,82 +358,33 @@ It is possible to not be able to connect to the server on the first attempt. In method will throw an exception. The following code snippet is an example of how to write a simple retry mechanism for such situations: -.. sourcecode:: Kotlin +.. literalinclude:: ../../samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt + :language: kotlin + :start-after: DOCSTART rpcClientConnectionWithRetry + :end-before: DOCEND rpcClientConnectionWithRetry - fun establishConnectionWithRetry(nodeHostAndPort: NetworkHostAndPort, username: String, password: String): CordaRPCConnection { - val retryInterval = 5.seconds - - do { - val connection = try { - logger.info("Connecting to: $nodeHostAndPort") - val client = CordaRPCClient( - nodeHostAndPort, - object : CordaRPCClientConfiguration { - override val connectionMaxRetryInterval = retryInterval - } - ) - val _connection = client.start(username, password) - // Check connection is truly operational before returning it. - val nodeInfo = _connection.proxy.nodeInfo() - require(nodeInfo.legalIdentitiesAndCerts.isNotEmpty()) - _connection - } catch(secEx: ActiveMQSecurityException) { - // Happens when incorrect credentials provided - no point to retry connecting. - throw secEx - } - catch(ex: RPCException) { - // Deliberately not logging full stack trace as it will be full of internal stacktraces. - logger.info("Exception upon establishing connection: " + ex.message) - null - } - - if(connection != null) { - logger.info("Connection successfully established with: $nodeHostAndPort") - return connection - } - // Could not connect this time round - pause before giving another try. - Thread.sleep(retryInterval.toMillis()) - } while (connection == null) - } +.. warning:: The list of ``NetworkHostAndPort`` passed to this function should represent one or more addresses reflecting the number of + instances of a node configured to service the client RPC request. See ``haAddressPool`` in `CordaRPCClient`_ for further information on + using an RPC Client for load balancing and failover. After a successful connection, it is possible for the server to become unavailable. In this case, all RPC calls will throw an exception and created observables will no longer receive observations. Below is an example of how to reconnect and back-fill any data that might have been missed while the connection was down. This is done by using the ``onError`` handler on the ``Observable`` returned by ``CordaRPCOps``. -.. sourcecode:: Kotlin +.. literalinclude:: ../../samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt + :language: kotlin + :start-after: DOCSTART rpcClientConnectionRecovery + :end-before: DOCEND rpcClientConnectionRecovery - fun performRpcReconnect(nodeHostAndPort: NetworkHostAndPort, username: String, password: String) { - val connection = establishConnectionWithRetry(nodeHostAndPort, username, password) - val proxy = connection.proxy +In this code snippet it is possible to see that the function ``performRpcReconnect`` creates an RPC connection and implements +the error handler upon subscription to an ``Observable``. The call to this ``onError`` handler will be triggered upon failover, at which +point the client will terminate its existing subscription, close its RPC connection and recursively call ``performRpcReconnect``, +which will re-subscribe once the RPC connection is re-established. - val (stateMachineInfos, stateMachineUpdatesRaw) = proxy.stateMachinesFeed() - - val retryableStateMachineUpdatesSubscription: AtomicReference = AtomicReference(null) - val subscription: Subscription = stateMachineUpdatesRaw - .startWith(stateMachineInfos.map { StateMachineUpdate.Added(it) }) - .subscribe({ clientCode(it) /* Client code here */ }, { - // Terminate subscription such that nothing gets past this point to downstream Observables. - retryableStateMachineUpdatesSubscription.get()?.unsubscribe() - // It is good idea to close connection to properly mark the end of it. During re-connect we will create a new - // client and a new connection, so no going back to this one. Also the server might be down, so we are - // force closing the connection to avoid propagation of notification to the server side. - connection.forceClose() - // Perform re-connect. - performRpcReconnect(nodeHostAndPort, username, password) - }) - - retryableStateMachineUpdatesSubscription.set(subscription) - } - -In this code snippet it is possible to see that function ``performRpcReconnect`` creates an RPC connection and implements -the error handler upon subscription to an ``Observable``. The call to this ``onError`` handler will be made when failover -happens then the code will terminate existing subscription, closes RPC connection and recursively calls ``performRpcReconnect`` -which will re-subscribe once RPC connection comes back online. - -Client code if fed with instances of ``StateMachineInfo`` using call ``clientCode(it)``. Upon re-connecting, this code receives -all the items. Some of these items might have already been delivered to client code prior to failover occurred. -It is down to client code in this case handle those duplicate items as appropriate. +Within the body of the ``subscribe`` function itself, the client code receives instances of ``StateMachineInfo``. Upon re-connecting, this code receives +*all* the instances of ``StateMachineInfo``, some of which may already been delivered to the client code prior to previous disconnect. +It is the responsibility of the client code to handle potential duplicated instances of ``StateMachineInfo`` as appropriate. Wire security ------------- diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/IssueCash.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/IssueCash.kt index 24ac13e1d1..4416847d45 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/IssueCash.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/IssueCash.kt @@ -5,7 +5,6 @@ import net.corda.bank.api.BankOfCordaClientApi import net.corda.bank.api.BankOfCordaWebApi import net.corda.core.contracts.Amount import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.VisibleForTesting import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort import net.corda.testing.core.BOC_NAME @@ -48,8 +47,7 @@ object IssueCash { } } - @VisibleForTesting - fun requestRpcIssue(amount: Amount): SignedTransaction { + private fun requestRpcIssue(amount: Amount): SignedTransaction { return BankOfCordaClientApi.requestRPCIssue(NetworkHostAndPort("localhost", BOC_RPC_PORT), createParams(amount, NOTARY_NAME)) } diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt index dd79412e77..c8188c3ef7 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt @@ -2,13 +2,18 @@ package net.corda.bank.api import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams import net.corda.client.rpc.CordaRPCClient +import net.corda.client.rpc.CordaRPCClientConfiguration +import net.corda.client.rpc.CordaRPCConnection +import net.corda.client.rpc.RPCException +import net.corda.core.messaging.StateMachineUpdate import net.corda.core.messaging.startFlow import net.corda.core.transactions.SignedTransaction -import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.getOrThrow +import net.corda.core.utilities.* import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.testing.http.HttpApi +import org.apache.activemq.artemis.api.core.ActiveMQSecurityException +import rx.Subscription +import java.util.concurrent.atomic.AtomicReference /** * Interface for communicating with Bank of Corda node @@ -17,6 +22,8 @@ object BankOfCordaClientApi { const val BOC_RPC_USER = "bankUser" const val BOC_RPC_PWD = "test" + private val logger = loggerFor() + /** * HTTP API */ @@ -29,12 +36,19 @@ object BankOfCordaClientApi { /** * RPC API * - * @return a pair of the issuing and payment transactions. + * @return a payment transaction (following successful issuance of cash to self). */ - fun requestRPCIssue(rpcAddress: NetworkHostAndPort, params: IssueRequestParams): SignedTransaction { - val client = CordaRPCClient(rpcAddress) + fun requestRPCIssue(rpcAddress: NetworkHostAndPort, params: IssueRequestParams): SignedTransaction = requestRPCIssueHA(listOf(rpcAddress), params) + + /** + * RPC API + * + * @return a cash issue transaction. + */ + fun requestRPCIssueHA(availableRpcServers: List, params: IssueRequestParams): SignedTransaction { + val client = performRpcReconnect(availableRpcServers, BOC_RPC_USER, BOC_RPC_PWD) // TODO: privileged security controls required - client.start(BOC_RPC_USER, BOC_RPC_PWD).use { connection -> + client.use { connection -> val rpc = connection.proxy rpc.waitUntilNetworkReady().getOrThrow() @@ -47,8 +61,68 @@ object BankOfCordaClientApi { val anonymous = true val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte()) + logger.info("${rpc.nodeInfo()} issuing ${params.amount} to transfer to $issueToParty ...") return rpc.startFlow(::CashIssueAndPaymentFlow, params.amount, issuerBankPartyRef, issueToParty, anonymous, notaryLegalIdentity) .returnValue.getOrThrow().stx } } + + // DOCSTART rpcClientConnectionRecovery + fun performRpcReconnect(nodeHostAndPorts: List, username: String, password: String): CordaRPCConnection { + val connection = establishConnectionWithRetry(nodeHostAndPorts, username, password) + val proxy = connection.proxy + + val (stateMachineInfos, stateMachineUpdatesRaw) = proxy.stateMachinesFeed() + + val retryableStateMachineUpdatesSubscription: AtomicReference = AtomicReference(null) + val subscription: Subscription = stateMachineUpdatesRaw + .startWith(stateMachineInfos.map { StateMachineUpdate.Added(it) }) + .subscribe({ /* Client code here */ }, { + // Terminate subscription such that nothing gets past this point to downstream Observables. + retryableStateMachineUpdatesSubscription.get()?.unsubscribe() + // It is good idea to close connection to properly mark the end of it. During re-connect we will create a new + // client and a new connection, so no going back to this one. Also the server might be down, so we are + // force closing the connection to avoid propagation of notification to the server side. + connection.forceClose() + // Perform re-connect. + performRpcReconnect(nodeHostAndPorts, username, password) + }) + + retryableStateMachineUpdatesSubscription.set(subscription) + return connection + } + // DOCEND rpcClientConnectionRecovery + + // DOCSTART rpcClientConnectionWithRetry + private fun establishConnectionWithRetry(nodeHostAndPorts: List, username: String, password: String): CordaRPCConnection { + val retryInterval = 5.seconds + var connection: CordaRPCConnection? + do { + connection = try { + logger.info("Connecting to: $nodeHostAndPorts") + val client = CordaRPCClient( + nodeHostAndPorts, + CordaRPCClientConfiguration(connectionMaxRetryInterval = retryInterval) + ) + val _connection = client.start(username, password) + // Check connection is truly operational before returning it. + val nodeInfo = _connection.proxy.nodeInfo() + require(nodeInfo.legalIdentitiesAndCerts.isNotEmpty()) + _connection + } catch (secEx: ActiveMQSecurityException) { + // Happens when incorrect credentials provided - no point retrying connection + logger.info("Security exception upon attempt to establish connection: " + secEx.message) + throw secEx + } catch (ex: RPCException) { + logger.info("Exception upon attempt to establish connection: " + ex.message) + null // force retry after sleep + } + // Could not connect this time round - pause before giving another try. + Thread.sleep(retryInterval.toMillis()) + } while (connection == null) + + logger.info("Connection successfully established with: ${connection.proxy.nodeInfo()}") + return connection + } + // DOCEND rpcClientConnectionWithRetry } From 76dc981b910acf77c3f122a1db476aa4992c452b Mon Sep 17 00:00:00 2001 From: JamesHR3 <45565019+JamesHR3@users.noreply.github.com> Date: Wed, 13 Mar 2019 16:33:29 +0000 Subject: [PATCH 090/159] [CORDA-2737] Buffer events from observables in ProgressTracker until subscribed to (#4882) --- .../corda/core/utilities/ProgressTracker.kt | 5 +-- .../core/utilities/ProgressTrackerTest.kt | 45 ++++++++++++++++--- .../finance/flows/CashIssueAndPaymentFlow.kt | 10 +++-- .../statemachine/FlowFrameworkTests.kt | 2 + .../shell/utlities/ANSIProgressRenderer.kt | 2 - 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt index 554526d71c..b511a26a27 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt @@ -5,7 +5,6 @@ import net.corda.core.internal.STRUCTURAL_STEP_PREFIX import net.corda.core.serialization.CordaSerializable import rx.Observable import rx.Subscription -import rx.subjects.PublishSubject import rx.subjects.ReplaySubject import java.util.* @@ -91,8 +90,8 @@ class ProgressTracker(vararg inputSteps: Step) { private var _allStepsCache: List> = _allSteps() // This field won't be serialized. - private val _changes by transient { PublishSubject.create() } - private val _stepsTreeChanges by transient { PublishSubject.create>>() } + private val _changes by transient { ReplaySubject.create() } + private val _stepsTreeChanges by transient { ReplaySubject.create>>() } private val _stepsTreeIndexChanges by transient { ReplaySubject.create() } var currentStep: Step diff --git a/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt b/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt index 0014cabbcd..70ccd7fddb 100644 --- a/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt +++ b/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt @@ -130,7 +130,7 @@ class ProgressTrackerTest { // Assert no structure changes and proper steps propagation. assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 2, 4, 6)) - assertThat(stepsTreeNotification).isEmpty() + assertThat(stepsTreeNotification).hasSize(1) // One entry per child progress tracker set } @Test @@ -165,7 +165,7 @@ class ProgressTrackerTest { // Assert no structure changes and proper steps propagation. assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 4, 7)) - assertThat(stepsTreeNotification).isEmpty() + assertThat(stepsTreeNotification).hasSize(2) // One entry per child progress tracker set } @Test @@ -179,7 +179,7 @@ class ProgressTrackerTest { } // Put current state as a first change for simplicity when asserting. - val stepsTreeNotification = mutableListOf(pt.allStepsLabels) + val stepsTreeNotification = mutableListOf>>() pt.stepsTreeChanges.subscribe { stepsTreeNotification += it } @@ -202,7 +202,7 @@ class ProgressTrackerTest { // Assert no structure changes and proper steps propagation. assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(2, 7, 10)) - assertThat(stepsTreeNotification).hasSize(2) // 1 change + 1 our initial state + assertThat(stepsTreeNotification).hasSize(2) // One state per child progress tracker set } @Test @@ -216,7 +216,7 @@ class ProgressTrackerTest { } // Put current state as a first change for simplicity when asserting. - val stepsTreeNotification = mutableListOf(pt.allStepsLabels) + val stepsTreeNotification = mutableListOf>>() pt.stepsTreeChanges.subscribe { stepsTreeNotification += it } @@ -237,7 +237,7 @@ class ProgressTrackerTest { // Assert no structure changes and proper steps propagation. assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(2, 5, 3)) - assertThat(stepsTreeNotification).hasSize(2) // 1 change + 1 our initial state. + assertThat(stepsTreeNotification).hasSize(2) // One state per child progress tracker set } @Test @@ -264,4 +264,37 @@ class ProgressTrackerTest { assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 2, 3)) } + + @Test + fun `all step changes seen if subscribed mid flow`() { + val steps = mutableListOf() + pt.nextStep() + pt.nextStep() + pt.nextStep() + pt.changes.subscribe { steps.add(it.toString())} + pt.nextStep() + pt.nextStep() + pt.nextStep() + assertEquals(listOf("Starting", "one", "two", "three", "four", "Done"), steps) + } + + @Test + fun `all tree changes seen if subscribed mid flow`() { + val stepTreeNotifications = mutableListOf>>() + pt.setChildProgressTracker(SimpleSteps.TWO, pt2) + + pt.currentStep = SimpleSteps.ONE + pt.currentStep = SimpleSteps.TWO + + pt.setChildProgressTracker(SimpleSteps.TWO, pt3) + pt.stepsTreeChanges.subscribe { stepTreeNotifications.add(it)} + + fun assertStepsTree(index: Int, step: ProgressTracker.Step) { + assertEquals(step.label, stepTreeNotifications[index][pt.stepsTreeIndex].second) + } + pt2.currentStep = ChildSteps.AYY + pt3.currentStep = BabySteps.UNOS + assertStepsTree(0, ChildSteps.AYY) + assertStepsTree(1, BabySteps.UNOS) + } } diff --git a/finance/workflows/src/main/kotlin/net/corda/finance/flows/CashIssueAndPaymentFlow.kt b/finance/workflows/src/main/kotlin/net/corda/finance/flows/CashIssueAndPaymentFlow.kt index 08618cabce..1370f59fc6 100644 --- a/finance/workflows/src/main/kotlin/net/corda/finance/flows/CashIssueAndPaymentFlow.kt +++ b/finance/workflows/src/main/kotlin/net/corda/finance/flows/CashIssueAndPaymentFlow.kt @@ -10,14 +10,16 @@ import net.corda.core.utilities.ProgressTracker import java.util.* /** - * Initiates a flow that self-issues cash (which should then be sent to recipient(s) using a payment transaction). + * Initiates a flow that self-issues cash and then send this to a recipient. * * We issue cash only to ourselves so that all KYC/AML checks on payments are enforced consistently, rather than risk * checks for issuance and payments differing. Outside of test scenarios it would be extremely unusual to issue cash * and immediately transfer it, so impact of this limitation is considered minimal. * * @param amount the amount of currency to issue. - * @param issuerBankPartyRef a reference to put on the issued currency. + * @param issueRef a reference to put on the issued currency. + * @param recipient the recipient of the currency + * @param anonymous if true, the recipient of the cash will be anonymous. Should be true for normal usage * @param notary the notary to set on the output states. */ @StartableByRPC @@ -31,9 +33,9 @@ class CashIssueAndPaymentFlow(val amount: Amount, issueRef: OpaqueBytes, recipient: Party, anonymous: Boolean, - notary: Party) : this(amount, issueRef, recipient, anonymous, notary, tracker()) + notary: Party) : this(amount, issueRef, recipient, anonymous, notary, ProgressTracker()) - constructor(request: IssueAndPaymentRequest) : this(request.amount, request.issueRef, request.recipient, request.anonymous, request.notary, tracker()) + constructor(request: IssueAndPaymentRequest) : this(request.amount, request.issueRef, request.recipient, request.anonymous, request.notary, ProgressTracker()) @Suspendable override fun call(): Result { diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 383605cd2c..b31c446986 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -193,6 +193,7 @@ class FlowFrameworkTests { assertThat(receivingFiber.state).isEqualTo(Strand.State.WAITING) assertThat((erroringFlow.get().stateMachine as FlowStateMachineImpl).state).isEqualTo(Strand.State.WAITING) assertThat(erroringFlowSteps.get()).containsExactly( + Notification.createOnNext(ProgressTracker.STARTING), Notification.createOnNext(ExceptionFlow.START_STEP), Notification.createOnError(erroringFlow.get().exceptionThrown) ) @@ -413,6 +414,7 @@ class FlowFrameworkTests { erroringFlowFuture.getOrThrow() val flowSteps = erroringFlowSteps.get() assertThat(flowSteps).containsExactly( + Notification.createOnNext(ProgressTracker.STARTING), Notification.createOnNext(ExceptionFlow.START_STEP), Notification.createOnError(erroringFlowFuture.get().exceptionThrown) ) diff --git a/tools/shell/src/main/kotlin/net/corda/tools/shell/utlities/ANSIProgressRenderer.kt b/tools/shell/src/main/kotlin/net/corda/tools/shell/utlities/ANSIProgressRenderer.kt index 57ccf776da..341454ff77 100644 --- a/tools/shell/src/main/kotlin/net/corda/tools/shell/utlities/ANSIProgressRenderer.kt +++ b/tools/shell/src/main/kotlin/net/corda/tools/shell/utlities/ANSIProgressRenderer.kt @@ -78,7 +78,6 @@ abstract class ANSIProgressRenderer { flowProgressHandle?.apply { stepsTreeIndexFeed?.apply { - treeIndex = snapshot treeIndexProcessed.add(snapshot) subscriptionIndex = updates.subscribe({ treeIndex = it @@ -87,7 +86,6 @@ abstract class ANSIProgressRenderer { }, { done(it) }, { done(null) }) } stepsTreeFeed?.apply { - tree = snapshot subscriptionTree = updates.subscribe({ remapIndices(it) tree = it From 077f7385c75647490cf30b8202c7cb9f395c25b6 Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Thu, 14 Mar 2019 13:39:55 +0000 Subject: [PATCH 091/159] CORDA-2393 Improve documentation for PostgreSQL to avoid missing hibernate_sequence (#4891) --- docs/source/node-database.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/source/node-database.rst b/docs/source/node-database.rst index 9528f9c15e..5eb6dbd6f4 100644 --- a/docs/source/node-database.rst +++ b/docs/source/node-database.rst @@ -32,11 +32,19 @@ configuration for PostgreSQL: Note that: -* Database schema name can be set in JDBC URL string e.g. currentSchema=myschema +* Database schema name can be set in JDBC URL string e.g. *currentSchema=my_schema* * Database schema name must either match the ``dataSource.user`` value to end up on the standard schema search path according to the `PostgreSQL documentation `_, or the schema search path must be set explicitly for the user. +* If your PostgresSQL database is hosting multiple schema instances (using the JDBC URL currentSchema=my_schema) + for different Corda nodes, you will need to create a *hibernate_sequence* sequence object manually for each subsequent schema added after the first instance. + Corda doesn't provision Hibernate with a schema namespace setting and a sequence object may be not created. + Run the DDL statement and replace *my_schema* with your schema namespace: + + .. sourcecode:: groovy + + CREATE SEQUENCE my_schema.hibernate_sequence INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 8 CACHE 1 NO CYCLE; SQLServer --------- From 03967742e43a7522ddc43f06d94373befc54a5e6 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Thu, 14 Mar 2019 17:42:39 +0000 Subject: [PATCH 092/159] CORDA-2747: Package DJVM CLI for delivery. (#4893) * Modify DJVM CLI so that it runs as executable JAR. * Package DJVM CLI as a distributable ZIP. * Add djvm.bat file for Windows. --- build.gradle | 1 + djvm/build.gradle | 6 ++--- djvm/cli/build.gradle | 48 +++++++++++++++++++++++++++++++------ djvm/cli/src/shell/djvm | 16 +++++++++++++ djvm/cli/src/shell/djvm.bat | 15 ++++++++++++ djvm/cli/src/shell/install | 7 ++++++ 6 files changed, 83 insertions(+), 10 deletions(-) create mode 100755 djvm/cli/src/shell/djvm create mode 100644 djvm/cli/src/shell/djvm.bat create mode 100755 djvm/cli/src/shell/install diff --git a/build.gradle b/build.gradle index f25bd52f8e..702c54fd17 100644 --- a/build.gradle +++ b/build.gradle @@ -364,6 +364,7 @@ bintrayConfig { 'corda-core-deterministic', 'corda-deterministic-verifier', 'corda-djvm', + 'corda-djvm-cli', 'corda', 'corda-finance-workflows', 'corda-finance-contracts', diff --git a/djvm/build.gradle b/djvm/build.gradle index 3a50ad7e6b..43775d36ce 100644 --- a/djvm/build.gradle +++ b/djvm/build.gradle @@ -1,9 +1,9 @@ plugins { id 'com.github.johnrengelman.shadow' + id 'net.corda.plugins.publish-utils' + id 'com.jfrog.artifactory' + id 'idea' } -apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'com.jfrog.artifactory' -apply plugin: 'idea' description 'Corda deterministic JVM sandbox' diff --git a/djvm/cli/build.gradle b/djvm/cli/build.gradle index e6399e3e69..e38d6d0909 100644 --- a/djvm/cli/build.gradle +++ b/djvm/cli/build.gradle @@ -1,10 +1,18 @@ plugins { id 'com.github.johnrengelman.shadow' + id 'net.corda.plugins.publish-utils' + id 'com.jfrog.artifactory' +} + +description 'Corda deterministic JVM sandbox command-line tool' + +ext { + djvmName = 'corda-djvm-cli' } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + implementation "org.jetbrains.kotlin:kotlin-reflect" implementation "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" implementation "com.jcabi:jcabi-manifests:$jcabi_manifests_version" @@ -15,14 +23,40 @@ dependencies { jar.enabled = false shadowJar { - baseName = "corda-djvm" - classifier = 'cli' + baseName = djvmName + classifier = '' manifest { attributes( - 'Automatic-Module-Name': 'net.corda.djvm', + 'Automatic-Module-Name': 'net.corda.djvm.cli', 'Main-Class': 'net.corda.djvm.tools.cli.Program', - 'Build-Date': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") + 'Build-Date': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + 'Class-Path': 'tmp/' ) } } -assemble.dependsOn shadowJar + +task shadowZip(type: Zip) { + baseName = djvmName + classifier = '' + + from(shadowJar) { + rename "$djvmName-(.*).jar", "${djvmName}.jar" + } + from('src/shell/') { + fileMode = 0755 + } + zip64 true +} + +assemble.dependsOn shadowZip + +artifacts { + publish shadowZip +} + +publish { + dependenciesFrom configurations.shadow + publishSources = false + publishJavadoc = false + name shadowZip.baseName +} diff --git a/djvm/cli/src/shell/djvm b/djvm/cli/src/shell/djvm new file mode 100755 index 0000000000..368b50beb7 --- /dev/null +++ b/djvm/cli/src/shell/djvm @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +SCRIPT_DIR=$(dirname $(readlink -f ${BASH_SOURCE[0]})) + +CLASSPATH="${CLASSPATH:-}" + +DEBUG=`echo "${DEBUG:-0}" | sed 's/^[Nn][Oo]*$/0/g'` +DEBUG_PORT=5005 +DEBUG_AGENT="" + +if [ "$DEBUG" != 0 ]; then + echo "Opening remote debugging session on port $DEBUG_PORT" + DEBUG_AGENT="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=$DEBUG_PORT" +fi + +exec java $DEBUG_AGENT -cp "$CLASSPATH:.:tmp:$SCRIPT_DIR/corda-djvm-cli.jar" net.corda.djvm.tools.cli.Program "$@" diff --git a/djvm/cli/src/shell/djvm.bat b/djvm/cli/src/shell/djvm.bat new file mode 100644 index 0000000000..74fc1386d0 --- /dev/null +++ b/djvm/cli/src/shell/djvm.bat @@ -0,0 +1,15 @@ +@ECHO off + +SETLOCAL ENABLEEXTENSIONS + +IF NOT DEFINED CLASSPATH (SET CLASSPATH=) + +IF DEFINED DEBUG ( + SET DEBUG_PORT=5005 + SET DEBUG_AGENT=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=%DEBUG_PORT% + ECHO Opening remote debugging session on port %DEBUG_PORT% +) ELSE ( + SET DEBUG_AGENT= +) + +CALL java %DEBUG_AGENT% -cp "%CLASSPATH%;.;tmp;%~dp0\corda-djvm-cli.jar" net.corda.djvm.tools.cli.Program %* diff --git a/djvm/cli/src/shell/install b/djvm/cli/src/shell/install new file mode 100755 index 0000000000..69058ef4f1 --- /dev/null +++ b/djvm/cli/src/shell/install @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +SCRIPT_DIR=$(dirname $(readlink -f ${BASH_SOURCE[0]})) + +# Generate auto-completion file for Bash and ZSH +java -cp ${SCRIPT_DIR}/corda-djvm-cli.jar \ + picocli.AutoComplete -n djvm net.corda.djvm.tools.cli.Commands -f From 82c45c6f83aa01c9a6ac0be64375a1a7c3aad4b2 Mon Sep 17 00:00:00 2001 From: Jonathan Locke <36930160+lockathan@users.noreply.github.com> Date: Fri, 15 Mar 2019 09:54:53 +0000 Subject: [PATCH 093/159] CORDA-2506: Better handling of invalid log path (#4895) Test if we have access to the logging path (baseDirectory/logs) before attempting to write to them. This allows us to shut down gracefully with an easily understandable error message. Without doing this, the log4j2 will attempt to access the logPath when it first uses the logger in the call() function. --- .../net/corda/node/internal/NodeStartup.kt | 26 ++++++++++++++++--- .../subcommands/InitialRegistrationCli.kt | 2 +- .../subcommands/ValidateConfigurationCli.kt | 2 +- .../net/corda/cliutils/CordaCliWrapper.kt | 14 ++++++---- .../net/corda/tools/shell/StandaloneShell.kt | 3 ++- 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index e4c31906b9..ba8a87da89 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -1,10 +1,12 @@ package net.corda.node.internal import io.netty.channel.unix.Errors +import net.corda.cliutils.printError import net.corda.cliutils.CliWrapperBase import net.corda.cliutils.CordaCliWrapper import net.corda.cliutils.CordaVersionProvider import net.corda.cliutils.ExitCodes +import net.corda.cliutils.ShellConstants import net.corda.core.contracts.HashAttachmentConstraint import net.corda.core.crypto.Crypto import net.corda.core.internal.* @@ -53,7 +55,7 @@ abstract class NodeCliCommand(alias: String, description: String, val startup: N const val LOGS_DIRECTORY_NAME = "logs" } - override fun initLogging() = this.initLogging(cmdLineOptions.baseDirectory) + override fun initLogging(): Boolean = this.initLogging(cmdLineOptions.baseDirectory) @Mixin val cmdLineOptions = SharedNodeCmdLineOptions() @@ -71,7 +73,7 @@ open class NodeStartupCli : CordaCliWrapper("corda", "Runs a Corda Node") { private val initialRegistrationCli by lazy { InitialRegistrationCli(startup) } private val validateConfigurationCli by lazy { ValidateConfigurationCli() } - override fun initLogging() = this.initLogging(cmdLineOptions.baseDirectory) + override fun initLogging(): Boolean = this.initLogging(cmdLineOptions.baseDirectory) override fun additionalSubCommands() = setOf(networkCacheCli, justGenerateNodeInfoCli, justGenerateRpcSslCertsCli, initialRegistrationCli, validateConfigurationCli) @@ -437,14 +439,32 @@ interface NodeStartupLogging { } } -fun CliWrapperBase.initLogging(baseDirectory: Path) { +fun CliWrapperBase.initLogging(baseDirectory: Path): Boolean { System.setProperty("defaultLogLevel", specifiedLogLevel) // These properties are referenced from the XML config file. if (verbose) { System.setProperty("consoleLoggingEnabled", "true") System.setProperty("consoleLogLevel", specifiedLogLevel) Node.renderBasicInfoToConsole = false } + + //Test for access to the logging path and shutdown if we are unable to reach it. + val logPath = baseDirectory / NodeCliCommand.LOGS_DIRECTORY_NAME + try { + logPath.createDirectories() + } catch (e: IOException) { + printError("Unable to create logging directory ${logPath.toString()}. Node will now shutdown.") + return false + } catch (e: SecurityException) { + printError("Current user is unable to access logging directory ${logPath.toString()}. Node will now shutdown.") + return false + } + if (!logPath.isDirectory()) { + printError("Unable to access logging directory ${logPath.toString()}. Node will now shutdown.") + return false + } + System.setProperty("log-path", (baseDirectory / NodeCliCommand.LOGS_DIRECTORY_NAME).toString()) SLF4JBridgeHandler.removeHandlersForRootLogger() // The default j.u.l config adds a ConsoleHandler. SLF4JBridgeHandler.install() + return true } diff --git a/node/src/main/kotlin/net/corda/node/internal/subcommands/InitialRegistrationCli.kt b/node/src/main/kotlin/net/corda/node/internal/subcommands/InitialRegistrationCli.kt index 228f6e3400..8cdea61831 100644 --- a/node/src/main/kotlin/net/corda/node/internal/subcommands/InitialRegistrationCli.kt +++ b/node/src/main/kotlin/net/corda/node/internal/subcommands/InitialRegistrationCli.kt @@ -30,7 +30,7 @@ class InitialRegistrationCli(val startup: NodeStartup): CliWrapperBase("initial- return startup.initialiseAndRun(cmdLineOptions, InitialRegistration(cmdLineOptions.baseDirectory, networkRootTrustStorePath, networkRootTrustStorePassword, startup)) } - override fun initLogging() = this.initLogging(cmdLineOptions.baseDirectory) + override fun initLogging(): Boolean = this.initLogging(cmdLineOptions.baseDirectory) @Mixin val cmdLineOptions = InitialRegistrationCmdLineOptions() diff --git a/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt b/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt index fd264e4563..7dca45faca 100644 --- a/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt +++ b/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt @@ -32,7 +32,7 @@ internal class ValidateConfigurationCli : CliWrapperBase("validate-configuration @Mixin private val cmdLineOptions = SharedNodeCmdLineOptions() - override fun initLogging() = initLogging(cmdLineOptions.baseDirectory) + override fun initLogging(): Boolean = initLogging(cmdLineOptions.baseDirectory) override fun runProgram(): Int { val rawConfig = cmdLineOptions.rawConfiguration().doOnErrors(cmdLineOptions::logRawConfigurationErrors).optional ?: return ExitCodes.FAILURE diff --git a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt index 7a61cc3a28..0b219e67a5 100644 --- a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt +++ b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt @@ -1,5 +1,6 @@ package net.corda.cliutils +import net.corda.cliutils.ExitCodes import net.corda.core.internal.rootMessage import net.corda.core.utilities.contextLogger import org.fusesource.jansi.AnsiConsole @@ -123,12 +124,13 @@ abstract class CliWrapperBase(val alias: String, val description: String) : Call // This needs to be called before loggers (See: NodeStartup.kt:51 logger called by lazy, initLogging happens before). // Node's logging is more rich. In corda configurations two properties, defaultLoggingLevel and consoleLogLevel, are usually used. - open fun initLogging() { + open fun initLogging(): Boolean { System.setProperty("defaultLogLevel", specifiedLogLevel) // These properties are referenced from the XML config file. if (verbose) { System.setProperty("consoleLogLevel", specifiedLogLevel) } System.setProperty("log-path", Paths.get(".").toString()) + return true } // Override this function with the actual method to be run once all the arguments have been parsed. The return number @@ -141,7 +143,9 @@ abstract class CliWrapperBase(val alias: String, val description: String) : Call return runProgram() } - val specifiedLogLevel: String by lazy { System.getProperty("log4j2.level")?.toLowerCase(Locale.ENGLISH) ?: loggingLevel.name.toLowerCase(Locale.ENGLISH) } + val specifiedLogLevel: String by lazy { + System.getProperty("log4j2.level")?.toLowerCase(Locale.ENGLISH) ?: loggingLevel.name.toLowerCase(Locale.ENGLISH) + } } /** @@ -178,20 +182,20 @@ abstract class CordaCliWrapper(alias: String, description: String) : CliWrapperB } override fun call(): Int { - initLogging() + if (!initLogging()) { + return ExitCodes.FAILURE + } logger.info("Application Args: ${args.joinToString(" ")}") installShellExtensionsParser.updateShellExtensions() return runProgram() } fun printHelp() = cmd.usage(System.out) - } fun printWarning(message: String) = System.err.println("${ShellConstants.YELLOW}$message${ShellConstants.RESET}") fun printError(message: String) = System.err.println("${ShellConstants.RED}$message${ShellConstants.RESET}") - /** * Useful commonly used constants applicable to many CLI tools */ diff --git a/tools/shell-cli/src/main/kotlin/net/corda/tools/shell/StandaloneShell.kt b/tools/shell-cli/src/main/kotlin/net/corda/tools/shell/StandaloneShell.kt index 8d40c858ea..aa2016aef6 100644 --- a/tools/shell-cli/src/main/kotlin/net/corda/tools/shell/StandaloneShell.kt +++ b/tools/shell-cli/src/main/kotlin/net/corda/tools/shell/StandaloneShell.kt @@ -56,10 +56,11 @@ class StandaloneShell : CordaCliWrapper("corda-shell", "The Corda standalone she private fun getManifestEntry(key: String) = if (Manifests.exists(key)) Manifests.read(key) else "Unknown" - override fun initLogging() { + override fun initLogging() : Boolean { super.initLogging() SLF4JBridgeHandler.removeHandlersForRootLogger() // The default j.u.l config adds a ConsoleHandler. SLF4JBridgeHandler.install() + return true } override fun runProgram(): Int { From e3ada049d42a4173e35820e2eae6a3a7f2e24efd Mon Sep 17 00:00:00 2001 From: Andrius Dagys Date: Fri, 15 Mar 2019 11:14:48 +0100 Subject: [PATCH 094/159] CORDA-2745: Cache notary identity lookups (#4892) Add a cache for notary identities in the PersistentIdentityService. This solves a reported problem where notary identity lookup fails if its network map entry is missing, which results in an exception when trying to insert a state into the vault after recording a transaction. --- .../net/corda/node/internal/AbstractNode.kt | 2 +- .../identity/PersistentIdentityService.kt | 18 +++++++++++++++--- ...AbstractPartyToX500NameAsStringConverter.kt | 6 +++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 8a1bc980c8..0063bb6d31 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -343,7 +343,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, X509Utilities.validateCertPath(trustRoot, identity.certPath) val nodeCa = configuration.signingCertificateStore.get()[CORDA_CLIENT_CA] - identityService.start(trustRoot, listOf(identity.certificate, nodeCa)) + identityService.start(trustRoot, listOf(identity.certificate, nodeCa), netParams.notaries.map { it.identity }) val (keyPairs, nodeInfoAndSigned, myNotaryIdentity) = database.transaction { updateNodeInfo(identity, identityKeyPair, publish = true) diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index 552ccd36dc..2fe6d1deb8 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -101,16 +101,20 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri private lateinit var _trustAnchor: TrustAnchor override val trustAnchor: TrustAnchor get() = _trustAnchor + /** Stores notary identities obtained from the network parameters, for which we don't need to perform a database lookup. */ + private val notaryIdentityCache = HashSet() + // CordaPersistence is not a c'tor parameter to work around the cyclic dependency lateinit var database: CordaPersistence private val keyToParties = createPKMap(cacheFactory) private val principalToParties = createX500Map(cacheFactory) - fun start(trustRoot: X509Certificate, caCertificates: List = emptyList()) { + fun start(trustRoot: X509Certificate, caCertificates: List = emptyList(), notaryIdentities: List = emptyList()) { _trustRoot = trustRoot _trustAnchor = TrustAnchor(trustRoot, null) _caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(caCertificates.toSet() + trustRoot)) + notaryIdentityCache.addAll(notaryIdentities) } fun loadIdentities(identities: Collection = emptySet(), confidentialIdentities: Collection = emptySet()) { @@ -170,7 +174,16 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party - override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? = database.transaction { super.wellKnownPartyFromAnonymous(party) } + override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? { + // Skip database lookup if the party is a notary identity. + // This also prevents an issue where the notary identity can't be resolved if it's not in the network map cache. The node obtains + // a trusted list of notary identities from the network parameters automatically. + return if (party is Party && party in notaryIdentityCache) { + party + } else { + database.transaction { super.wellKnownPartyFromAnonymous(party) } + } + } override fun partiesFromName(query: String, exactMatch: Boolean): Set { return database.transaction { @@ -194,5 +207,4 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri fun stripNotOurKeys(keys: Iterable): Iterable { return keys.filter { certificateFromKey(it)?.name in ourNames } } - } diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt index b476872172..078447854a 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt @@ -8,8 +8,8 @@ import javax.persistence.AttributeConverter import javax.persistence.Converter /** - * Converter to persist a party as its' well known identity (where resolvable). - * Completely anonymous parties are stored as null (to preserve privacy). + * Converter to persist a party as its well known identity (where resolvable). + * Completely anonymous parties are stored as *null* (to preserve privacy). */ @Converter(autoApply = true) class AbstractPartyToX500NameAsStringConverter(private val wellKnownPartyFromX500Name: (CordaX500Name) -> Party?, @@ -24,7 +24,7 @@ class AbstractPartyToX500NameAsStringConverter(private val wellKnownPartyFromX50 if (partyName != null) return partyName log.warn("Identity service unable to resolve AbstractParty: $party") } - return null // non resolvable anonymous parties + return null // non resolvable anonymous parties are stored as nulls } override fun convertToEntityAttribute(dbData: String?): AbstractParty? { From 345a76d1cdc8fd193e010b579c143c4075e8c7ba Mon Sep 17 00:00:00 2001 From: Dominic Fox <40790090+r3domfox@users.noreply.github.com> Date: Fri, 15 Mar 2019 15:16:14 +0000 Subject: [PATCH 095/159] CORDA-2742 treat boxed types as assignable from primitives (#4890) --- .../amqp/EvolutionSerializerFactory.kt | 2 +- .../internal/amqp/EvolvabilityTests.kt | 27 ++++++++++++++++++ .../EvolvabilityTests.evolutionWithPrimitives | Bin 0 -> 767 bytes 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 serialization/src/test/resources/net/corda/serialization/internal/amqp/EvolvabilityTests.evolutionWithPrimitives diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt index 8d017fa057..c8b16a6f5d 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt @@ -76,7 +76,7 @@ class DefaultEvolutionSerializerFactory( val localClass = localProperty.type.observedType.asClass() val remoteClass = remoteProperty.type.typeIdentifier.getLocalType(classLoader).asClass() - if (!localClass.isAssignableFrom(remoteClass)) { + if (!localClass.isAssignableFrom(remoteClass) && remoteClass != localClass.kotlin.javaPrimitiveType) { throw EvolutionSerializationException(this, "Local type $localClass of property $name is not assignable from remote type $remoteClass") } diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt index 288a220281..6ebe8eb8fd 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt @@ -711,4 +711,31 @@ class EvolvabilityTests { assertEquals("dronf", deserialized.fnord) } + + // Container class + data class ParameterizedContainer(val parameterized: Parameterized?) + // Class as it was serialized + // data class Parameterized(val a: A, val b: Set) + + // Marker interface to force evolution + interface ForceEvolution + + // Class after evolution + data class Parameterized(val a: A, val b: Set) : ForceEvolution + + // See CORDA-2742 + @Test + fun evolutionWithPrimitives() { + val resource = "EvolvabilityTests.evolutionWithPrimitives" + val sf = testDefaultFactory() + // Uncomment to recreate + // File(URI("$localPath/$resource")).writeBytes(SerializationOutput(sf).serialize(ParameterizedContainer(Parameterized(10, setOf(20)))).bytes) + + val url = EvolvabilityTests::class.java.getResource(resource) + + val sc2 = url.readBytes() + val deserialized = DeserializationInput(sf).deserialize(SerializedBytes(sc2)) + + assertEquals(10, deserialized.parameterized?.a) + } } diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EvolvabilityTests.evolutionWithPrimitives b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EvolvabilityTests.evolutionWithPrimitives new file mode 100644 index 0000000000000000000000000000000000000000..7d43194b6ba6c933ccc49a07c7cde3d3023a541b GIT binary patch literal 767 zcmb`FPfG$p7{+Jzk5W*FsC!6sh*~WpC{osBGsAQ>#jdkD#l-$=cieDKeU+gvG99|t zx9Br;YBq3D%IM(bWni9np5ODnGw^~Tf&hT0S4&g?;2Qwahd9XKIp<=k4HCsk-pV_E zyg#aTXsy>CAEUydAGb>--h(r`+SRDwBCmLN9u@qU)IHN_JywKnPwFnuDD#ydE*^9) z`|aL^X&v>OwHu?6C}r4PPjRFKr!nEGesfJwRsN~6X3D6AI3UO-F`~qCHR4hnxX9L! z^WbYnU)CwQEe1h~=4Z8E$5N6&XyiNk~(}AIqBayQYFIy+H5<$nW*- literal 0 HcmV?d00001 From abbf2562c2b47595e0baa1be8c41f9ca4053b263 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Sun, 17 Mar 2019 06:06:51 +0000 Subject: [PATCH 096/159] CORDA-2750: Modularise the DJVM build. (#4897) * CORDA-2750: Modularise the DJVM build. * CORDA-2750: Update documentation and scripts for modular DJVM. --- build.gradle | 2 - constants.properties | 2 - djvm/.gitignore | 12 +- djvm/build.gradle | 183 ++++++++++-------- djvm/djvm/build.gradle | 87 +++++++++ djvm/{ => djvm}/cli/build.gradle | 10 +- .../net/corda/djvm/tools/cli/BuildCommand.kt | 0 .../net/corda/djvm/tools/cli/CheckCommand.kt | 0 .../net/corda/djvm/tools/cli/ClassCommand.kt | 0 .../net/corda/djvm/tools/cli/CommandBase.kt | 0 .../net/corda/djvm/tools/cli/Commands.kt | 0 .../corda/djvm/tools/cli/InspectionCommand.kt | 0 .../net/corda/djvm/tools/cli/NewCommand.kt | 0 .../net/corda/djvm/tools/cli/Program.kt | 0 .../net/corda/djvm/tools/cli/RunCommand.kt | 0 .../net/corda/djvm/tools/cli/ShowCommand.kt | 0 .../net/corda/djvm/tools/cli/TreeCommand.kt | 0 .../net/corda/djvm/tools/cli/Utilities.kt | 0 .../corda/djvm/tools/cli/VersionProvider.kt | 0 .../cli/src/main/resources/log4j2.xml | 0 djvm/{ => djvm}/cli/src/shell/djvm | 0 djvm/{ => djvm}/cli/src/shell/djvm.bat | 0 djvm/{ => djvm}/cli/src/shell/install | 0 djvm/{ => djvm}/shell/.gitignore | 0 djvm/{ => djvm}/shell/djvm | 5 +- djvm/{ => djvm}/shell/install | 15 +- .../java/sandbox/java/lang/Appendable.java | 0 .../main/java/sandbox/java/lang/Boolean.java | 0 .../src/main/java/sandbox/java/lang/Byte.java | 0 .../java/sandbox/java/lang/CharSequence.java | 0 .../java/sandbox/java/lang/Character.java | 0 .../java/sandbox/java/lang/Comparable.java | 0 .../java/lang/DJVMThrowableWrapper.java | 0 .../main/java/sandbox/java/lang/Double.java | 0 .../src/main/java/sandbox/java/lang/Enum.java | 0 .../main/java/sandbox/java/lang/Float.java | 0 .../main/java/sandbox/java/lang/Integer.java | 0 .../main/java/sandbox/java/lang/Iterable.java | 0 .../src/main/java/sandbox/java/lang/Long.java | 0 .../main/java/sandbox/java/lang/Number.java | 0 .../main/java/sandbox/java/lang/Object.java | 0 .../main/java/sandbox/java/lang/Runtime.java | 0 .../main/java/sandbox/java/lang/Short.java | 0 .../sandbox/java/lang/StackTraceElement.java | 0 .../main/java/sandbox/java/lang/String.java | 0 .../java/sandbox/java/lang/StringBuffer.java | 0 .../java/sandbox/java/lang/StringBuilder.java | 0 .../main/java/sandbox/java/lang/System.java | 0 .../java/sandbox/java/lang/ThreadLocal.java | 0 .../java/sandbox/java/lang/Throwable.java | 0 .../sandbox/java/nio/charset/Charset.java | 0 .../java/sandbox/java/util/Comparator.java | 0 .../java/sandbox/java/util/LinkedHashMap.java | 0 .../main/java/sandbox/java/util/Locale.java | 0 .../src/main/java/sandbox/java/util/Map.java | 0 .../sandbox/java/util/function/Function.java | 0 .../sandbox/java/util/function/Supplier.java | 0 .../java/sandbox/sun/misc/JavaLangAccess.java | 0 .../java/sandbox/sun/misc/SharedSecrets.java | 0 .../net/corda/djvm/SandboxConfiguration.kt | 0 .../net/corda/djvm/SandboxRuntimeContext.kt | 0 .../djvm/analysis/AnalysisConfiguration.kt | 0 .../corda/djvm/analysis/AnalysisContext.kt | 0 .../djvm/analysis/AnalysisRuntimeContext.kt | 0 .../djvm/analysis/ClassAndMemberVisitor.kt | 0 .../net/corda/djvm/analysis/ClassResolver.kt | 0 .../corda/djvm/analysis/ExceptionResolver.kt | 0 .../net/corda/djvm/analysis/SourceLocation.kt | 0 .../net/corda/djvm/analysis/Whitelist.kt | 0 .../djvm/code/ClassDefinitionProvider.kt | 0 .../net/corda/djvm/code/ClassMutator.kt | 0 .../net/corda/djvm/code/DefinitionProvider.kt | 0 .../kotlin/net/corda/djvm/code/Emitter.kt | 0 .../net/corda/djvm/code/EmitterContext.kt | 0 .../net/corda/djvm/code/EmitterModule.kt | 0 .../kotlin/net/corda/djvm/code/Instruction.kt | 0 .../djvm/code/MemberDefinitionProvider.kt | 0 .../main/kotlin/net/corda/djvm/code/Types.kt | 0 .../code/instructions/BranchInstruction.kt | 0 .../corda/djvm/code/instructions/CodeLabel.kt | 0 .../code/instructions/ConstantInstruction.kt | 0 .../DynamicInvocationInstruction.kt | 0 .../code/instructions/IntegerInstruction.kt | 0 .../instructions/MemberAccessInstruction.kt | 0 .../djvm/code/instructions/MethodEntry.kt | 0 .../instructions/NoOperationInstruction.kt | 0 .../instructions/TableSwitchInstruction.kt | 0 .../corda/djvm/code/instructions/TryBlock.kt | 0 .../djvm/code/instructions/TryCatchBlock.kt | 0 .../djvm/code/instructions/TryFinallyBlock.kt | 0 .../djvm/code/instructions/TypeInstruction.kt | 0 .../net/corda/djvm/costing/RuntimeCost.kt | 0 .../corda/djvm/costing/RuntimeCostSummary.kt | 0 .../corda/djvm/costing/TypedRuntimeCost.kt | 0 .../net/corda/djvm/execution/CostSummary.kt | 0 .../execution/DeterministicSandboxExecutor.kt | 0 .../corda/djvm/execution/ExecutionProfile.kt | 0 .../corda/djvm/execution/ExecutionSummary.kt | 0 .../execution/ExecutionSummaryWithResult.kt | 0 .../net/corda/djvm/execution/IsolatedTask.kt | 0 .../corda/djvm/execution/QueueProcessor.kt | 0 .../corda/djvm/execution/SandboxException.kt | 0 .../corda/djvm/execution/SandboxExecutor.kt | 0 .../corda/djvm/formatting/MemberFormatter.kt | 0 .../kotlin/net/corda/djvm/messages/Message.kt | 0 .../corda/djvm/messages/MessageCollection.kt | 0 .../net/corda/djvm/messages/Severity.kt | 0 .../corda/djvm/references/AnnotationModule.kt | 0 .../corda/djvm/references/ClassHierarchy.kt | 0 .../net/corda/djvm/references/ClassModule.kt | 0 .../corda/djvm/references/ClassReference.kt | 0 .../djvm/references/ClassRepresentation.kt | 0 .../corda/djvm/references/EntityReference.kt | 0 .../djvm/references/EntityWithAccessFlag.kt | 0 .../net/corda/djvm/references/Member.kt | 0 .../djvm/references/MemberInformation.kt | 0 .../net/corda/djvm/references/MemberModule.kt | 0 .../corda/djvm/references/MemberReference.kt | 0 .../net/corda/djvm/references/ReferenceMap.kt | 0 .../djvm/references/ReferenceWithLocation.kt | 0 .../net/corda/djvm/rewiring/ByteCode.kt | 0 .../net/corda/djvm/rewiring/ClassRewriter.kt | 0 .../net/corda/djvm/rewiring/LoadedClass.kt | 0 .../corda/djvm/rewiring/SandboxClassLoader.kt | 0 .../rewiring/SandboxClassLoadingException.kt | 0 .../djvm/rewiring/SandboxClassRemapper.kt | 0 .../corda/djvm/rewiring/SandboxClassWriter.kt | 0 .../corda/djvm/rewiring/SandboxRemapper.kt | 0 .../djvm/rewiring/ThrowableWrapperFactory.kt | 0 .../kotlin/net/corda/djvm/rules/ClassRule.kt | 0 .../net/corda/djvm/rules/InstructionRule.kt | 0 .../kotlin/net/corda/djvm/rules/MemberRule.kt | 0 .../main/kotlin/net/corda/djvm/rules/Rule.kt | 0 .../AlwaysInheritFromSandboxedObject.kt | 0 .../implementation/AlwaysUseExactMath.kt | 0 .../AlwaysUseNonSynchronizedMethods.kt | 0 .../AlwaysUseStrictFloatingPointArithmetic.kt | 0 .../rules/implementation/ArgumentUnwrapper.kt | 0 .../DisallowCatchingBlacklistedExceptions.kt | 0 .../DisallowDynamicInvocation.kt | 0 .../DisallowNonDeterministicMethods.kt | 0 .../DisallowOverriddenSandboxPackage.kt | 0 .../DisallowUnsupportedApiVersions.kt | 0 .../HandleExceptionUnwrapper.kt | 0 .../rules/implementation/IgnoreBreakpoints.kt | 0 .../IgnoreSynchronizedBlocks.kt | 0 .../rules/implementation/ReturnTypeWrapper.kt | 0 .../implementation/RewriteClassMethods.kt | 0 .../implementation/RewriteObjectMethods.kt | 0 .../implementation/StaticConstantRemover.kt | 0 .../implementation/StringConstantWrapper.kt | 0 .../implementation/StubOutFinalizerMethods.kt | 0 .../implementation/StubOutNativeMethods.kt | 0 .../StubOutReflectionMethods.kt | 0 .../implementation/ThrowExceptionWrapper.kt | 0 .../instrumentation/TraceAllocations.kt | 0 .../instrumentation/TraceInvocations.kt | 0 .../instrumentation/TraceJumps.kt | 0 .../instrumentation/TraceThrows.kt | 0 .../net/corda/djvm/source/ClassSource.kt | 0 .../djvm/source/JarInputStreamIterator.kt | 0 .../net/corda/djvm/source/PathClassSource.kt | 0 .../corda/djvm/source/SourceClassLoader.kt | 0 .../net/corda/djvm/utilities/Discovery.kt | 0 .../net/corda/djvm/utilities/Logging.kt | 0 .../net/corda/djvm/utilities/Processor.kt | 0 .../djvm/validation/ConstraintProvider.kt | 0 .../validation/ReferenceValidationSummary.kt | 0 .../net/corda/djvm/validation/RuleContext.kt | 0 .../corda/djvm/validation/RuleValidator.kt | 0 .../src/main/kotlin/sandbox/Task.kt | 0 .../src/main/kotlin/sandbox/java/lang/DJVM.kt | 0 .../kotlin/sandbox/java/lang/DJVMException.kt | 0 .../djvm/costing/RuntimeCostAccounter.kt | 0 .../djvm/costing/ThresholdViolationError.kt | 0 .../corda/djvm/rules/RuleViolationError.kt | 0 .../test/java/net/corda/djvm/WithJava.java | 0 .../djvm/execution/SandboxEnumJavaTest.java | 0 .../execution/SandboxExecutorJavaTest.java | 62 ++++++ .../SandboxObjectHashCodeJavaTest.java | 0 .../execution/SandboxThrowableJavaTest.java | 0 .../src/test/kotlin/foo/bar/sandbox/A.kt | 0 .../src/test/kotlin/foo/bar/sandbox/B.kt | 0 .../src/test/kotlin/foo/bar/sandbox/C.kt | 0 .../test/kotlin/foo/bar/sandbox/Callable.kt | 0 .../src/test/kotlin/foo/bar/sandbox/Empty.kt | 0 .../kotlin/foo/bar/sandbox/KotlinClass.kt | 0 .../test/kotlin/foo/bar/sandbox/MyObject.kt | 0 .../kotlin/foo/bar/sandbox/StrictFloat.kt | 0 .../net/corda/djvm/DJVMExceptionTest.kt | 0 .../test/kotlin/net/corda/djvm/DJVMTest.kt | 0 .../test/kotlin/net/corda/djvm/TestBase.kt | 0 .../test/kotlin/net/corda/djvm/Utilities.kt | 0 .../analysis/ClassAndMemberVisitorTest.kt | 0 .../corda/djvm/analysis/ClassResolverTest.kt | 0 .../corda/djvm/analysis/SourceLocationTest.kt | 0 .../net/corda/djvm/analysis/WhitelistTest.kt | 0 .../djvm/annotations/NonDeterministic.kt | 0 .../djvm/assertions/AssertionExtensions.kt | 5 +- .../assertions/AssertiveClassHierarchy.kt | 8 +- .../AssertiveClassHierarchyWithClass.kt | 14 +- ...sertiveClassHierarchyWithClassAndMember.kt | 6 +- .../assertions/AssertiveClassWithByteCode.kt | 0 .../djvm/assertions/AssertiveDJVMObject.kt | 0 .../djvm/assertions/AssertiveMessages.kt | 14 +- .../djvm/assertions/AssertiveReferenceMap.kt | 18 +- .../AssertiveReferenceMapWithEntity.kt | 4 +- .../assertions/AssertiveRuntimeCostSummary.kt | 14 +- .../net/corda/djvm/code/ClassMutatorTest.kt | 0 .../net/corda/djvm/code/EmitterModuleTest.kt | 0 .../net/corda/djvm/costing/RuntimeCostTest.kt | 0 .../corda/djvm/execution/SandboxEnumTest.kt | 0 .../djvm/execution/SandboxExecutorTest.kt | 5 +- .../djvm/execution/SandboxThrowableTest.kt | 0 .../djvm/formatter/MemberFormatterTest.kt | 0 .../djvm/references/ClassHierarchyTest.kt | 0 .../corda/djvm/references/ClassModuleTest.kt | 0 .../corda/djvm/references/MemberModuleTest.kt | 0 .../corda/djvm/rewiring/ClassRewriterTest.kt | 0 .../djvm/rules/ReferenceExtractorTest.kt | 0 .../net/corda/djvm/rules/RuleValidatorTest.kt | 0 .../djvm/source/SourceClassLoaderTest.kt | 0 .../net/corda/djvm/utilities/DiscoveryTest.kt | 0 .../sandbox/greymalkin/StringReturner.kt | 0 .../test/resources/jar-with-single-class.jar | Bin .../test/resources/jar-with-two-classes.jar | Bin .../src/test/resources/log4j2-test.xml | 0 djvm/gradle.properties | 13 ++ djvm/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 55190 bytes djvm/gradle/wrapper/gradle-wrapper.properties | 5 + djvm/gradlew | 172 ++++++++++++++++ djvm/gradlew.bat | 84 ++++++++ djvm/settings.gradle | 4 + docs/source/key-concepts-djvm.rst | 2 +- settings.gradle | 2 - 235 files changed, 606 insertions(+), 142 deletions(-) create mode 100644 djvm/djvm/build.gradle rename djvm/{ => djvm}/cli/build.gradle (85%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/BuildCommand.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/CheckCommand.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/InspectionCommand.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/Program.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/RunCommand.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/ShowCommand.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/TreeCommand.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt (100%) rename djvm/{ => djvm}/cli/src/main/kotlin/net/corda/djvm/tools/cli/VersionProvider.kt (100%) rename djvm/{ => djvm}/cli/src/main/resources/log4j2.xml (100%) rename djvm/{ => djvm}/cli/src/shell/djvm (100%) rename djvm/{ => djvm}/cli/src/shell/djvm.bat (100%) rename djvm/{ => djvm}/cli/src/shell/install (100%) rename djvm/{ => djvm}/shell/.gitignore (100%) rename djvm/{ => djvm}/shell/djvm (65%) rename djvm/{ => djvm}/shell/install (58%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Appendable.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Boolean.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Byte.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/CharSequence.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Character.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Comparable.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/DJVMThrowableWrapper.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Double.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Enum.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Float.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Integer.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Iterable.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Long.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Number.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Object.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Runtime.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Short.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/StackTraceElement.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/String.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/StringBuffer.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/StringBuilder.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/System.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/ThreadLocal.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/lang/Throwable.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/nio/charset/Charset.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/util/Comparator.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/util/LinkedHashMap.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/util/Locale.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/util/Map.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/util/function/Function.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/java/util/function/Supplier.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/sun/misc/JavaLangAccess.java (100%) rename djvm/{ => djvm}/src/main/java/sandbox/sun/misc/SharedSecrets.java (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/SandboxConfiguration.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/SandboxRuntimeContext.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/analysis/AnalysisConfiguration.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/analysis/AnalysisContext.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/analysis/AnalysisRuntimeContext.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/analysis/ClassAndMemberVisitor.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/analysis/ExceptionResolver.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/analysis/SourceLocation.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/analysis/Whitelist.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/ClassDefinitionProvider.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/ClassMutator.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/DefinitionProvider.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/Emitter.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/EmitterContext.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/EmitterModule.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/Instruction.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/MemberDefinitionProvider.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/Types.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/BranchInstruction.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/CodeLabel.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/ConstantInstruction.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/DynamicInvocationInstruction.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/IntegerInstruction.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/MemberAccessInstruction.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/MethodEntry.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/NoOperationInstruction.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/TableSwitchInstruction.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/TryBlock.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/TryCatchBlock.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/TryFinallyBlock.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/code/instructions/TypeInstruction.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/costing/RuntimeCost.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/costing/RuntimeCostSummary.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/costing/TypedRuntimeCost.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/execution/CostSummary.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/execution/DeterministicSandboxExecutor.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/execution/ExecutionProfile.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/execution/ExecutionSummary.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/execution/ExecutionSummaryWithResult.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/execution/IsolatedTask.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/execution/QueueProcessor.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/execution/SandboxException.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/execution/SandboxExecutor.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/formatting/MemberFormatter.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/messages/Message.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/messages/MessageCollection.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/messages/Severity.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/AnnotationModule.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/ClassHierarchy.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/ClassModule.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/ClassReference.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/ClassRepresentation.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/EntityReference.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/EntityWithAccessFlag.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/Member.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/MemberInformation.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/MemberModule.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/MemberReference.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/ReferenceMap.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/references/ReferenceWithLocation.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rewiring/ByteCode.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rewiring/LoadedClass.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoadingException.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassRemapper.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rewiring/SandboxRemapper.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rewiring/ThrowableWrapperFactory.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/ClassRule.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/InstructionRule.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/MemberRule.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/Rule.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysInheritFromSandboxedObject.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseExactMath.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseNonSynchronizedMethods.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseStrictFloatingPointArithmetic.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/ArgumentUnwrapper.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowCatchingBlacklistedExceptions.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowDynamicInvocation.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowNonDeterministicMethods.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowOverriddenSandboxPackage.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowUnsupportedApiVersions.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/HandleExceptionUnwrapper.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/IgnoreBreakpoints.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/IgnoreSynchronizedBlocks.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/ReturnTypeWrapper.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/RewriteClassMethods.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/RewriteObjectMethods.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/StaticConstantRemover.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/StringConstantWrapper.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutFinalizerMethods.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutNativeMethods.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutReflectionMethods.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/ThrowExceptionWrapper.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceAllocations.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceInvocations.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceJumps.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceThrows.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/source/ClassSource.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/source/JarInputStreamIterator.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/source/PathClassSource.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/utilities/Discovery.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/utilities/Logging.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/utilities/Processor.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/validation/ConstraintProvider.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/validation/ReferenceValidationSummary.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/validation/RuleContext.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/net/corda/djvm/validation/RuleValidator.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/sandbox/Task.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/sandbox/java/lang/DJVM.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/sandbox/java/lang/DJVMException.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/sandbox/net/corda/djvm/costing/RuntimeCostAccounter.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/sandbox/net/corda/djvm/costing/ThresholdViolationError.kt (100%) rename djvm/{ => djvm}/src/main/kotlin/sandbox/net/corda/djvm/rules/RuleViolationError.kt (100%) rename djvm/{ => djvm}/src/test/java/net/corda/djvm/WithJava.java (100%) rename djvm/{ => djvm}/src/test/java/net/corda/djvm/execution/SandboxEnumJavaTest.java (100%) create mode 100644 djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxExecutorJavaTest.java rename djvm/{ => djvm}/src/test/java/net/corda/djvm/execution/SandboxObjectHashCodeJavaTest.java (100%) rename djvm/{ => djvm}/src/test/java/net/corda/djvm/execution/SandboxThrowableJavaTest.java (100%) rename djvm/{ => djvm}/src/test/kotlin/foo/bar/sandbox/A.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/foo/bar/sandbox/B.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/foo/bar/sandbox/C.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/foo/bar/sandbox/Callable.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/foo/bar/sandbox/Empty.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/foo/bar/sandbox/KotlinClass.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/foo/bar/sandbox/MyObject.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/foo/bar/sandbox/StrictFloat.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/DJVMExceptionTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/DJVMTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/TestBase.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/Utilities.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/analysis/ClassAndMemberVisitorTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/analysis/ClassResolverTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/analysis/SourceLocationTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/analysis/WhitelistTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/annotations/NonDeterministic.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/assertions/AssertionExtensions.kt (94%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchy.kt (73%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClass.kt (75%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClassAndMember.kt (83%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassWithByteCode.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/assertions/AssertiveDJVMObject.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/assertions/AssertiveMessages.kt (81%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMap.kt (69%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMapWithEntity.kt (87%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/assertions/AssertiveRuntimeCostSummary.kt (78%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/code/ClassMutatorTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/code/EmitterModuleTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/costing/RuntimeCostTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/execution/SandboxEnumTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/execution/SandboxExecutorTest.kt (99%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/execution/SandboxThrowableTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/formatter/MemberFormatterTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/references/ClassHierarchyTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/references/ClassModuleTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/references/MemberModuleTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/rewiring/ClassRewriterTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/rules/ReferenceExtractorTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/rules/RuleValidatorTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/source/SourceClassLoaderTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/net/corda/djvm/utilities/DiscoveryTest.kt (100%) rename djvm/{ => djvm}/src/test/kotlin/sandbox/greymalkin/StringReturner.kt (100%) rename djvm/{ => djvm}/src/test/resources/jar-with-single-class.jar (100%) rename djvm/{ => djvm}/src/test/resources/jar-with-two-classes.jar (100%) rename djvm/{ => djvm}/src/test/resources/log4j2-test.xml (100%) create mode 100644 djvm/gradle.properties create mode 100644 djvm/gradle/wrapper/gradle-wrapper.jar create mode 100644 djvm/gradle/wrapper/gradle-wrapper.properties create mode 100755 djvm/gradlew create mode 100644 djvm/gradlew.bat create mode 100644 djvm/settings.gradle diff --git a/build.gradle b/build.gradle index 702c54fd17..fee8ea3ebc 100644 --- a/build.gradle +++ b/build.gradle @@ -363,8 +363,6 @@ bintrayConfig { 'corda-core', 'corda-core-deterministic', 'corda-deterministic-verifier', - 'corda-djvm', - 'corda-djvm-cli', 'corda', 'corda-finance-workflows', 'corda-finance-contracts', diff --git a/constants.properties b/constants.properties index 3d170ec880..869bc9cc21 100644 --- a/constants.properties +++ b/constants.properties @@ -2,8 +2,6 @@ # because some versions here need to be matched by app authors in # their own projects. So don't get fancy with syntax! -# It is also parsed by the scripts in djvm/shell/. - cordaVersion=5.0-SNAPSHOT gradlePluginsVersion=4.0.42 kotlinVersion=1.2.71 diff --git a/djvm/.gitignore b/djvm/.gitignore index 9aaddce91f..9c2a46a770 100644 --- a/djvm/.gitignore +++ b/djvm/.gitignore @@ -1,3 +1,13 @@ -tmp/ +# DJVM-specific files +**/tmp/ *.log *.log.gz + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ + +**/out/ + diff --git a/djvm/build.gradle b/djvm/build.gradle index 43775d36ce..d4e33ab268 100644 --- a/djvm/build.gradle +++ b/djvm/build.gradle @@ -1,91 +1,120 @@ +buildscript { + ext { + corda_djvm_version = '5.0-SNAPSHOT' + artifactory_contextUrl = 'https://ci-artifactory.corda.r3cev.com/artifactory' + } + + repositories { + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + plugins { - id 'com.github.johnrengelman.shadow' - id 'net.corda.plugins.publish-utils' - id 'com.jfrog.artifactory' - id 'idea' + id 'net.corda.plugins.publish-utils' version '4.0.42' apply false + id 'com.github.johnrengelman.shadow' version '5.0.0' apply false + id 'com.jfrog.artifactory' version '4.7.3' apply false + id 'com.jfrog.bintray' version '1.4' apply false + id 'com.gradle.build-scan' version '2.2.1' } -description 'Corda deterministic JVM sandbox' +import static org.gradle.api.JavaVersion.* +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -ext { - // Shaded version of ASM to avoid conflict with root project. - asm_version = '6.2.1' -} +subprojects { + group 'net.corda' + version corda_djvm_version -repositories { - maven { - url "$artifactory_contextUrl/corda-dev" + repositories { + mavenCentral() + jcenter() + } + + tasks.withType(JavaCompile) { + sourceCompatibility = VERSION_1_8 + targetCompatibility = VERSION_1_8 + options.encoding = 'UTF-8' + } + + tasks.withType(KotlinCompile) { + kotlinOptions { + languageVersion = '1.2' + apiVersion = '1.2' + jvmTarget = VERSION_1_8 + javaParameters = true // Useful for reflection. + freeCompilerArgs = ['-Xjvm-default=enable'] + } + } + + tasks.withType(Jar) { task -> + manifest { + attributes('Corda-Vendor': 'Corda Open Source') + attributes('Automatic-Module-Name': "net.corda.${task.project.name.replaceAll('-', '.')}") + } + } + + tasks.withType(Test) { + // Prevent the project from creating temporary files outside of the build directory. + systemProperty 'java.io.tmpdir', buildDir.absolutePath } } -configurations { - testImplementation.extendsFrom shadow - jdkRt.resolutionStrategy { - // Always check the repository for a newer SNAPSHOT. - cacheChangingModulesFor 0, 'seconds' +apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'com.jfrog.artifactory' + +bintrayConfig { + user = System.getenv('CORDA_BINTRAY_USER') + key = System.getenv('CORDA_BINTRAY_KEY') + repo = 'corda' + org = 'r3' + licenses = ['Apache-2.0'] + vcsUrl = 'https://github.com/corda/corda' + projectUrl = 'https://github.com/corda/corda' + gpgSign = true + gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') + publications = [ + 'corda-djvm', + 'corda-djvm-cli' + ] + license { + name = 'Apache-2.0' + url = 'https://www.apache.org/licenses/LICENSE-2.0' + distribution = 'repo' + } + developer { + id = 'R3' + name = 'R3' + email = 'dev@corda.net' } } -dependencies { - shadow "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - shadow "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - shadow "org.slf4j:slf4j-api:$slf4j_version" +artifactory { + publish { + contextUrl = artifactory_contextUrl + repository { + repoKey = 'corda-dev' + username = System.getenv('CORDA_ARTIFACTORY_USERNAME') + password = System.getenv('CORDA_ARTIFACTORY_PASSWORD') + } - // ASM: byte code manipulation library - implementation "org.ow2.asm:asm:$asm_version" - implementation "org.ow2.asm:asm-commons:$asm_version" - - // ClassGraph: classpath scanning - shadow "io.github.classgraph:classgraph:$class_graph_version" - - // Test utilities - testImplementation "junit:junit:$junit_version" - testImplementation "org.assertj:assertj-core:$assertj_version" - testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" - jdkRt "net.corda:deterministic-rt:latest.integration" -} - -jar.enabled = false - -shadowJar { - baseName 'corda-djvm' - classifier '' - relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm' - - // These particular classes are only needed to "bootstrap" - // the compilation of the other sandbox classes. At runtime, - // we will generate better versions from deterministic-rt.jar. - exclude 'sandbox/java/lang/Appendable.class' - exclude 'sandbox/java/lang/CharSequence.class' - exclude 'sandbox/java/lang/Character\$Subset.class' - exclude 'sandbox/java/lang/Character\$Unicode*.class' - exclude 'sandbox/java/lang/Comparable.class' - exclude 'sandbox/java/lang/Enum.class' - exclude 'sandbox/java/lang/Iterable.class' - exclude 'sandbox/java/lang/StackTraceElement.class' - exclude 'sandbox/java/lang/StringBuffer.class' - exclude 'sandbox/java/lang/StringBuilder.class' - exclude 'sandbox/java/nio/**' - exclude 'sandbox/java/util/**' -} -assemble.dependsOn shadowJar - -tasks.withType(Test) { - systemProperty 'deterministic-rt.path', configurations.jdkRt.asPath -} - -artifacts { - publish shadowJar -} - -publish { - dependenciesFrom configurations.shadow - name shadowJar.baseName -} - -idea { - module { - downloadJavadoc = true - downloadSources = true + defaults { + // The root project has applied 'publish-utils' but has nothing to publish. + if (project != rootProject) { + publications(project.extensions.publish.name()) + } + } } } + +wrapper { + gradleVersion = "5.2.1" + distributionType = Wrapper.DistributionType.ALL +} + +buildScan { + termsOfServiceUrl = 'https://gradle.com/terms-of-service' + termsOfServiceAgree = 'yes' +} diff --git a/djvm/djvm/build.gradle b/djvm/djvm/build.gradle new file mode 100644 index 0000000000..d5535ba8ee --- /dev/null +++ b/djvm/djvm/build.gradle @@ -0,0 +1,87 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'com.github.johnrengelman.shadow' + id 'net.corda.plugins.publish-utils' + id 'com.jfrog.artifactory' + id 'idea' +} + +description 'Corda deterministic JVM sandbox' + +repositories { + maven { + url "$artifactory_contextUrl/corda-dev" + } +} + +configurations { + testImplementation.extendsFrom shadow + jdkRt.resolutionStrategy { + // Always check the repository for a newer SNAPSHOT. + cacheChangingModulesFor 0, 'seconds' + } +} + +dependencies { + shadow "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + shadow "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + shadow "org.slf4j:slf4j-api:$slf4j_version" + + // ASM: byte code manipulation library + implementation "org.ow2.asm:asm:$asm_version" + implementation "org.ow2.asm:asm-commons:$asm_version" + + // ClassGraph: classpath scanning + shadow "io.github.classgraph:classgraph:$class_graph_version" + + // Test utilities + testImplementation "junit:junit:$junit_version" + testImplementation "org.assertj:assertj-core:$assertj_version" + testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" + jdkRt "net.corda:deterministic-rt:latest.integration" +} + +jar.enabled = false + +shadowJar { + archiveBaseName = 'corda-djvm' + archiveClassifier = '' + relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm' + + // These particular classes are only needed to "bootstrap" + // the compilation of the other sandbox classes. At runtime, + // we will generate better versions from deterministic-rt.jar. + exclude 'sandbox/java/lang/Appendable.class' + exclude 'sandbox/java/lang/CharSequence.class' + exclude 'sandbox/java/lang/Character\$Subset.class' + exclude 'sandbox/java/lang/Character\$Unicode*.class' + exclude 'sandbox/java/lang/Comparable.class' + exclude 'sandbox/java/lang/Enum.class' + exclude 'sandbox/java/lang/Iterable.class' + exclude 'sandbox/java/lang/StackTraceElement.class' + exclude 'sandbox/java/lang/StringBuffer.class' + exclude 'sandbox/java/lang/StringBuilder.class' + exclude 'sandbox/java/nio/**' + exclude 'sandbox/java/util/**' +} +assemble.dependsOn shadowJar + +tasks.withType(Test) { + systemProperty 'deterministic-rt.path', configurations.jdkRt.asPath +} + +artifacts { + publish shadowJar +} + +publish { + dependenciesFrom configurations.shadow + name shadowJar.baseName +} + +idea { + module { + downloadJavadoc = true + downloadSources = true + } +} diff --git a/djvm/cli/build.gradle b/djvm/djvm/cli/build.gradle similarity index 85% rename from djvm/cli/build.gradle rename to djvm/djvm/cli/build.gradle index e38d6d0909..403356ab2d 100644 --- a/djvm/cli/build.gradle +++ b/djvm/djvm/cli/build.gradle @@ -1,4 +1,5 @@ plugins { + id 'org.jetbrains.kotlin.jvm' id 'com.github.johnrengelman.shadow' id 'net.corda.plugins.publish-utils' id 'com.jfrog.artifactory' @@ -14,6 +15,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-reflect" implementation "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" + implementation "org.apache.logging.log4j:log4j-core:$log4j_version" implementation "com.jcabi:jcabi-manifests:$jcabi_manifests_version" implementation "info.picocli:picocli:$picocli_version" @@ -23,8 +25,8 @@ dependencies { jar.enabled = false shadowJar { - baseName = djvmName - classifier = '' + archiveBaseName = djvmName + archiveClassifier = '' manifest { attributes( 'Automatic-Module-Name': 'net.corda.djvm.cli', @@ -36,8 +38,8 @@ shadowJar { } task shadowZip(type: Zip) { - baseName = djvmName - classifier = '' + archiveBaseName = djvmName + archiveClassifier = '' from(shadowJar) { rename "$djvmName-(.*).jar", "${djvmName}.jar" diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/BuildCommand.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/BuildCommand.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/BuildCommand.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/BuildCommand.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CheckCommand.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CheckCommand.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CheckCommand.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CheckCommand.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/CommandBase.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/InspectionCommand.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/InspectionCommand.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/InspectionCommand.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/InspectionCommand.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Program.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Program.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Program.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Program.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/RunCommand.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/RunCommand.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/RunCommand.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/RunCommand.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ShowCommand.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ShowCommand.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ShowCommand.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ShowCommand.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/TreeCommand.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/TreeCommand.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/TreeCommand.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/TreeCommand.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/VersionProvider.kt b/djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/VersionProvider.kt similarity index 100% rename from djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/VersionProvider.kt rename to djvm/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/VersionProvider.kt diff --git a/djvm/cli/src/main/resources/log4j2.xml b/djvm/djvm/cli/src/main/resources/log4j2.xml similarity index 100% rename from djvm/cli/src/main/resources/log4j2.xml rename to djvm/djvm/cli/src/main/resources/log4j2.xml diff --git a/djvm/cli/src/shell/djvm b/djvm/djvm/cli/src/shell/djvm similarity index 100% rename from djvm/cli/src/shell/djvm rename to djvm/djvm/cli/src/shell/djvm diff --git a/djvm/cli/src/shell/djvm.bat b/djvm/djvm/cli/src/shell/djvm.bat similarity index 100% rename from djvm/cli/src/shell/djvm.bat rename to djvm/djvm/cli/src/shell/djvm.bat diff --git a/djvm/cli/src/shell/install b/djvm/djvm/cli/src/shell/install similarity index 100% rename from djvm/cli/src/shell/install rename to djvm/djvm/cli/src/shell/install diff --git a/djvm/shell/.gitignore b/djvm/djvm/shell/.gitignore similarity index 100% rename from djvm/shell/.gitignore rename to djvm/djvm/shell/.gitignore diff --git a/djvm/shell/djvm b/djvm/djvm/shell/djvm similarity index 65% rename from djvm/shell/djvm rename to djvm/djvm/shell/djvm index 645a50b0ba..2b7ff4fe8a 100755 --- a/djvm/shell/djvm +++ b/djvm/djvm/shell/djvm @@ -3,8 +3,7 @@ file="${BASH_SOURCE[0]}" linked_file="$(test -L "$file" && readlink "$file" || echo "$file")" base_dir="$(cd "$(dirname "$linked_file")/../" && pwd)" -version="$(cat $base_dir/../constants.properties | sed -n 's/^[ ]*cordaVersion[ =]*\(.*\).*$/\1/p')" -jar_file="$base_dir/cli/build/libs/corda-djvm-$version-cli.jar" +djvm_cli_jar=$(ls -1 $base_dir/cli/build/libs/corda-djvm-cli-*.jar) CLASSPATH="${CLASSPATH:-}" @@ -17,4 +16,4 @@ if [ "$DEBUG" != 0 ]; then DEBUG_AGENT="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=$DEBUG_PORT" fi -java $DEBUG_AGENT -cp "$CLASSPATH:.:tmp:$jar_file" net.corda.djvm.tools.cli.Program "$@" +java $DEBUG_AGENT -cp "$CLASSPATH:.:tmp:$djvm_cli_jar" net.corda.djvm.tools.cli.Program "$@" diff --git a/djvm/shell/install b/djvm/djvm/shell/install similarity index 58% rename from djvm/shell/install rename to djvm/djvm/shell/install index 5dba7ecd02..1870d34884 100755 --- a/djvm/shell/install +++ b/djvm/djvm/shell/install @@ -2,16 +2,23 @@ file="${BASH_SOURCE[0]}" base_dir="$(cd "$(dirname "$file")/" && pwd)" -version="$(cat $base_dir/../../constants.properties | sed -n 's/^[ ]*cordaVersion[ =]*\(.*\).*$/\1/p')" # Build DJVM module and CLI cd "$base_dir/.." -../gradlew shadowJar +if !(../gradlew shadowJar); then + echo "Failed to build DJVM" + exit 1 +fi + +djvm_cli_jar=$(ls -1 $base_dir/../cli/build/libs/corda-djvm-cli-*.jar) # Generate auto-completion file for Bash and ZSH cd "$base_dir" -java -cp "$base_dir/../cli/build/libs/corda-djvm-$version-cli.jar" \ - picocli.AutoComplete -n djvm net.corda.djvm.tools.cli.Commands -f +if !(java -cp $djvm_cli_jar \ + picocli.AutoComplete -n djvm net.corda.djvm.tools.cli.Commands -f); then + echo "Failed to generate auto-completion file" + exit 1 +fi # Create a symbolic link to the `djvm` utility sudo ln -sf "$base_dir/djvm" /usr/local/bin/djvm diff --git a/djvm/src/main/java/sandbox/java/lang/Appendable.java b/djvm/djvm/src/main/java/sandbox/java/lang/Appendable.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Appendable.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Appendable.java diff --git a/djvm/src/main/java/sandbox/java/lang/Boolean.java b/djvm/djvm/src/main/java/sandbox/java/lang/Boolean.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Boolean.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Boolean.java diff --git a/djvm/src/main/java/sandbox/java/lang/Byte.java b/djvm/djvm/src/main/java/sandbox/java/lang/Byte.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Byte.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Byte.java diff --git a/djvm/src/main/java/sandbox/java/lang/CharSequence.java b/djvm/djvm/src/main/java/sandbox/java/lang/CharSequence.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/CharSequence.java rename to djvm/djvm/src/main/java/sandbox/java/lang/CharSequence.java diff --git a/djvm/src/main/java/sandbox/java/lang/Character.java b/djvm/djvm/src/main/java/sandbox/java/lang/Character.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Character.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Character.java diff --git a/djvm/src/main/java/sandbox/java/lang/Comparable.java b/djvm/djvm/src/main/java/sandbox/java/lang/Comparable.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Comparable.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Comparable.java diff --git a/djvm/src/main/java/sandbox/java/lang/DJVMThrowableWrapper.java b/djvm/djvm/src/main/java/sandbox/java/lang/DJVMThrowableWrapper.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/DJVMThrowableWrapper.java rename to djvm/djvm/src/main/java/sandbox/java/lang/DJVMThrowableWrapper.java diff --git a/djvm/src/main/java/sandbox/java/lang/Double.java b/djvm/djvm/src/main/java/sandbox/java/lang/Double.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Double.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Double.java diff --git a/djvm/src/main/java/sandbox/java/lang/Enum.java b/djvm/djvm/src/main/java/sandbox/java/lang/Enum.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Enum.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Enum.java diff --git a/djvm/src/main/java/sandbox/java/lang/Float.java b/djvm/djvm/src/main/java/sandbox/java/lang/Float.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Float.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Float.java diff --git a/djvm/src/main/java/sandbox/java/lang/Integer.java b/djvm/djvm/src/main/java/sandbox/java/lang/Integer.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Integer.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Integer.java diff --git a/djvm/src/main/java/sandbox/java/lang/Iterable.java b/djvm/djvm/src/main/java/sandbox/java/lang/Iterable.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Iterable.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Iterable.java diff --git a/djvm/src/main/java/sandbox/java/lang/Long.java b/djvm/djvm/src/main/java/sandbox/java/lang/Long.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Long.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Long.java diff --git a/djvm/src/main/java/sandbox/java/lang/Number.java b/djvm/djvm/src/main/java/sandbox/java/lang/Number.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Number.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Number.java diff --git a/djvm/src/main/java/sandbox/java/lang/Object.java b/djvm/djvm/src/main/java/sandbox/java/lang/Object.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Object.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Object.java diff --git a/djvm/src/main/java/sandbox/java/lang/Runtime.java b/djvm/djvm/src/main/java/sandbox/java/lang/Runtime.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Runtime.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Runtime.java diff --git a/djvm/src/main/java/sandbox/java/lang/Short.java b/djvm/djvm/src/main/java/sandbox/java/lang/Short.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Short.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Short.java diff --git a/djvm/src/main/java/sandbox/java/lang/StackTraceElement.java b/djvm/djvm/src/main/java/sandbox/java/lang/StackTraceElement.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/StackTraceElement.java rename to djvm/djvm/src/main/java/sandbox/java/lang/StackTraceElement.java diff --git a/djvm/src/main/java/sandbox/java/lang/String.java b/djvm/djvm/src/main/java/sandbox/java/lang/String.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/String.java rename to djvm/djvm/src/main/java/sandbox/java/lang/String.java diff --git a/djvm/src/main/java/sandbox/java/lang/StringBuffer.java b/djvm/djvm/src/main/java/sandbox/java/lang/StringBuffer.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/StringBuffer.java rename to djvm/djvm/src/main/java/sandbox/java/lang/StringBuffer.java diff --git a/djvm/src/main/java/sandbox/java/lang/StringBuilder.java b/djvm/djvm/src/main/java/sandbox/java/lang/StringBuilder.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/StringBuilder.java rename to djvm/djvm/src/main/java/sandbox/java/lang/StringBuilder.java diff --git a/djvm/src/main/java/sandbox/java/lang/System.java b/djvm/djvm/src/main/java/sandbox/java/lang/System.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/System.java rename to djvm/djvm/src/main/java/sandbox/java/lang/System.java diff --git a/djvm/src/main/java/sandbox/java/lang/ThreadLocal.java b/djvm/djvm/src/main/java/sandbox/java/lang/ThreadLocal.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/ThreadLocal.java rename to djvm/djvm/src/main/java/sandbox/java/lang/ThreadLocal.java diff --git a/djvm/src/main/java/sandbox/java/lang/Throwable.java b/djvm/djvm/src/main/java/sandbox/java/lang/Throwable.java similarity index 100% rename from djvm/src/main/java/sandbox/java/lang/Throwable.java rename to djvm/djvm/src/main/java/sandbox/java/lang/Throwable.java diff --git a/djvm/src/main/java/sandbox/java/nio/charset/Charset.java b/djvm/djvm/src/main/java/sandbox/java/nio/charset/Charset.java similarity index 100% rename from djvm/src/main/java/sandbox/java/nio/charset/Charset.java rename to djvm/djvm/src/main/java/sandbox/java/nio/charset/Charset.java diff --git a/djvm/src/main/java/sandbox/java/util/Comparator.java b/djvm/djvm/src/main/java/sandbox/java/util/Comparator.java similarity index 100% rename from djvm/src/main/java/sandbox/java/util/Comparator.java rename to djvm/djvm/src/main/java/sandbox/java/util/Comparator.java diff --git a/djvm/src/main/java/sandbox/java/util/LinkedHashMap.java b/djvm/djvm/src/main/java/sandbox/java/util/LinkedHashMap.java similarity index 100% rename from djvm/src/main/java/sandbox/java/util/LinkedHashMap.java rename to djvm/djvm/src/main/java/sandbox/java/util/LinkedHashMap.java diff --git a/djvm/src/main/java/sandbox/java/util/Locale.java b/djvm/djvm/src/main/java/sandbox/java/util/Locale.java similarity index 100% rename from djvm/src/main/java/sandbox/java/util/Locale.java rename to djvm/djvm/src/main/java/sandbox/java/util/Locale.java diff --git a/djvm/src/main/java/sandbox/java/util/Map.java b/djvm/djvm/src/main/java/sandbox/java/util/Map.java similarity index 100% rename from djvm/src/main/java/sandbox/java/util/Map.java rename to djvm/djvm/src/main/java/sandbox/java/util/Map.java diff --git a/djvm/src/main/java/sandbox/java/util/function/Function.java b/djvm/djvm/src/main/java/sandbox/java/util/function/Function.java similarity index 100% rename from djvm/src/main/java/sandbox/java/util/function/Function.java rename to djvm/djvm/src/main/java/sandbox/java/util/function/Function.java diff --git a/djvm/src/main/java/sandbox/java/util/function/Supplier.java b/djvm/djvm/src/main/java/sandbox/java/util/function/Supplier.java similarity index 100% rename from djvm/src/main/java/sandbox/java/util/function/Supplier.java rename to djvm/djvm/src/main/java/sandbox/java/util/function/Supplier.java diff --git a/djvm/src/main/java/sandbox/sun/misc/JavaLangAccess.java b/djvm/djvm/src/main/java/sandbox/sun/misc/JavaLangAccess.java similarity index 100% rename from djvm/src/main/java/sandbox/sun/misc/JavaLangAccess.java rename to djvm/djvm/src/main/java/sandbox/sun/misc/JavaLangAccess.java diff --git a/djvm/src/main/java/sandbox/sun/misc/SharedSecrets.java b/djvm/djvm/src/main/java/sandbox/sun/misc/SharedSecrets.java similarity index 100% rename from djvm/src/main/java/sandbox/sun/misc/SharedSecrets.java rename to djvm/djvm/src/main/java/sandbox/sun/misc/SharedSecrets.java diff --git a/djvm/src/main/kotlin/net/corda/djvm/SandboxConfiguration.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/SandboxConfiguration.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/SandboxConfiguration.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/SandboxConfiguration.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/SandboxRuntimeContext.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/SandboxRuntimeContext.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/SandboxRuntimeContext.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/SandboxRuntimeContext.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisConfiguration.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisConfiguration.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisConfiguration.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisConfiguration.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisContext.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisContext.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisContext.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisContext.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisRuntimeContext.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisRuntimeContext.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisRuntimeContext.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisRuntimeContext.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassAndMemberVisitor.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassAndMemberVisitor.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/analysis/ClassAndMemberVisitor.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassAndMemberVisitor.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/analysis/ExceptionResolver.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/ExceptionResolver.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/analysis/ExceptionResolver.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/ExceptionResolver.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/analysis/SourceLocation.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/SourceLocation.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/analysis/SourceLocation.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/SourceLocation.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/analysis/Whitelist.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/Whitelist.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/analysis/Whitelist.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/Whitelist.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/ClassDefinitionProvider.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/ClassDefinitionProvider.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/ClassDefinitionProvider.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/ClassDefinitionProvider.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/ClassMutator.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/ClassMutator.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/ClassMutator.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/ClassMutator.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/DefinitionProvider.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/DefinitionProvider.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/DefinitionProvider.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/DefinitionProvider.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/Emitter.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/Emitter.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/Emitter.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/Emitter.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/EmitterContext.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/EmitterContext.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/EmitterContext.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/EmitterContext.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/EmitterModule.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/EmitterModule.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/EmitterModule.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/EmitterModule.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/Instruction.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/Instruction.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/Instruction.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/Instruction.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/MemberDefinitionProvider.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/MemberDefinitionProvider.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/MemberDefinitionProvider.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/MemberDefinitionProvider.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/Types.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/Types.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/Types.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/Types.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/BranchInstruction.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/BranchInstruction.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/BranchInstruction.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/BranchInstruction.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/CodeLabel.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/CodeLabel.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/CodeLabel.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/CodeLabel.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/ConstantInstruction.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/ConstantInstruction.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/ConstantInstruction.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/ConstantInstruction.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/DynamicInvocationInstruction.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/DynamicInvocationInstruction.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/DynamicInvocationInstruction.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/DynamicInvocationInstruction.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/IntegerInstruction.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/IntegerInstruction.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/IntegerInstruction.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/IntegerInstruction.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/MemberAccessInstruction.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/MemberAccessInstruction.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/MemberAccessInstruction.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/MemberAccessInstruction.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/MethodEntry.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/MethodEntry.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/MethodEntry.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/MethodEntry.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/NoOperationInstruction.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/NoOperationInstruction.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/NoOperationInstruction.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/NoOperationInstruction.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TableSwitchInstruction.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TableSwitchInstruction.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/TableSwitchInstruction.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TableSwitchInstruction.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryBlock.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryBlock.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryBlock.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryBlock.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryCatchBlock.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryCatchBlock.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryCatchBlock.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryCatchBlock.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryFinallyBlock.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryFinallyBlock.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryFinallyBlock.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TryFinallyBlock.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TypeInstruction.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TypeInstruction.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/code/instructions/TypeInstruction.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/code/instructions/TypeInstruction.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/costing/RuntimeCost.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/costing/RuntimeCost.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/costing/RuntimeCost.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/costing/RuntimeCost.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/costing/RuntimeCostSummary.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/costing/RuntimeCostSummary.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/costing/RuntimeCostSummary.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/costing/RuntimeCostSummary.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/costing/TypedRuntimeCost.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/costing/TypedRuntimeCost.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/costing/TypedRuntimeCost.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/costing/TypedRuntimeCost.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/execution/CostSummary.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/execution/CostSummary.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/execution/CostSummary.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/execution/CostSummary.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/execution/DeterministicSandboxExecutor.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/execution/DeterministicSandboxExecutor.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/execution/DeterministicSandboxExecutor.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/execution/DeterministicSandboxExecutor.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionProfile.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionProfile.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionProfile.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionProfile.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionSummary.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionSummary.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionSummary.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionSummary.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionSummaryWithResult.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionSummaryWithResult.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionSummaryWithResult.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/execution/ExecutionSummaryWithResult.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/execution/IsolatedTask.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/execution/IsolatedTask.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/execution/IsolatedTask.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/execution/IsolatedTask.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/execution/QueueProcessor.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/execution/QueueProcessor.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/execution/QueueProcessor.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/execution/QueueProcessor.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/execution/SandboxException.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/execution/SandboxException.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/execution/SandboxException.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/execution/SandboxException.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/execution/SandboxExecutor.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/execution/SandboxExecutor.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/execution/SandboxExecutor.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/execution/SandboxExecutor.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/formatting/MemberFormatter.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/formatting/MemberFormatter.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/formatting/MemberFormatter.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/formatting/MemberFormatter.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/messages/Message.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/messages/Message.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/messages/Message.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/messages/Message.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/messages/MessageCollection.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/messages/MessageCollection.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/messages/MessageCollection.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/messages/MessageCollection.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/messages/Severity.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/messages/Severity.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/messages/Severity.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/messages/Severity.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/AnnotationModule.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/AnnotationModule.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/AnnotationModule.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/AnnotationModule.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/ClassHierarchy.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/ClassHierarchy.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/ClassHierarchy.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/ClassHierarchy.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/ClassModule.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/ClassModule.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/ClassModule.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/ClassModule.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/ClassReference.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/ClassReference.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/ClassReference.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/ClassReference.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/ClassRepresentation.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/ClassRepresentation.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/ClassRepresentation.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/ClassRepresentation.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/EntityReference.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/EntityReference.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/EntityReference.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/EntityReference.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/EntityWithAccessFlag.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/EntityWithAccessFlag.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/EntityWithAccessFlag.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/EntityWithAccessFlag.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/Member.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/Member.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/Member.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/Member.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/MemberInformation.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/MemberInformation.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/MemberInformation.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/MemberInformation.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/MemberModule.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/MemberModule.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/MemberModule.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/MemberModule.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/MemberReference.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/MemberReference.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/MemberReference.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/MemberReference.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/ReferenceMap.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/ReferenceMap.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/ReferenceMap.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/ReferenceMap.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/references/ReferenceWithLocation.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/references/ReferenceWithLocation.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/references/ReferenceWithLocation.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/references/ReferenceWithLocation.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/ByteCode.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/ByteCode.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rewiring/ByteCode.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/ByteCode.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/LoadedClass.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/LoadedClass.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rewiring/LoadedClass.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/LoadedClass.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoadingException.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoadingException.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoadingException.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoadingException.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassRemapper.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassRemapper.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassRemapper.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassRemapper.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxRemapper.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxRemapper.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxRemapper.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxRemapper.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/ThrowableWrapperFactory.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/ThrowableWrapperFactory.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rewiring/ThrowableWrapperFactory.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/ThrowableWrapperFactory.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/ClassRule.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/ClassRule.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/ClassRule.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/ClassRule.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/InstructionRule.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/InstructionRule.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/InstructionRule.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/InstructionRule.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/MemberRule.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/MemberRule.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/MemberRule.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/MemberRule.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/Rule.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/Rule.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/Rule.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/Rule.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysInheritFromSandboxedObject.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysInheritFromSandboxedObject.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysInheritFromSandboxedObject.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysInheritFromSandboxedObject.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseExactMath.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseExactMath.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseExactMath.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseExactMath.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseNonSynchronizedMethods.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseNonSynchronizedMethods.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseNonSynchronizedMethods.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseNonSynchronizedMethods.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseStrictFloatingPointArithmetic.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseStrictFloatingPointArithmetic.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseStrictFloatingPointArithmetic.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/AlwaysUseStrictFloatingPointArithmetic.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ArgumentUnwrapper.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ArgumentUnwrapper.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ArgumentUnwrapper.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ArgumentUnwrapper.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowCatchingBlacklistedExceptions.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowCatchingBlacklistedExceptions.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowCatchingBlacklistedExceptions.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowCatchingBlacklistedExceptions.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowDynamicInvocation.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowDynamicInvocation.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowDynamicInvocation.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowDynamicInvocation.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowNonDeterministicMethods.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowNonDeterministicMethods.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowNonDeterministicMethods.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowNonDeterministicMethods.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowOverriddenSandboxPackage.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowOverriddenSandboxPackage.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowOverriddenSandboxPackage.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowOverriddenSandboxPackage.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowUnsupportedApiVersions.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowUnsupportedApiVersions.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowUnsupportedApiVersions.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/DisallowUnsupportedApiVersions.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/HandleExceptionUnwrapper.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/HandleExceptionUnwrapper.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/HandleExceptionUnwrapper.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/HandleExceptionUnwrapper.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/IgnoreBreakpoints.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/IgnoreBreakpoints.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/IgnoreBreakpoints.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/IgnoreBreakpoints.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/IgnoreSynchronizedBlocks.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/IgnoreSynchronizedBlocks.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/IgnoreSynchronizedBlocks.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/IgnoreSynchronizedBlocks.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ReturnTypeWrapper.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ReturnTypeWrapper.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ReturnTypeWrapper.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ReturnTypeWrapper.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/RewriteClassMethods.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/RewriteClassMethods.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/RewriteClassMethods.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/RewriteClassMethods.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/RewriteObjectMethods.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/RewriteObjectMethods.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/RewriteObjectMethods.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/RewriteObjectMethods.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StaticConstantRemover.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StaticConstantRemover.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StaticConstantRemover.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StaticConstantRemover.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StringConstantWrapper.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StringConstantWrapper.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StringConstantWrapper.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StringConstantWrapper.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutFinalizerMethods.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutFinalizerMethods.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutFinalizerMethods.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutFinalizerMethods.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutNativeMethods.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutNativeMethods.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutNativeMethods.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutNativeMethods.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutReflectionMethods.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutReflectionMethods.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutReflectionMethods.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/StubOutReflectionMethods.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ThrowExceptionWrapper.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ThrowExceptionWrapper.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ThrowExceptionWrapper.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/ThrowExceptionWrapper.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceAllocations.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceAllocations.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceAllocations.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceAllocations.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceInvocations.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceInvocations.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceInvocations.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceInvocations.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceJumps.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceJumps.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceJumps.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceJumps.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceThrows.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceThrows.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceThrows.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/rules/implementation/instrumentation/TraceThrows.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/source/JarInputStreamIterator.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/source/JarInputStreamIterator.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/source/JarInputStreamIterator.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/source/JarInputStreamIterator.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/source/PathClassSource.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/source/PathClassSource.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/source/PathClassSource.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/source/PathClassSource.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/utilities/Discovery.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/utilities/Discovery.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/utilities/Discovery.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/utilities/Discovery.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/utilities/Logging.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/utilities/Logging.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/utilities/Logging.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/utilities/Logging.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/utilities/Processor.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/utilities/Processor.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/utilities/Processor.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/utilities/Processor.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/validation/ConstraintProvider.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/validation/ConstraintProvider.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/validation/ConstraintProvider.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/validation/ConstraintProvider.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/validation/ReferenceValidationSummary.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/validation/ReferenceValidationSummary.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/validation/ReferenceValidationSummary.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/validation/ReferenceValidationSummary.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/validation/RuleContext.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/validation/RuleContext.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/validation/RuleContext.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/validation/RuleContext.kt diff --git a/djvm/src/main/kotlin/net/corda/djvm/validation/RuleValidator.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/validation/RuleValidator.kt similarity index 100% rename from djvm/src/main/kotlin/net/corda/djvm/validation/RuleValidator.kt rename to djvm/djvm/src/main/kotlin/net/corda/djvm/validation/RuleValidator.kt diff --git a/djvm/src/main/kotlin/sandbox/Task.kt b/djvm/djvm/src/main/kotlin/sandbox/Task.kt similarity index 100% rename from djvm/src/main/kotlin/sandbox/Task.kt rename to djvm/djvm/src/main/kotlin/sandbox/Task.kt diff --git a/djvm/src/main/kotlin/sandbox/java/lang/DJVM.kt b/djvm/djvm/src/main/kotlin/sandbox/java/lang/DJVM.kt similarity index 100% rename from djvm/src/main/kotlin/sandbox/java/lang/DJVM.kt rename to djvm/djvm/src/main/kotlin/sandbox/java/lang/DJVM.kt diff --git a/djvm/src/main/kotlin/sandbox/java/lang/DJVMException.kt b/djvm/djvm/src/main/kotlin/sandbox/java/lang/DJVMException.kt similarity index 100% rename from djvm/src/main/kotlin/sandbox/java/lang/DJVMException.kt rename to djvm/djvm/src/main/kotlin/sandbox/java/lang/DJVMException.kt diff --git a/djvm/src/main/kotlin/sandbox/net/corda/djvm/costing/RuntimeCostAccounter.kt b/djvm/djvm/src/main/kotlin/sandbox/net/corda/djvm/costing/RuntimeCostAccounter.kt similarity index 100% rename from djvm/src/main/kotlin/sandbox/net/corda/djvm/costing/RuntimeCostAccounter.kt rename to djvm/djvm/src/main/kotlin/sandbox/net/corda/djvm/costing/RuntimeCostAccounter.kt diff --git a/djvm/src/main/kotlin/sandbox/net/corda/djvm/costing/ThresholdViolationError.kt b/djvm/djvm/src/main/kotlin/sandbox/net/corda/djvm/costing/ThresholdViolationError.kt similarity index 100% rename from djvm/src/main/kotlin/sandbox/net/corda/djvm/costing/ThresholdViolationError.kt rename to djvm/djvm/src/main/kotlin/sandbox/net/corda/djvm/costing/ThresholdViolationError.kt diff --git a/djvm/src/main/kotlin/sandbox/net/corda/djvm/rules/RuleViolationError.kt b/djvm/djvm/src/main/kotlin/sandbox/net/corda/djvm/rules/RuleViolationError.kt similarity index 100% rename from djvm/src/main/kotlin/sandbox/net/corda/djvm/rules/RuleViolationError.kt rename to djvm/djvm/src/main/kotlin/sandbox/net/corda/djvm/rules/RuleViolationError.kt diff --git a/djvm/src/test/java/net/corda/djvm/WithJava.java b/djvm/djvm/src/test/java/net/corda/djvm/WithJava.java similarity index 100% rename from djvm/src/test/java/net/corda/djvm/WithJava.java rename to djvm/djvm/src/test/java/net/corda/djvm/WithJava.java diff --git a/djvm/src/test/java/net/corda/djvm/execution/SandboxEnumJavaTest.java b/djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxEnumJavaTest.java similarity index 100% rename from djvm/src/test/java/net/corda/djvm/execution/SandboxEnumJavaTest.java rename to djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxEnumJavaTest.java diff --git a/djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxExecutorJavaTest.java b/djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxExecutorJavaTest.java new file mode 100644 index 0000000000..78d1dff711 --- /dev/null +++ b/djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxExecutorJavaTest.java @@ -0,0 +1,62 @@ +package net.corda.djvm.execution; + +import net.corda.djvm.TestBase; +import net.corda.djvm.WithJava; +import org.junit.Test; + +import java.util.Set; +import java.util.function.Function; + +import static java.util.Collections.singleton; +import static net.corda.djvm.messages.Severity.WARNING; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +public class SandboxExecutorJavaTest extends TestBase { + private static final int TX_ID = 101; + + @Test + public void testTransaction() { + Set> pinnedClasses = singleton(Transaction.class); + sandbox(new Object[0], pinnedClasses, WARNING, true, ctx -> { + SandboxExecutor contractExecutor = new DeterministicSandboxExecutor<>(ctx.getConfiguration()); + Transaction tx = new Transaction(TX_ID); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> WithJava.run(contractExecutor, ContractWrapper.class, tx)) + .withMessageContaining("Contract constraint violated: txId=" + TX_ID); + return null; + }); + } + + public interface Contract { + @SuppressWarnings("unused") + void verify(Transaction tx); + } + + public static class ContractImplementation implements Contract { + @Override + public void verify(Transaction tx) { + throw new IllegalArgumentException("Contract constraint violated: txId=" + tx.getId()); + } + } + + public static class ContractWrapper implements Function { + @Override + public Void apply(Transaction input) { + new ContractImplementation().verify(input); + return null; + } + } + + @SuppressWarnings("WeakerAccess") + public static class Transaction { + private final int id; + + Transaction(int id) { + this.id = id; + } + + public int getId() { + return id; + } + } +} \ No newline at end of file diff --git a/djvm/src/test/java/net/corda/djvm/execution/SandboxObjectHashCodeJavaTest.java b/djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxObjectHashCodeJavaTest.java similarity index 100% rename from djvm/src/test/java/net/corda/djvm/execution/SandboxObjectHashCodeJavaTest.java rename to djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxObjectHashCodeJavaTest.java diff --git a/djvm/src/test/java/net/corda/djvm/execution/SandboxThrowableJavaTest.java b/djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxThrowableJavaTest.java similarity index 100% rename from djvm/src/test/java/net/corda/djvm/execution/SandboxThrowableJavaTest.java rename to djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxThrowableJavaTest.java diff --git a/djvm/src/test/kotlin/foo/bar/sandbox/A.kt b/djvm/djvm/src/test/kotlin/foo/bar/sandbox/A.kt similarity index 100% rename from djvm/src/test/kotlin/foo/bar/sandbox/A.kt rename to djvm/djvm/src/test/kotlin/foo/bar/sandbox/A.kt diff --git a/djvm/src/test/kotlin/foo/bar/sandbox/B.kt b/djvm/djvm/src/test/kotlin/foo/bar/sandbox/B.kt similarity index 100% rename from djvm/src/test/kotlin/foo/bar/sandbox/B.kt rename to djvm/djvm/src/test/kotlin/foo/bar/sandbox/B.kt diff --git a/djvm/src/test/kotlin/foo/bar/sandbox/C.kt b/djvm/djvm/src/test/kotlin/foo/bar/sandbox/C.kt similarity index 100% rename from djvm/src/test/kotlin/foo/bar/sandbox/C.kt rename to djvm/djvm/src/test/kotlin/foo/bar/sandbox/C.kt diff --git a/djvm/src/test/kotlin/foo/bar/sandbox/Callable.kt b/djvm/djvm/src/test/kotlin/foo/bar/sandbox/Callable.kt similarity index 100% rename from djvm/src/test/kotlin/foo/bar/sandbox/Callable.kt rename to djvm/djvm/src/test/kotlin/foo/bar/sandbox/Callable.kt diff --git a/djvm/src/test/kotlin/foo/bar/sandbox/Empty.kt b/djvm/djvm/src/test/kotlin/foo/bar/sandbox/Empty.kt similarity index 100% rename from djvm/src/test/kotlin/foo/bar/sandbox/Empty.kt rename to djvm/djvm/src/test/kotlin/foo/bar/sandbox/Empty.kt diff --git a/djvm/src/test/kotlin/foo/bar/sandbox/KotlinClass.kt b/djvm/djvm/src/test/kotlin/foo/bar/sandbox/KotlinClass.kt similarity index 100% rename from djvm/src/test/kotlin/foo/bar/sandbox/KotlinClass.kt rename to djvm/djvm/src/test/kotlin/foo/bar/sandbox/KotlinClass.kt diff --git a/djvm/src/test/kotlin/foo/bar/sandbox/MyObject.kt b/djvm/djvm/src/test/kotlin/foo/bar/sandbox/MyObject.kt similarity index 100% rename from djvm/src/test/kotlin/foo/bar/sandbox/MyObject.kt rename to djvm/djvm/src/test/kotlin/foo/bar/sandbox/MyObject.kt diff --git a/djvm/src/test/kotlin/foo/bar/sandbox/StrictFloat.kt b/djvm/djvm/src/test/kotlin/foo/bar/sandbox/StrictFloat.kt similarity index 100% rename from djvm/src/test/kotlin/foo/bar/sandbox/StrictFloat.kt rename to djvm/djvm/src/test/kotlin/foo/bar/sandbox/StrictFloat.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/DJVMExceptionTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/DJVMExceptionTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/DJVMExceptionTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/DJVMExceptionTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/DJVMTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/DJVMTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/DJVMTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/DJVMTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/TestBase.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/TestBase.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/TestBase.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/TestBase.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/Utilities.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/Utilities.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/Utilities.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/Utilities.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/analysis/ClassAndMemberVisitorTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/analysis/ClassAndMemberVisitorTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/analysis/ClassAndMemberVisitorTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/analysis/ClassAndMemberVisitorTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/analysis/ClassResolverTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/analysis/ClassResolverTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/analysis/ClassResolverTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/analysis/ClassResolverTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/analysis/SourceLocationTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/analysis/SourceLocationTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/analysis/SourceLocationTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/analysis/SourceLocationTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/analysis/WhitelistTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/analysis/WhitelistTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/analysis/WhitelistTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/analysis/WhitelistTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/annotations/NonDeterministic.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/annotations/NonDeterministic.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/annotations/NonDeterministic.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/annotations/NonDeterministic.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertionExtensions.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertionExtensions.kt similarity index 94% rename from djvm/src/test/kotlin/net/corda/djvm/assertions/AssertionExtensions.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertionExtensions.kt index 09df337ff3..4b81827f01 100644 --- a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertionExtensions.kt +++ b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertionExtensions.kt @@ -10,8 +10,7 @@ import net.corda.djvm.references.ClassHierarchy import net.corda.djvm.references.Member import net.corda.djvm.references.ReferenceMap import net.corda.djvm.rewiring.LoadedClass -import org.assertj.core.api.Assertions -import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.* import org.assertj.core.api.IterableAssert import org.assertj.core.api.ListAssert import org.assertj.core.api.ThrowableAssertAlternative @@ -67,7 +66,7 @@ object AssertionExtensions { fun ListAssert.withMessage(message: String): ListAssert = this .`as`("HasMessage($message)") .anySatisfy { - Assertions.assertThat(it.message).contains(message) + assertThat(it.message).contains(message) } } diff --git a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchy.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchy.kt similarity index 73% rename from djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchy.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchy.kt index 310016df5d..ff57dbe0ee 100644 --- a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchy.kt +++ b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchy.kt @@ -1,22 +1,22 @@ package net.corda.djvm.assertions import net.corda.djvm.references.ClassHierarchy -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.* open class AssertiveClassHierarchy(protected val hierarchy: ClassHierarchy) { fun hasCount(count: Int): AssertiveClassHierarchy { - Assertions.assertThat(hierarchy.names.size) + assertThat(hierarchy.names.size) .`as`("Number of classes") .isEqualTo(count) return this } fun hasClass(name: String): AssertiveClassHierarchyWithClass { - Assertions.assertThat(hierarchy.names) + assertThat(hierarchy.names) .`as`("Class($name)") .anySatisfy { - Assertions.assertThat(it).isEqualTo(name) + assertThat(it).isEqualTo(name) } return AssertiveClassHierarchyWithClass(hierarchy, name) } diff --git a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClass.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClass.kt similarity index 75% rename from djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClass.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClass.kt index 6450485143..c1c421e0db 100644 --- a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClass.kt +++ b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClass.kt @@ -2,7 +2,7 @@ package net.corda.djvm.assertions import net.corda.djvm.references.ClassRepresentation import net.corda.djvm.references.ClassHierarchy -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.* open class AssertiveClassHierarchyWithClass( hierarchy: ClassHierarchy, @@ -13,30 +13,30 @@ open class AssertiveClassHierarchyWithClass( get() = hierarchy[className]!! fun withInterfaceCount(count: Int): AssertiveClassHierarchyWithClass { - Assertions.assertThat(clazz.interfaces.size) + assertThat(clazz.interfaces.size) .`as`("$clazz.InterfaceCount($count)") .isEqualTo(count) return this } fun withInterface(name: String): AssertiveClassHierarchyWithClass { - Assertions.assertThat(clazz.interfaces).contains(name) + assertThat(clazz.interfaces).contains(name) return this } fun withMemberCount(count: Int): AssertiveClassHierarchyWithClass { - Assertions.assertThat(clazz.members.size) + assertThat(clazz.members.size) .`as`("MemberCount($className)") .isEqualTo(count) return this } fun withMember(name: String, signature: String): AssertiveClassHierarchyWithClassAndMember { - Assertions.assertThat(clazz.members.values) + assertThat(clazz.members.values) .`as`("Member($className.$name:$signature") .anySatisfy { - Assertions.assertThat(it.memberName).isEqualTo(name) - Assertions.assertThat(it.signature).isEqualTo(signature) + assertThat(it.memberName).isEqualTo(name) + assertThat(it.signature).isEqualTo(signature) } val member = clazz.members.values.first { it.memberName == name && it.signature == signature diff --git a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClassAndMember.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClassAndMember.kt similarity index 83% rename from djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClassAndMember.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClassAndMember.kt index 38d6269559..7e4a53231b 100644 --- a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClassAndMember.kt +++ b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassHierarchyWithClassAndMember.kt @@ -2,7 +2,7 @@ package net.corda.djvm.assertions import net.corda.djvm.references.ClassHierarchy import net.corda.djvm.references.Member -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.* @Suppress("unused", "CanBeParameter") class AssertiveClassHierarchyWithClassAndMember( @@ -12,14 +12,14 @@ class AssertiveClassHierarchyWithClassAndMember( ) : AssertiveClassHierarchyWithClass(hierarchy, className) { fun withAccessFlag(flag: Int): AssertiveClassHierarchyWithClassAndMember { - Assertions.assertThat(member.access and flag) + assertThat(member.access and flag) .`as`("$member.AccessFlag($flag)") .isNotEqualTo(0) return this } fun withNoAccessFlag(flag: Int): AssertiveClassHierarchyWithClassAndMember { - Assertions.assertThat(member.access and flag) + assertThat(member.access and flag) .`as`("$member.AccessFlag($flag)") .isEqualTo(0) return this diff --git a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassWithByteCode.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassWithByteCode.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassWithByteCode.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveClassWithByteCode.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveDJVMObject.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveDJVMObject.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveDJVMObject.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveDJVMObject.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveMessages.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveMessages.kt similarity index 81% rename from djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveMessages.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveMessages.kt index 45ad527225..6ace6e3cab 100644 --- a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveMessages.kt +++ b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveMessages.kt @@ -2,7 +2,7 @@ package net.corda.djvm.assertions import net.corda.djvm.messages.MessageCollection import net.corda.djvm.messages.Severity -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.* @Suppress("unused") class AssertiveMessages(private val messages: MessageCollection) { @@ -14,7 +14,7 @@ class AssertiveMessages(private val messages: MessageCollection) { } fun hasErrorCount(count: Int): AssertiveMessages { - Assertions.assertThat(messages.statistics[Severity.ERROR]) + assertThat(messages.statistics[Severity.ERROR]) .`as`("Number of errors") .withFailMessage(formatMessages(Severity.ERROR, count)) .isEqualTo(count) @@ -22,7 +22,7 @@ class AssertiveMessages(private val messages: MessageCollection) { } fun hasWarningCount(count: Int): AssertiveMessages { - Assertions.assertThat(messages.statistics[Severity.WARNING]) + assertThat(messages.statistics[Severity.WARNING]) .`as`("Number of warnings") .withFailMessage(formatMessages(Severity.WARNING, count)) .isEqualTo(count) @@ -30,7 +30,7 @@ class AssertiveMessages(private val messages: MessageCollection) { } fun hasInfoCount(count: Int): AssertiveMessages { - Assertions.assertThat(messages.statistics[Severity.INFORMATIONAL]) + assertThat(messages.statistics[Severity.INFORMATIONAL]) .`as`("Number of informational messages") .withFailMessage(formatMessages(Severity.INFORMATIONAL, count)) .isEqualTo(count) @@ -38,7 +38,7 @@ class AssertiveMessages(private val messages: MessageCollection) { } fun hasTraceCount(count: Int): AssertiveMessages { - Assertions.assertThat(messages.statistics[Severity.TRACE]) + assertThat(messages.statistics[Severity.TRACE]) .`as`("Number of trace messages") .withFailMessage(formatMessages(Severity.TRACE, count)) .isEqualTo(count) @@ -46,10 +46,10 @@ class AssertiveMessages(private val messages: MessageCollection) { } fun withMessage(message: String): AssertiveMessages { - Assertions.assertThat(messages.sorted()) + assertThat(messages.sorted()) .`as`("Has message: $message") .anySatisfy { - Assertions.assertThat(it.message).contains(message) + assertThat(it.message).contains(message) } return this } diff --git a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMap.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMap.kt similarity index 69% rename from djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMap.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMap.kt index 5b349edbe3..512a1ab155 100644 --- a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMap.kt +++ b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMap.kt @@ -3,26 +3,26 @@ package net.corda.djvm.assertions import net.corda.djvm.references.ClassReference import net.corda.djvm.references.MemberReference import net.corda.djvm.references.ReferenceMap -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.* @Suppress("unused") open class AssertiveReferenceMap(private val references: ReferenceMap) { fun hasCount(count: Int): AssertiveReferenceMap { val allReferences = references.joinToString("\n") { " - $it" } - Assertions.assertThat(references.numberOfReferences) + assertThat(references.numberOfReferences) .overridingErrorMessage("Expected $count reference(s), found:\n$allReferences") .isEqualTo(count) return this } fun hasClass(clazz: String): AssertiveReferenceMapWithEntity { - Assertions.assertThat(references.iterator()) + assertThat(references) .`as`("Class($clazz)") .anySatisfy { - Assertions.assertThat(it).isInstanceOf(ClassReference::class.java) + assertThat(it).isInstanceOf(ClassReference::class.java) if (it is ClassReference) { - Assertions.assertThat(it.className).isEqualTo(clazz) + assertThat(it.className).isEqualTo(clazz) } } val reference = ClassReference(clazz) @@ -30,13 +30,13 @@ open class AssertiveReferenceMap(private val references: ReferenceMap) { } fun hasMember(owner: String, member: String, signature: String): AssertiveReferenceMapWithEntity { - Assertions.assertThat(references.iterator()) + assertThat(references) .`as`("Member($owner.$member)") .anySatisfy { - Assertions.assertThat(it).isInstanceOf(MemberReference::class.java) + assertThat(it).isInstanceOf(MemberReference::class.java) if (it is MemberReference) { - Assertions.assertThat(it.className).isEqualTo(owner) - Assertions.assertThat(it.memberName).isEqualTo(member) + assertThat(it.className).isEqualTo(owner) + assertThat(it.memberName).isEqualTo(member) } } val reference = MemberReference(owner, member, signature) diff --git a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMapWithEntity.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMapWithEntity.kt similarity index 87% rename from djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMapWithEntity.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMapWithEntity.kt index ec19591eac..80c797fda3 100644 --- a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMapWithEntity.kt +++ b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveReferenceMapWithEntity.kt @@ -3,7 +3,7 @@ package net.corda.djvm.assertions import net.corda.djvm.analysis.SourceLocation import net.corda.djvm.references.EntityReference import net.corda.djvm.references.ReferenceMap -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.* class AssertiveReferenceMapWithEntity( references: ReferenceMap, @@ -12,7 +12,7 @@ class AssertiveReferenceMapWithEntity( ) : AssertiveReferenceMap(references) { fun withLocationCount(count: Int): AssertiveReferenceMapWithEntity { - Assertions.assertThat(locations.size) + assertThat(locations.size) .`as`("LocationCount($entity)") .isEqualTo(count) return this diff --git a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveRuntimeCostSummary.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveRuntimeCostSummary.kt similarity index 78% rename from djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveRuntimeCostSummary.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveRuntimeCostSummary.kt index 5ac1e33538..a5cc2eca73 100644 --- a/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveRuntimeCostSummary.kt +++ b/djvm/djvm/src/test/kotlin/net/corda/djvm/assertions/AssertiveRuntimeCostSummary.kt @@ -1,7 +1,7 @@ package net.corda.djvm.assertions import net.corda.djvm.costing.RuntimeCostSummary -import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.* @Suppress("MemberVisibilityCanBePrivate") class AssertiveRuntimeCostSummary(private val costs: RuntimeCostSummary) { @@ -14,42 +14,42 @@ class AssertiveRuntimeCostSummary(private val costs: RuntimeCostSummary) { } fun hasAllocationCost(cost: Long): AssertiveRuntimeCostSummary { - Assertions.assertThat(costs.allocationCost.value) + assertThat(costs.allocationCost.value) .`as`("Allocation cost") .isEqualTo(cost) return this } fun hasInvocationCost(cost: Long): AssertiveRuntimeCostSummary { - Assertions.assertThat(costs.invocationCost.value) + assertThat(costs.invocationCost.value) .`as`("Invocation cost") .isEqualTo(cost) return this } fun hasInvocationCostGreaterThanOrEqualTo(cost: Long): AssertiveRuntimeCostSummary { - Assertions.assertThat(costs.invocationCost.value) + assertThat(costs.invocationCost.value) .`as`("Invocation cost") .isGreaterThanOrEqualTo(cost) return this } fun hasJumpCost(cost: Long): AssertiveRuntimeCostSummary { - Assertions.assertThat(costs.jumpCost.value) + assertThat(costs.jumpCost.value) .`as`("Jump cost") .isEqualTo(cost) return this } fun hasJumpCostGreaterThanOrEqualTo(cost: Long): AssertiveRuntimeCostSummary { - Assertions.assertThat(costs.jumpCost.value) + assertThat(costs.jumpCost.value) .`as`("Jump cost") .isGreaterThanOrEqualTo(cost) return this } fun hasThrowCost(cost: Long): AssertiveRuntimeCostSummary { - Assertions.assertThat(costs.throwCost.value) + assertThat(costs.throwCost.value) .`as`("Throw cost") .isEqualTo(cost) return this diff --git a/djvm/src/test/kotlin/net/corda/djvm/code/ClassMutatorTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/code/ClassMutatorTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/code/ClassMutatorTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/code/ClassMutatorTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/code/EmitterModuleTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/code/EmitterModuleTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/code/EmitterModuleTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/code/EmitterModuleTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/costing/RuntimeCostTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/costing/RuntimeCostTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/costing/RuntimeCostTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/costing/RuntimeCostTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxEnumTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxEnumTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/execution/SandboxEnumTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxEnumTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxExecutorTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxExecutorTest.kt similarity index 99% rename from djvm/src/test/kotlin/net/corda/djvm/execution/SandboxExecutorTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxExecutorTest.kt index 0708af248d..892d896970 100644 --- a/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxExecutorTest.kt +++ b/djvm/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxExecutorTest.kt @@ -5,7 +5,6 @@ import foo.bar.sandbox.testClock import foo.bar.sandbox.toNumber import net.corda.djvm.TestBase import net.corda.djvm.analysis.Whitelist.Companion.MINIMAL -import net.corda.djvm.Utilities import net.corda.djvm.Utilities.throwRuleViolationError import net.corda.djvm.Utilities.throwThresholdViolationError import net.corda.djvm.assertions.AssertionExtensions.withProblem @@ -36,9 +35,7 @@ class SandboxExecutorTest : TestBase() { } @Test - fun `can load and execute contract`() = sandbox(DEFAULT, - pinnedClasses = setOf(Transaction::class.java, Utilities::class.java) - ) { + fun `can load and execute contract`() = sandbox(DEFAULT, pinnedClasses = setOf(Transaction::class.java)) { val contractExecutor = DeterministicSandboxExecutor(configuration) val tx = Transaction(1) assertThatExceptionOfType(SandboxException::class.java) diff --git a/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxThrowableTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxThrowableTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/execution/SandboxThrowableTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxThrowableTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/formatter/MemberFormatterTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/formatter/MemberFormatterTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/formatter/MemberFormatterTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/formatter/MemberFormatterTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/references/ClassHierarchyTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/references/ClassHierarchyTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/references/ClassHierarchyTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/references/ClassHierarchyTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/references/ClassModuleTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/references/ClassModuleTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/references/ClassModuleTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/references/ClassModuleTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/references/MemberModuleTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/references/MemberModuleTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/references/MemberModuleTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/references/MemberModuleTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/rewiring/ClassRewriterTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/rewiring/ClassRewriterTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/rewiring/ClassRewriterTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/rewiring/ClassRewriterTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/rules/ReferenceExtractorTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/rules/ReferenceExtractorTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/rules/ReferenceExtractorTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/rules/ReferenceExtractorTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/rules/RuleValidatorTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/rules/RuleValidatorTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/rules/RuleValidatorTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/rules/RuleValidatorTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/source/SourceClassLoaderTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/source/SourceClassLoaderTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/source/SourceClassLoaderTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/source/SourceClassLoaderTest.kt diff --git a/djvm/src/test/kotlin/net/corda/djvm/utilities/DiscoveryTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/utilities/DiscoveryTest.kt similarity index 100% rename from djvm/src/test/kotlin/net/corda/djvm/utilities/DiscoveryTest.kt rename to djvm/djvm/src/test/kotlin/net/corda/djvm/utilities/DiscoveryTest.kt diff --git a/djvm/src/test/kotlin/sandbox/greymalkin/StringReturner.kt b/djvm/djvm/src/test/kotlin/sandbox/greymalkin/StringReturner.kt similarity index 100% rename from djvm/src/test/kotlin/sandbox/greymalkin/StringReturner.kt rename to djvm/djvm/src/test/kotlin/sandbox/greymalkin/StringReturner.kt diff --git a/djvm/src/test/resources/jar-with-single-class.jar b/djvm/djvm/src/test/resources/jar-with-single-class.jar similarity index 100% rename from djvm/src/test/resources/jar-with-single-class.jar rename to djvm/djvm/src/test/resources/jar-with-single-class.jar diff --git a/djvm/src/test/resources/jar-with-two-classes.jar b/djvm/djvm/src/test/resources/jar-with-two-classes.jar similarity index 100% rename from djvm/src/test/resources/jar-with-two-classes.jar rename to djvm/djvm/src/test/resources/jar-with-two-classes.jar diff --git a/djvm/src/test/resources/log4j2-test.xml b/djvm/djvm/src/test/resources/log4j2-test.xml similarity index 100% rename from djvm/src/test/resources/log4j2-test.xml rename to djvm/djvm/src/test/resources/log4j2-test.xml diff --git a/djvm/gradle.properties b/djvm/gradle.properties new file mode 100644 index 0000000000..582007711d --- /dev/null +++ b/djvm/gradle.properties @@ -0,0 +1,13 @@ +kotlin.incremental=true +org.gradle.jvmargs=-XX:+UseG1GC -Xmx1g -Dfile.encoding=UTF-8 + +asm_version=6.2.1 +assertj_version=3.12.1 +class_graph_version=4.6.12 +jcabi_manifests_version=1.1 +jopt_simple_version=5.0.2 +junit_version=4.12 +kotlin_version=1.2.71 +log4j_version=2.11.2 +picocli_version=3.8.0 +slf4j_version=1.7.26 diff --git a/djvm/gradle/wrapper/gradle-wrapper.jar b/djvm/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..87b738cbd051603d91cc39de6cb000dd98fe6b02 GIT binary patch literal 55190 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 z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/djvm/gradlew.bat b/djvm/gradlew.bat new file mode 100644 index 0000000000..0f8d5937c4 --- /dev/null +++ b/djvm/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/djvm/settings.gradle b/djvm/settings.gradle new file mode 100644 index 0000000000..b6f85fa9a0 --- /dev/null +++ b/djvm/settings.gradle @@ -0,0 +1,4 @@ +rootProject.name = "deterministic-jvm-sandbox" + +include 'djvm' +include 'djvm:cli' diff --git a/docs/source/key-concepts-djvm.rst b/docs/source/key-concepts-djvm.rst index 4566c27b9c..b487cfd08b 100644 --- a/docs/source/key-concepts-djvm.rst +++ b/docs/source/key-concepts-djvm.rst @@ -300,7 +300,7 @@ Open your terminal and navigate to the ``djvm`` directory in the Corda source tr :: - $ ./shell/install + $ djvm/shell/install This will build the DJVM tool and install a shortcut on Bash-enabled systems. It will also generate a Bash completion diff --git a/settings.gradle b/settings.gradle index 120b0d866b..a1392c809f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,9 +14,7 @@ include 'client:jackson' include 'client:jfx' include 'client:mock' include 'client:rpc' -include 'djvm' include 'docker' -include 'djvm:cli' include 'webserver' include 'webserver:webcapsule' include 'experimental' From fb4dc0a6ac2292c532de68c9473638881e1ccd00 Mon Sep 17 00:00:00 2001 From: Tommy Lillehagen Date: Mon, 18 Mar 2019 10:29:23 +0000 Subject: [PATCH 097/159] ENT-3060 - Change log level in transition executor (#4898) CORDA-2757 / ENT-3060 - Change log level in flow transition executor --- .../node/services/statemachine/TransitionExecutorImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/TransitionExecutorImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/TransitionExecutorImpl.kt index d781bda14a..1ae80f8f5a 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/TransitionExecutorImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/TransitionExecutorImpl.kt @@ -46,12 +46,12 @@ class TransitionExecutorImpl( // error as that may result in an infinite loop, e.g. error propagation fails -> record error -> propagate fails again. // Instead we just keep around the old error state and wait for a new schedule, perhaps // triggered from a flow hospital - log.error("Error while executing $action during transition to errored state, aborting transition", exception) + log.warn("Error while executing $action during transition to errored state, aborting transition", exception) return Pair(FlowContinuation.Abort, previousState.copy(isFlowResumed = false)) } else { // Otherwise error the state manually keeping the old flow state and schedule a DoRemainingWork // to trigger error propagation - log.error("Error while executing $action, erroring state", exception) + log.info("Error while executing $action, erroring state", exception) val newState = previousState.copy( checkpoint = previousState.checkpoint.copy( errorState = previousState.checkpoint.errorState.addErrors( From 31100cd70852fb4c4b9619de313e38464b44feb4 Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Mon, 18 Mar 2019 11:51:08 +0000 Subject: [PATCH 098/159] CORDA-2748 Always set the ThreadLocal in the Fiber from the Thread, even if not yet set in the Thread. (#4896) --- .../net/corda/node/services/statemachine/FiberUtils.kt | 8 +++++++- .../node/services/statemachine/FlowStateMachineImpl.kt | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FiberUtils.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FiberUtils.kt index ee50b52bd7..5fa6f0e3c4 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FiberUtils.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FiberUtils.kt @@ -3,13 +3,19 @@ package net.corda.node.services.statemachine import co.paralleluniverse.concurrent.util.ThreadAccess import co.paralleluniverse.fibers.Fiber import java.lang.reflect.Field +import java.lang.reflect.Method private val fiberThreadLocalsField: Field = Fiber::class.java.getDeclaredField("fiberLocals").apply { this.isAccessible = true } private fun Fiber.swappedOutThreadLocals(): Any = fiberThreadLocalsField.get(this) +private val threadLocalInitialValueMethod: Method = ThreadLocal::class.java.getDeclaredMethod("initialValue") + .apply { this.isAccessible = true } + +private fun ThreadLocal.initialValue(): T? = threadLocalInitialValueMethod.invoke(this) as T? + // TODO: This method uses a built-in Quasar function to make a map of all ThreadLocals. This is probably inefficient, but the only API readily available. fun Fiber.swappedOutThreadLocalValue(threadLocal: ThreadLocal): T? { val threadLocals = swappedOutThreadLocals() - return ThreadAccess.toMap(threadLocals)[threadLocal] as T? + return (ThreadAccess.toMap(threadLocals)[threadLocal] as T?) ?: threadLocal.initialValue() } diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index 254f530df8..d6b3497820 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -157,7 +157,6 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, val continuation = processEvent(transitionExecutor, nextEvent) when (continuation) { is FlowContinuation.Resume -> { - openThreadLocalWormhole() return continuation.result } is FlowContinuation.Throw -> { @@ -170,6 +169,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, } } finally { checkDbTransaction(isDbTransactionOpenOnExit) + openThreadLocalWormhole() } } @@ -215,7 +215,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, val threadLocal = getTransientField(TransientValues::database).hikariPoolThreadLocal if (threadLocal != null) { val valueFromThread = swappedOutThreadLocalValue(threadLocal) - if (valueFromThread != null) threadLocal.set(valueFromThread) + threadLocal.set(valueFromThread) } } From a90f394d431a9d65d482cfb55c45244a8429650c Mon Sep 17 00:00:00 2001 From: JamesHR3 <45565019+JamesHR3@users.noreply.github.com> Date: Mon, 18 Mar 2019 13:50:29 +0000 Subject: [PATCH 099/159] [CORDA-2738] Allow the ProgressTracker to cope with child trackers with the same steps (#4894) --- .../corda/core/utilities/ProgressTracker.kt | 72 ++++++++----- .../core/utilities/ProgressTrackerTest.kt | 61 +++++++---- .../finance/flows/CashIssueAndPaymentFlow.kt | 13 ++- .../shell/utlities/ANSIProgressRenderer.kt | 102 ++++++++++++------ .../utilities/ANSIProgressRendererTest.kt | 46 ++++++-- 5 files changed, 206 insertions(+), 88 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt index b511a26a27..ffe765d274 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt @@ -50,7 +50,9 @@ class ProgressTracker(vararg inputSteps: Step) { } } - /** The superclass of all step objects. */ + /** + * The superclass of all step objects. + */ @CordaSerializable open class Step(open val label: String) { open val changes: Observable get() = Observable.empty() @@ -84,7 +86,9 @@ class ProgressTracker(vararg inputSteps: Step) { private val childProgressTrackers = mutableMapOf() - /** The steps in this tracker, same as the steps passed to the constructor but with UNSTARTED and DONE inserted. */ + /** + * The steps in this tracker, same as the steps passed to the constructor but with UNSTARTED and DONE inserted. + */ val steps = arrayOf(UNSTARTED, STARTING, *inputSteps, DONE) private var _allStepsCache: List> = _allSteps() @@ -94,6 +98,10 @@ class ProgressTracker(vararg inputSteps: Step) { private val _stepsTreeChanges by transient { ReplaySubject.create>>() } private val _stepsTreeIndexChanges by transient { ReplaySubject.create() } + /** + * Reading returns the value of steps[stepIndex], writing moves the position of the current tracker. Once moved to + * the [DONE] state, this tracker is finished and the current step cannot be moved again. + */ var currentStep: Step get() = steps[stepIndex] set(value) { @@ -134,6 +142,9 @@ class ProgressTracker(vararg inputSteps: Step) { steps.forEach { configureChildTrackerForStep(it) } + // Immediately update the step tree observable to ensure the first update the client receives is the initial state of the progress + // tracker. + _stepsTreeChanges.onNext(allStepsLabels) this.currentStep = UNSTARTED } @@ -144,13 +155,17 @@ class ProgressTracker(vararg inputSteps: Step) { } } - /** The zero-based index of the current step in the [steps] array (i.e. with UNSTARTED and DONE) */ + /** + * The zero-based index of the current step in the [steps] array (i.e. with UNSTARTED and DONE) + */ var stepIndex: Int = 0 private set(value) { field = value } - /** The zero-bases index of the current step in a [allStepsLabels] list */ + /** + * The zero-bases index of the current step in a [allStepsLabels] list + */ var stepsTreeIndex: Int = -1 private set(value) { if (value != field) { @@ -160,26 +175,12 @@ class ProgressTracker(vararg inputSteps: Step) { } /** - * Reading returns the value of steps[stepIndex], writing moves the position of the current tracker. Once moved to - * the [DONE] state, this tracker is finished and the current step cannot be moved again. + * Returns the current step, descending into children to find the deepest step we are up to. */ - - /** Returns the current step, descending into children to find the deepest step we are up to. */ + @Suppress("unused") val currentStepRecursive: Step get() = getChildProgressTracker(currentStep)?.currentStepRecursive ?: currentStep - /** Returns the current step, descending into children to find the deepest started step we are up to. */ - private val currentStartedStepRecursive: Step - get() { - val step = getChildProgressTracker(currentStep)?.currentStartedStepRecursive ?: currentStep - return if (step == UNSTARTED) currentStep else step - } - - private fun currentStepRecursiveWithoutUnstarted(): Step { - val stepRecursive = getChildProgressTracker(currentStep)?.currentStartedStepRecursive - return if (stepRecursive == null || stepRecursive == UNSTARTED) currentStep else stepRecursive - } - fun getChildProgressTracker(step: Step): ProgressTracker? = childProgressTrackers[step]?.tracker fun setChildProgressTracker(step: ProgressTracker.Step, childProgressTracker: ProgressTracker) { @@ -213,12 +214,17 @@ class ProgressTracker(vararg inputSteps: Step) { _stepsTreeChanges.onError(error) } - /** The parent of this tracker: set automatically by the parent when a tracker is added as a child */ + /** + * The parent of this tracker: set automatically by the parent when a tracker is added as a child + */ var parent: ProgressTracker? = null private set - /** Walks up the tree to find the top level tracker. If this is the top level tracker, returns 'this' */ - @Suppress("unused") // TODO: Review by EOY2016 if this property is useful anywhere. + /** + * Walks up the tree to find the top level tracker. If this is the top level tracker, returns 'this'. + * Required for API compatibility. + */ + @Suppress("unused") val topLevelTracker: ProgressTracker get() { var cursor: ProgressTracker = this @@ -233,9 +239,21 @@ class ProgressTracker(vararg inputSteps: Step) { recalculateStepsTreeIndex() } + private fun getStepIndexAtLevel(): Int { + // This gets the index of the current step in the context of this progress tracker, so it will always be at the top level in + // the allStepsCache. + val index = _allStepsCache.indexOf(Pair(0, currentStep)) + return if (index >= 0) index else 0 + } + + private fun getCurrentStepTreeIndex(): Int { + val indexAtLevel = getStepIndexAtLevel() + val additionalIndex = getChildProgressTracker(currentStep)?.getCurrentStepTreeIndex() ?: 0 + return indexAtLevel + additionalIndex + } + private fun recalculateStepsTreeIndex() { - val step = currentStepRecursiveWithoutUnstarted() - stepsTreeIndex = _allStepsCache.indexOfFirst { it.second == step } + stepsTreeIndex = getCurrentStepTreeIndex() } private fun _allSteps(level: Int = 0): List> { @@ -290,7 +308,9 @@ class ProgressTracker(vararg inputSteps: Step) { */ val stepsTreeIndexChanges: Observable get() = _stepsTreeIndexChanges - /** Returns true if the progress tracker has ended, either by reaching the [DONE] step or prematurely with an error */ + /** + * Returns true if the progress tracker has ended, either by reaching the [DONE] step or prematurely with an error + */ val hasEnded: Boolean get() = _changes.hasCompleted() || _changes.hasThrowable() } // TODO: Expose the concept of errors. diff --git a/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt b/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt index 70ccd7fddb..13769fcb0f 100644 --- a/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt +++ b/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt @@ -36,12 +36,14 @@ class ProgressTrackerTest { lateinit var pt: ProgressTracker lateinit var pt2: ProgressTracker lateinit var pt3: ProgressTracker + lateinit var pt4: ProgressTracker @Before fun before() { pt = SimpleSteps.tracker() pt2 = ChildSteps.tracker() pt3 = BabySteps.tracker() + pt4 = ChildSteps.tracker() } @Test @@ -129,8 +131,8 @@ class ProgressTrackerTest { assertCurrentStepsTree(6, SimpleSteps.THREE) // Assert no structure changes and proper steps propagation. - assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 2, 4, 6)) - assertThat(stepsTreeNotification).hasSize(1) // One entry per child progress tracker set + assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(0, 1, 2, 4, 6)) + assertThat(stepsTreeNotification).hasSize(2) // The initial tree state, plus one per tree update } @Test @@ -164,8 +166,8 @@ class ProgressTrackerTest { assertCurrentStepsTree(7, ChildSteps.SEA) // Assert no structure changes and proper steps propagation. - assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 4, 7)) - assertThat(stepsTreeNotification).hasSize(2) // One entry per child progress tracker set + assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(0, 1, 4, 7)) + assertThat(stepsTreeNotification).hasSize(3) // The initial tree state, plus one per update } @Test @@ -201,8 +203,8 @@ class ProgressTrackerTest { assertCurrentStepsTree(10, SimpleSteps.FOUR) // Assert no structure changes and proper steps propagation. - assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(2, 7, 10)) - assertThat(stepsTreeNotification).hasSize(2) // One state per child progress tracker set + assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(0, 2, 7, 10)) + assertThat(stepsTreeNotification).hasSize(3) // The initial tree state, plus one per update. } @Test @@ -236,8 +238,8 @@ class ProgressTrackerTest { assertCurrentStepsTree(3, BabySteps.UNOS) // Assert no structure changes and proper steps propagation. - assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(2, 5, 3)) - assertThat(stepsTreeNotification).hasSize(2) // One state per child progress tracker set + assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(0, 2, 5, 3)) + assertThat(stepsTreeNotification).hasSize(3) // The initial tree state, plus one per update } @Test @@ -256,13 +258,13 @@ class ProgressTrackerTest { pt.currentStep = SimpleSteps.TWO val stepsIndexNotifications = LinkedList() - pt.stepsTreeIndexChanges.subscribe() { + pt.stepsTreeIndexChanges.subscribe { stepsIndexNotifications += it } pt2.currentStep = ChildSteps.AYY - assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 2, 3)) + assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(0, 1, 2, 3)) } @Test @@ -281,20 +283,41 @@ class ProgressTrackerTest { @Test fun `all tree changes seen if subscribed mid flow`() { val stepTreeNotifications = mutableListOf>>() - pt.setChildProgressTracker(SimpleSteps.TWO, pt2) + val firstStepLabels = pt.allStepsLabels - pt.currentStep = SimpleSteps.ONE - pt.currentStep = SimpleSteps.TWO + pt.setChildProgressTracker(SimpleSteps.TWO, pt2) + val secondStepLabels = pt.allStepsLabels pt.setChildProgressTracker(SimpleSteps.TWO, pt3) + val thirdStepLabels = pt.allStepsLabels pt.stepsTreeChanges.subscribe { stepTreeNotifications.add(it)} - fun assertStepsTree(index: Int, step: ProgressTracker.Step) { - assertEquals(step.label, stepTreeNotifications[index][pt.stepsTreeIndex].second) - } + // Should have one notification for original tree, then one for each time it changed. + assertEquals(3, stepTreeNotifications.size) + assertEquals(listOf(firstStepLabels, secondStepLabels, thirdStepLabels), stepTreeNotifications) + } + + @Test + fun `trees with child trackers with duplicate steps reported correctly`() { + val stepTreeNotifications = mutableListOf>>() + val stepIndexNotifications = mutableListOf() + pt.stepsTreeChanges.subscribe { stepTreeNotifications += it } + pt.stepsTreeIndexChanges.subscribe { stepIndexNotifications += it } + pt.setChildProgressTracker(SimpleSteps.ONE, pt2) + pt.setChildProgressTracker(SimpleSteps.TWO, pt4) + + pt.currentStep = SimpleSteps.ONE pt2.currentStep = ChildSteps.AYY - pt3.currentStep = BabySteps.UNOS - assertStepsTree(0, ChildSteps.AYY) - assertStepsTree(1, BabySteps.UNOS) + pt2.nextStep() + pt2.nextStep() + pt.nextStep() + pt4.currentStep = ChildSteps.AYY + + assertEquals(listOf(0, 1, 2, 3, 4, 5, 6), stepIndexNotifications) + } + + @Test + fun `cannot assign step not belonging to this progress tracker`() { + assertFails { pt.currentStep = BabySteps.UNOS } } } diff --git a/finance/workflows/src/main/kotlin/net/corda/finance/flows/CashIssueAndPaymentFlow.kt b/finance/workflows/src/main/kotlin/net/corda/finance/flows/CashIssueAndPaymentFlow.kt index 1370f59fc6..a5d4d5a87c 100644 --- a/finance/workflows/src/main/kotlin/net/corda/finance/flows/CashIssueAndPaymentFlow.kt +++ b/finance/workflows/src/main/kotlin/net/corda/finance/flows/CashIssueAndPaymentFlow.kt @@ -33,13 +33,22 @@ class CashIssueAndPaymentFlow(val amount: Amount, issueRef: OpaqueBytes, recipient: Party, anonymous: Boolean, - notary: Party) : this(amount, issueRef, recipient, anonymous, notary, ProgressTracker()) + notary: Party) : this(amount, issueRef, recipient, anonymous, notary, tracker()) - constructor(request: IssueAndPaymentRequest) : this(request.amount, request.issueRef, request.recipient, request.anonymous, request.notary, ProgressTracker()) + constructor(request: IssueAndPaymentRequest) : this(request.amount, request.issueRef, request.recipient, request.anonymous, request.notary, tracker()) + + companion object { + val ISSUING_CASH = ProgressTracker.Step("Issuing cash") + val PAYING_RECIPIENT = ProgressTracker.Step("Paying recipient") + + fun tracker() = ProgressTracker(ISSUING_CASH, PAYING_RECIPIENT) + } @Suspendable override fun call(): Result { + progressTracker.currentStep = ISSUING_CASH subFlow(CashIssueFlow(amount, issueRef, notary)) + progressTracker.currentStep = PAYING_RECIPIENT return subFlow(CashPaymentFlow(amount, recipient, anonymous, notary)) } diff --git a/tools/shell/src/main/kotlin/net/corda/tools/shell/utlities/ANSIProgressRenderer.kt b/tools/shell/src/main/kotlin/net/corda/tools/shell/utlities/ANSIProgressRenderer.kt index 341454ff77..6332566073 100644 --- a/tools/shell/src/main/kotlin/net/corda/tools/shell/utlities/ANSIProgressRenderer.kt +++ b/tools/shell/src/main/kotlin/net/corda/tools/shell/utlities/ANSIProgressRenderer.kt @@ -16,22 +16,23 @@ import org.fusesource.jansi.Ansi import org.fusesource.jansi.Ansi.Attribute import org.fusesource.jansi.AnsiConsole import org.fusesource.jansi.AnsiOutputStream +import rx.Observable.combineLatest import rx.Subscription +import java.util.* import java.util.stream.IntStream import kotlin.streams.toList abstract class ANSIProgressRenderer { - private var subscriptionIndex: Subscription? = null - private var subscriptionTree: Subscription? = null + private var updatesSubscription: Subscription? = null protected var usingANSI = false protected var checkEmoji = false private val usingUnicode = !SystemUtils.IS_OS_WINDOWS - protected var treeIndex: Int = 0 - protected var treeIndexProcessed: MutableSet = mutableSetOf() - protected var tree: List> = listOf() + private var treeIndex: Int = 0 + private var treeIndexProcessed: MutableSet = mutableSetOf() + protected var tree: List = listOf() private var installedYet = false @@ -42,15 +43,18 @@ abstract class ANSIProgressRenderer { // prevLinesDraw is just for ANSI mode. protected var prevLinesDrawn = 0 + data class ProgressStep(val level: Int, val description: String, val parentIndex: Int?) + data class InputTreeStep(val level: Int, val description: String) + private fun done(error: Throwable?) { - if (error == null) _render(null) + if (error == null) renderInternal(null) draw(true, error) onDone() } fun render(flowProgressHandle: FlowProgressHandle<*>, onDone: () -> Unit = {}) { this.onDone = onDone - _render(flowProgressHandle) + renderInternal(flowProgressHandle) } protected abstract fun printLine(line:String) @@ -59,9 +63,8 @@ abstract class ANSIProgressRenderer { protected abstract fun setup() - private fun _render(flowProgressHandle: FlowProgressHandle<*>?) { - subscriptionIndex?.unsubscribe() - subscriptionTree?.unsubscribe() + private fun renderInternal(flowProgressHandle: FlowProgressHandle<*>?) { + updatesSubscription?.unsubscribe() treeIndex = 0 treeIndexProcessed.clear() tree = listOf() @@ -75,27 +78,64 @@ abstract class ANSIProgressRenderer { prevLinesDrawn = 0 draw(true) + val treeUpdates = flowProgressHandle?.stepsTreeFeed?.updates + val indexUpdates = flowProgressHandle?.stepsTreeIndexFeed?.updates - flowProgressHandle?.apply { - stepsTreeIndexFeed?.apply { - treeIndexProcessed.add(snapshot) - subscriptionIndex = updates.subscribe({ - treeIndex = it - treeIndexProcessed.add(it) + if (treeUpdates == null || indexUpdates == null) { + renderInBold("Cannot print progress for this flow as the required data is missing", Ansi()) + } else { + // By combining the two observables, a race condition where both emit items at roughly the same time is avoided. This could + // result in steps being incorrectly marked as skipped. Instead, whenever either observable emits an item, a pair of the + // last index and last tree is returned, which ensures that updates to either are processed in series. + updatesSubscription = combineLatest(treeUpdates, indexUpdates) { tree, index -> Pair(tree, index) }.subscribe( + { + val newTree = transformTree(it.first.map { elem -> InputTreeStep(elem.first, elem.second) }) + // Process indices first, as if the tree has changed the associated index with this update is for the old tree. Note + // that the one case where this isn't true is the very first update, but in this case the index should be 0 (as this + // update is for the initial state). The remapping on a new tree assumes the step at index 0 is always at least current, + // so this case is handled there. + treeIndex = it.second + treeIndexProcessed.add(it.second) + if (newTree != tree) { + remapIndices(newTree) + tree = newTree + } draw(true) - }, { done(it) }, { done(null) }) - } - stepsTreeFeed?.apply { - subscriptionTree = updates.subscribe({ - remapIndices(it) - tree = it - draw(true) - }, { done(it) }, { done(null) }) - } + }, + { done(it) }, + { done(null) } + ) } } - private fun remapIndices(newTree: List>) { + // Create a new tree of steps that also holds a reference to the parent of each step. This is required to uniquely identify each step + // (assuming that each step label is unique at a given level). + private fun transformTree(inputTree: List): List { + if (inputTree.isEmpty()) { + return listOf() + } + val stack = Stack>() + stack.push(Pair(0, inputTree[0])) + return inputTree.mapIndexed { index, step -> + val parentIndex = try { + val top = stack.peek() + val levelDifference = top.second.level - step.level + if (levelDifference >= 0) { + // The top of the stack is at the same or lower level than the current step. Remove items from the top until the topmost + // item is at a higher level - this is the parent step. + repeat(levelDifference + 1) { stack.pop() } + } + stack.peek().first + } catch (e: EmptyStackException) { + // If there is nothing on the stack at any point, it implies that this step is at the top level and has no parent. + null + } + stack.push(Pair(index, step)) + ProgressStep(step.level, step.description, parentIndex) + } + } + + private fun remapIndices(newTree: List) { val newIndices = newTree.filter { treeIndexProcessed.contains(tree.indexOf(it)) }.map { @@ -108,7 +148,7 @@ abstract class ANSIProgressRenderer { @Synchronized protected fun draw(moveUp: Boolean, error: Throwable? = null) { if (!usingANSI) { - val currentMessage = tree.getOrNull(treeIndex)?.second + val currentMessage = tree.getOrNull(treeIndex)?.description if (currentMessage != null && currentMessage != prevMessagePrinted) { printLine(currentMessage) prevMessagePrinted = currentMessage @@ -182,13 +222,13 @@ abstract class ANSIProgressRenderer { error -> if (usingUnicode) "${Emoji.noEntry} " else "ERROR: " else -> " " // Not reached yet. } - a(" ".repeat(step.first)) + a(" ".repeat(step.level)) a(marker) when { - activeStep -> renderInBold(step.second, ansi) - skippedStep -> renderInFaint(step.second, ansi) - else -> a(step.second) + activeStep -> renderInBold(step.description, ansi) + skippedStep -> renderInFaint(step.description, ansi) + else -> a(step.description) } eraseLine(Ansi.Erase.FORWARD) diff --git a/tools/shell/src/test/kotlin/net/corda/tools/shell/utilities/ANSIProgressRendererTest.kt b/tools/shell/src/test/kotlin/net/corda/tools/shell/utilities/ANSIProgressRendererTest.kt index b20c89168d..08dbe492c2 100644 --- a/tools/shell/src/test/kotlin/net/corda/tools/shell/utilities/ANSIProgressRendererTest.kt +++ b/tools/shell/src/test/kotlin/net/corda/tools/shell/utilities/ANSIProgressRendererTest.kt @@ -40,6 +40,10 @@ class ANSIProgressRendererTest { fun stepActive(stepLabel: String): String { return if (SystemUtils.IS_OS_WINDOWS) """CURRENT: $INTENSITY_BOLD_ON_ASCII$stepLabel$INTENSITY_OFF_ASCII""" else """▶︎ $INTENSITY_BOLD_ON_ASCII$stepLabel$INTENSITY_OFF_ASCII""" } + + fun stepNotRun(stepLabel: String): String { + return """ $stepLabel""" + } } lateinit var printWriter: RenderPrintWriter @@ -59,35 +63,57 @@ class ANSIProgressRendererTest { flowProgressHandle = FlowProgressHandleImpl(StateMachineRunId.createRandom(), openFuture(), Observable.empty(), stepsTreeIndexFeed, stepsTreeFeed) } + private fun checkTrackingState(captor: KArgumentCaptor, updates: Int, trackerState: List) { + verify(printWriter, times(updates)).print(captor.capture()) + assertThat(captor.lastValue.toString()).containsSequence(trackerState) + verify(printWriter, times(updates)).flush() + } + @Test fun `test that steps are rendered appropriately depending on their status`() { progressRenderer.render(flowProgressHandle) feedSubject.onNext(listOf(Pair(0, STEP_1_LABEL), Pair(0, STEP_2_LABEL), Pair(0, STEP_3_LABEL))) // The flow is currently at step 3, while step 1 has been completed and step 2 has been skipped. + indexSubject.onNext(0) indexSubject.onNext(2) val captor = argumentCaptor() - verify(printWriter, times(2)).print(captor.capture()) - assertThat(captor.secondValue.toString()).containsSequence(stepSuccess(STEP_1_LABEL), stepSkipped(STEP_2_LABEL), stepActive(STEP_3_LABEL)) - verify(printWriter, times(2)).flush() + checkTrackingState(captor, 2, listOf(stepSuccess(STEP_1_LABEL), stepSkipped(STEP_2_LABEL), stepActive(STEP_3_LABEL))) } @Test fun `changing tree causes correct steps to be marked as done`() { progressRenderer.render(flowProgressHandle) feedSubject.onNext(listOf(Pair(0, STEP_1_LABEL), Pair(1, STEP_2_LABEL), Pair(1, STEP_3_LABEL), Pair(0, STEP_4_LABEL), Pair(0, STEP_5_LABEL))) + indexSubject.onNext(0) indexSubject.onNext(1) indexSubject.onNext(2) val captor = argumentCaptor() - verify(printWriter, times(3)).print(captor.capture()) - assertThat(captor.lastValue.toString()).containsSequence(stepSuccess(STEP_1_LABEL), stepSuccess(STEP_2_LABEL), stepActive(STEP_3_LABEL)) - verify(printWriter, times(3)).flush() + checkTrackingState(captor, 3, listOf(stepSuccess(STEP_1_LABEL), stepSuccess(STEP_2_LABEL), stepActive(STEP_3_LABEL))) feedSubject.onNext(listOf(Pair(0, STEP_1_LABEL), Pair(0, STEP_4_LABEL), Pair(0, STEP_5_LABEL))) - verify(printWriter, times(4)).print(captor.capture()) - assertThat(captor.lastValue.toString()).containsSequence(stepActive(STEP_1_LABEL)) - assertThat(captor.lastValue.toString()).doesNotContain(stepActive(STEP_5_LABEL)) - verify(printWriter, times(4)).flush() + checkTrackingState(captor, 4, listOf(stepActive(STEP_1_LABEL), stepNotRun(STEP_4_LABEL), stepNotRun(STEP_5_LABEL))) + } + + @Test + fun `duplicate steps in different children handled correctly`() { + val captor = argumentCaptor() + progressRenderer.render(flowProgressHandle) + feedSubject.onNext(listOf(Pair(0, STEP_1_LABEL), Pair(0, STEP_2_LABEL))) + indexSubject.onNext(0) + + checkTrackingState(captor, 1, listOf(stepActive(STEP_1_LABEL), stepNotRun(STEP_2_LABEL))) + + feedSubject.onNext(listOf(Pair(0, STEP_1_LABEL), Pair(1, STEP_3_LABEL), Pair(0, STEP_2_LABEL), Pair(1, STEP_3_LABEL))) + indexSubject.onNext(1) + indexSubject.onNext(2) + indexSubject.onNext(3) + + checkTrackingState(captor, 5, listOf(stepSuccess(STEP_1_LABEL), stepSuccess(STEP_3_LABEL), stepSuccess(STEP_2_LABEL), stepActive(STEP_3_LABEL))) + + feedSubject.onNext(listOf(Pair(0, STEP_1_LABEL), Pair(1, STEP_3_LABEL), Pair(0, STEP_2_LABEL), Pair(1, STEP_3_LABEL), Pair(2, STEP_4_LABEL))) + + checkTrackingState(captor, 6, listOf(stepSuccess(STEP_1_LABEL), stepSuccess(STEP_3_LABEL), stepSuccess(STEP_2_LABEL), stepActive(STEP_3_LABEL), stepNotRun(STEP_4_LABEL))) } } \ No newline at end of file From 1fc8e1d7aeda3241e5719e233d5fba92bda8ef74 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 18 Mar 2019 14:48:13 +0000 Subject: [PATCH 100/159] CORDA-2759: Use GlobalTestPortAllocation for Node's integration tests. (#4899) --- node/build.gradle | 3 +++ .../kotlin/net/corda/testing/driver/Driver.kt | 17 +++++++++++++++-- .../driver/internal/GlobalTestPortAllocation.kt | 4 ++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/node/build.gradle b/node/build.gradle index a536c9f275..bcd228fae2 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -209,6 +209,9 @@ tasks.withType(JavaCompile) { task integrationTest(type: Test) { testClassesDirs = sourceSets.integrationTest.output.classesDirs classpath = sourceSets.integrationTest.runtimeClasspath + + systemProperty 'testing.global.port.allocation.enabled', true + systemProperty 'testing.global.port.allocation.starting.port', 10000 } jar { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 1b98472fec..4704416d0c 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -118,11 +118,24 @@ abstract class PortAllocation { /** * An implementation of [PortAllocation] which allocates ports sequentially */ - open class Incremental(startingPort: Int) : PortAllocation() { + open class Incremental(private val startingPort: Int) : PortAllocation() { + private companion object { + private const val FIRST_EPHEMERAL_PORT = 49152 + } + /** The backing [AtomicInteger] used to keep track of the currently allocated port */ val portCounter = AtomicInteger(startingPort) - override fun nextPort() = portCounter.andIncrement + override fun nextPort(): Int { + return portCounter.getAndUpdate { i -> + val next = i + 1 + if (next >= FIRST_EPHEMERAL_PORT) { + startingPort + } else { + next + } + } + } } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/GlobalTestPortAllocation.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/GlobalTestPortAllocation.kt index b6736bd5f0..f868bce7ef 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/GlobalTestPortAllocation.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/GlobalTestPortAllocation.kt @@ -12,8 +12,8 @@ fun incrementalPortAllocation(startingPortIfNoEnv: Int): PortAllocation { private object GlobalTestPortAllocation : PortAllocation.Incremental(startingPort = startingPort()) -private const val enablingEnvVar = "CORDA_TEST_GLOBAL_PORT_ALLOCATION_ENABLED" -private const val startingPortEnvVariable = "CORDA_TEST_GLOBAL_PORT_ALLOCATION_STARTING_PORT" +private const val enablingEnvVar = "TESTING_GLOBAL_PORT_ALLOCATION_ENABLED" +private const val startingPortEnvVariable = "TESTING_GLOBAL_PORT_ALLOCATION_STARTING_PORT" private val enablingSystemProperty = enablingEnvVar.toLowerCase().replace("_", ".") private val startingPortSystemProperty = startingPortEnvVariable.toLowerCase().replace("_", ".") private const val startingPortDefaultValue = 5000 From dc179d4ea1c13f1fa13bc95dad7337b4dc6589f6 Mon Sep 17 00:00:00 2001 From: Jonathan Locke <36930160+lockathan@users.noreply.github.com> Date: Mon, 18 Mar 2019 17:08:13 +0000 Subject: [PATCH 101/159] ENT-3057: Log hibernate warns and errors in different log (#4889) * ENT-3057: Log hibernate warns and errors in different log If a hibernate error occurs (deadlock, for example) that would cause a flow to be sent to the hospital, hibernate logs the warnings and errors before we do. This results in duplication in the logs, and pollutes the log. To solve this, we create a new log appender named diagnostic-{node-name}.log and log any org.hibernate messages of warn and above to that file. This way, messages are not lost, which means that the information can be retrieved if need be. * Corrected indentation of comment (changed tab to space) * Updated node-administration document to mention diagnostic logging change * Fixed integration test. It was breaking because it was fetching the first log file in the folder, assuming there would be only one. This assumption is now invalid because the diagnostic log file that was introduced. Two tests were found that used similar logic to find a log file to examine, hence both were corrected to look for log files beginning with "node" * Updated documentation as per review comments. --- config/dev/log4j2.xml | 48 +++++++++++++++++++ docs/source/node-administration.rst | 5 +- .../kotlin/net/corda/node/BootTests.kt | 3 +- .../net/corda/testing/driver/DriverTests.kt | 2 +- 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/config/dev/log4j2.xml b/config/dev/log4j2.xml index 6e27ecccb2..c19afbb322 100644 --- a/config/dev/log4j2.xml +++ b/config/dev/log4j2.xml @@ -4,6 +4,7 @@ ${sys:log-path:-logs} node-${hostName} + diagnostic-${hostName} ${log-path}/archive ${sys:defaultLogLevel:-info} ${sys:consoleLogLevel:-error} @@ -105,6 +106,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -119,6 +160,10 @@ + + + + @@ -130,6 +175,9 @@ + + + diff --git a/docs/source/node-administration.rst b/docs/source/node-administration.rst index 1b6e22e0c4..8316702416 100644 --- a/docs/source/node-administration.rst +++ b/docs/source/node-administration.rst @@ -10,7 +10,10 @@ Logging By default the node log files are stored to the ``logs`` subdirectory of the working directory and are rotated from time to time. You can have logging printed to the console as well by passing the ``--log-to-console`` command line flag. The default logging level is ``INFO`` which can be adjusted by the ``--logging-level`` command line argument. This configuration -option will affect all modules. +option will affect all modules. Hibernate (the JPA provider used by Corda) specific log messages of level ``WARN`` and above +will be logged to the diagnostic log file, which is stored in the same location as other log files (``logs`` subdirectory +by default). This is because Hibernate may log messages at WARN and ERROR that are handled internally by Corda and do not +need operator attention. If they do, they will be logged by Corda itself in the main node log file. It may be the case that you require to amend the log level of a particular subset of modules (e.g., if you'd like to take a closer look at hibernate activity). So, for more bespoke logging configuration, the logger settings can be completely overridden diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt index 59725970db..25101f6ad4 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -6,6 +6,7 @@ import net.corda.core.CordaRuntimeException import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC import net.corda.core.internal.div +import net.corda.core.internal.isRegularFile import net.corda.core.internal.list import net.corda.core.internal.readLines import net.corda.core.messaging.startFlow @@ -53,7 +54,7 @@ class BootTests { driver(DriverParameters(notarySpecs = emptyList())) { val alice = startNode(providedName = ALICE_NAME).get() val logFolder = alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME - val logFile = logFolder.list { it.filter { it.fileName.toString().endsWith(".log") }.findAny().get() } + val logFile = logFolder.list { it.filter { a -> a.isRegularFile() && a.fileName.toString().startsWith("node") }.findFirst().get() } // Start second Alice, should fail assertThatThrownBy { startNode(providedName = ALICE_NAME).getOrThrow() diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index 889cecf5c3..eab3ba4c16 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -92,7 +92,7 @@ class DriverTests { systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()) )) { val baseDirectory = startNode(providedName = DUMMY_BANK_A_NAME).getOrThrow().baseDirectory - val logFile = (baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list { it.sorted().findFirst().get() } + val logFile = (baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list { it.filter { a -> a.isRegularFile() && a.fileName.toString().startsWith("node") }.findFirst().get() } val debugLinesPresent = logFile.readLines { lines -> lines.anyMatch { line -> line.startsWith("[DEBUG]") } } assertThat(debugLinesPresent).isTrue() } From 3333464c39278301c912f43c6731161d9432f4ce Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 19 Mar 2019 18:16:24 +0000 Subject: [PATCH 102/159] BUILD: Fix some warnings about @BelongsToContract in tests. (#4902) --- .../kotlin/net/corda/core/internal/TopologicalSortTest.kt | 7 +++---- .../test/kotlin/net/corda/core/node/VaultUpdateTests.kt | 3 ++- .../kotlin/net/corda/testMessage/ScheduledState.kt | 2 ++ .../net/corda/node/services/events/ScheduledFlowTests.kt | 1 + .../node/services/transactions/ResolveStatePointersTest.kt | 2 ++ .../corda/node/services/vault/VaultSoftLockManagerTest.kt | 2 ++ .../kotlin/net/corda/node/testing/MessageChainState.kt | 1 + 7 files changed, 13 insertions(+), 5 deletions(-) diff --git a/core/src/test/kotlin/net/corda/core/internal/TopologicalSortTest.kt b/core/src/test/kotlin/net/corda/core/internal/TopologicalSortTest.kt index 8425bca130..556070f2c6 100644 --- a/core/src/test/kotlin/net/corda/core/internal/TopologicalSortTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/TopologicalSortTest.kt @@ -1,9 +1,7 @@ package net.corda.core.internal import net.corda.client.mock.Generator -import net.corda.core.contracts.ContractState -import net.corda.core.contracts.StateRef -import net.corda.core.contracts.TransactionState +import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SignatureMetadata import net.corda.core.crypto.TransactionSignature @@ -30,11 +28,12 @@ class TopologicalSortTest { override val references: List = emptyList() ) : CoreTransaction() { override val outputs: List> = (1..numberOfOutputs).map { - TransactionState(DummyState(), "", notary) + TransactionState(DummyState(), Contract::class.java.name, notary) } override val networkParametersHash: SecureHash? = testNetworkParameters().serialize().hash } + @BelongsToContract(Contract::class) class DummyState : ContractState { override val participants: List = emptyList() } diff --git a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt index 53c9e1ae6a..d56bdfef57 100644 --- a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt +++ b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt @@ -14,7 +14,7 @@ import kotlin.test.assertFailsWith class VaultUpdateTests { private companion object { - const val DUMMY_PROGRAM_ID = "net.corda.core.node.VaultUpdateTests.DummyContract" + const val DUMMY_PROGRAM_ID = "net.corda.core.node.VaultUpdateTests\$DummyContract" val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party val emptyUpdate = Vault.Update(emptySet(), emptySet(), type = Vault.UpdateType.GENERAL, references = emptySet()) } @@ -25,6 +25,7 @@ class VaultUpdateTests { } } + @BelongsToContract(DummyContract::class) private class DummyState : ContractState { override val participants: List = emptyList() } diff --git a/node/src/integration-test/kotlin/net/corda/testMessage/ScheduledState.kt b/node/src/integration-test/kotlin/net/corda/testMessage/ScheduledState.kt index dfa3390387..688be4fd33 100644 --- a/node/src/integration-test/kotlin/net/corda/testMessage/ScheduledState.kt +++ b/node/src/integration-test/kotlin/net/corda/testMessage/ScheduledState.kt @@ -45,6 +45,7 @@ class ScheduledResponderFlow(private val otherSide: FlowSession) : FlowLogic = listOf(), val bar: Int = 0, @@ -36,6 +37,7 @@ class ResolveStatePointersTest { override val linearId: UniqueIdentifier = UniqueIdentifier() ) : LinearState + @BelongsToContract(DummyContract::class) private data class Foo(val baz: LinearPointer, override val participants: List) : ContractState private val barOne = Bar(listOf(myself.party), 1) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt index 34f447e022..2db01f2c3c 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt @@ -117,10 +117,12 @@ class VaultSoftLockManagerTest { private abstract class ParticipantState(override val participants: List) : ContractState + @BelongsToContract(ContractImpl::class) private class PlainOldState(participants: List) : ParticipantState(participants) { constructor(nodePair: NodePair) : this(listOf(nodePair.client.info.singleIdentity())) } + @BelongsToContract(ContractImpl::class) private class FungibleAssetImpl(participants: List) : ParticipantState(participants), FungibleAsset { constructor(nodePair: NodePair) : this(listOf(nodePair.client.info.singleIdentity())) diff --git a/node/src/test/kotlin/net/corda/node/testing/MessageChainState.kt b/node/src/test/kotlin/net/corda/node/testing/MessageChainState.kt index d34eb8ab20..d8e3f0cea6 100644 --- a/node/src/test/kotlin/net/corda/node/testing/MessageChainState.kt +++ b/node/src/test/kotlin/net/corda/node/testing/MessageChainState.kt @@ -15,6 +15,7 @@ import javax.persistence.Table @CordaSerializable data class MessageData(val value: String) +@BelongsToContract(MessageChainContract::class) data class MessageChainState(val message: MessageData, val by: Party, override val linearId: UniqueIdentifier = UniqueIdentifier(), val extraParty: Party? = null) : LinearState, QueryableState { override val participants: List = if (extraParty == null) listOf(by) else listOf(by, extraParty) From 4b41bd0189c79a0e4d226cce55a0546d9d59a2d6 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Wed, 20 Mar 2019 13:39:51 +0000 Subject: [PATCH 103/159] BUILD: Resolve warnings with Gradle and system property API usage. (#4904) --- build.gradle | 20 ++++++++------------ gradle.properties | 5 +++-- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index fee8ea3ebc..187eb72efb 100644 --- a/build.gradle +++ b/build.gradle @@ -162,9 +162,9 @@ allprojects { suppressionFile = '.ci/dependency-checker/suppressedLibraries.xml' cveValidForHours = 1 format = 'ALL' - failOnError = project.getProperty('owasp.failOnError') + failOnError = project.property('owasp.failOnError') // by default CVSS is '11' which passes everything. Set between 0-10 to catch vulnerable deps - failBuildOnCVSS = project.getProperty('owasp.failBuildOnCVSS').toFloat() + failBuildOnCVSS = project.property('owasp.failBuildOnCVSS').toFloat() analyzers { assemblyEnabled = false @@ -180,7 +180,7 @@ allprojects { options.encoding = 'UTF-8' } - tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) { kotlinOptions { languageVersion = "1.2" apiVersion = "1.2" @@ -208,8 +208,12 @@ allprojects { // Prevent the project from creating temporary files outside of the build directory. systemProperty 'java.io.tmpdir', buildDir.absolutePath + if (project.hasProperty('test.parallel') && project.property('test.parallel').toBoolean()) { + maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) as int ?: 1 + } + if (System.getProperty("test.maxParallelForks") != null) { - maxParallelForks = Integer.valueOf(System.getProperty("test.maxParallelForks")) + maxParallelForks = Integer.getInteger('test.maxParallelForks') logger.debug("System property test.maxParallelForks found - setting max parallel forks to $maxParallelForks for $project") } @@ -273,14 +277,6 @@ allprojects { } } -subprojects { - tasks.withType(Test) { - if (project.getProperty('test.parallel').toBoolean()) { - maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 - } - } -} - // Check that we are running on a Java 8 JDK. The source/targetCompatibility values above aren't sufficient to // guarantee this because those are properties checked by the Java plugin, but we're using Kotlin. // diff --git a/gradle.properties b/gradle.properties index c9fc19fd4f..eec02efd03 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,7 @@ kotlin.incremental=true org.gradle.jvmargs=-XX:+UseG1GC -Xmx1g -Dfile.encoding=UTF-8 -org.gradle.caching=true +org.gradle.caching=false owasp.failOnError=false owasp.failBuildOnCVSS=11.0 -test.parallel=false \ No newline at end of file +compilation.allWarningsAsErrors=false +test.parallel=false From eb7928d761f433f1f4d07e3489a3903fb38b8c8d Mon Sep 17 00:00:00 2001 From: Tommy Lillehagen Date: Wed, 20 Mar 2019 15:30:20 +0000 Subject: [PATCH 104/159] CORDA-2763 - Log censored config at startup (#4906) --- .../node/internal/subcommands/ValidateConfigurationCli.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt b/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt index 7dca45faca..00bbdb1ffc 100644 --- a/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt +++ b/node/src/main/kotlin/net/corda/node/internal/subcommands/ValidateConfigurationCli.kt @@ -26,7 +26,7 @@ internal class ValidateConfigurationCli : CliWrapperBase("validate-configuration return "for path: \"$pathAsString\": $message" } - internal fun logRawConfig(config: Config) = logger.debug("Actual configuration:\n${V1NodeConfigurationSpec.describe(config, Any?::toConfigValue).render(configRenderingOptions)}") + internal fun logRawConfig(config: Config) = logger.info("Actual configuration:\n${V1NodeConfigurationSpec.describe(config, Any?::toConfigValue).render(configRenderingOptions)}") } @Mixin @@ -38,4 +38,4 @@ internal class ValidateConfigurationCli : CliWrapperBase("validate-configuration val rawConfig = cmdLineOptions.rawConfiguration().doOnErrors(cmdLineOptions::logRawConfigurationErrors).optional ?: return ExitCodes.FAILURE return cmdLineOptions.parseConfiguration(rawConfig).doIfValid { logRawConfig(rawConfig) }.doOnErrors(::logConfigurationErrors).optional?.let { ExitCodes.SUCCESS } ?: ExitCodes.FAILURE } -} \ No newline at end of file +} From 52ec48d63defffe160c32397e61def547034e17a Mon Sep 17 00:00:00 2001 From: dazraf Date: Wed, 20 Mar 2019 16:42:37 +0000 Subject: [PATCH 105/159] CORDA-2653 - ensure that during initialisation of a Corda Service, the current thread has a context classloader. (#4907) --- .../net/corda/node/internal/AbstractNode.kt | 38 ++++++++++++------- .../corda/node/internal/CordaServiceTest.kt | 26 +++++++++++-- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 0063bb6d31..f1feeb7f5d 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -575,21 +575,33 @@ abstract class AbstractNode(val configuration: NodeConfiguration, private fun installCordaServices() { val loadedServices = cordappLoader.cordapps.flatMap { it.services } - loadedServices.forEach { - try { - installCordaService(it) - } catch (e: NoSuchMethodException) { - log.error("${it.name}, as a Corda service, must have a constructor with a single parameter of type " + - ServiceHub::class.java.name) - } catch (e: ServiceInstantiationException) { - if (e.cause != null) { - log.error("Corda service ${it.name} failed to instantiate. Reason was: ${e.cause?.rootMessage}", e.cause) - } else { - log.error("Corda service ${it.name} failed to instantiate", e) + + // This sets the Cordapp classloader on the contextClassLoader of the current thread, prior to initializing services + // Needed because of bug CORDA-2653 - some Corda services can utilise third-party libraries that require access to + // the Thread context class loader + + val oldContextClassLoader : ClassLoader? = Thread.currentThread().contextClassLoader + try { + Thread.currentThread().contextClassLoader = cordappLoader.appClassLoader + + loadedServices.forEach { + try { + installCordaService(it) + } catch (e: NoSuchMethodException) { + log.error("${it.name}, as a Corda service, must have a constructor with a single parameter of type " + + ServiceHub::class.java.name) + } catch (e: ServiceInstantiationException) { + if (e.cause != null) { + log.error("Corda service ${it.name} failed to instantiate. Reason was: ${e.cause?.rootMessage}", e.cause) + } else { + log.error("Corda service ${it.name} failed to instantiate", e) + } + } catch (e: Exception) { + log.error("Unable to install Corda service ${it.name}", e) } - } catch (e: Exception) { - log.error("Unable to install Corda service ${it.name}", e) } + } finally { + Thread.currentThread().contextClassLoader = oldContextClassLoader } } diff --git a/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt b/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt index a9c09ca681..d36d1c0256 100644 --- a/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt @@ -26,10 +26,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import java.util.concurrent.atomic.AtomicInteger -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNotEquals -import kotlin.test.assertTrue +import kotlin.test.* class CordaServiceTest { private lateinit var mockNet: MockNetwork @@ -77,6 +74,12 @@ class CordaServiceTest { service.startServiceFlowAndTrack() } + @Test + fun `Corda service can access a non-null thread context classloader`() { + val service = nodeA.services.cordaService(CordaServiceThatRequiresThreadContextClassLoader::class.java) + service.thatWeCanAccessClassLoader() + } + /** * Reproduce CORDA-2296 * Querying the vault from a services constructor failed because the criteriaBuilder @@ -142,4 +145,19 @@ class CordaServiceTest { serviceHub.vaultService.trackBy(ContractState::class.java, criteria) } } + + /** + * See: CORDA-2653 + * This is to check that a corda service is presented with a non-null thread context classloader + */ + @CordaService + class CordaServiceThatRequiresThreadContextClassLoader(val serviceHub: AppServiceHub) : SingletonSerializeAsToken() { + init { + assertNotNull(Thread.currentThread().contextClassLoader, "thread context classloader should not be null during service initialisation") + } + + fun thatWeCanAccessClassLoader() { + assertNotNull(Thread.currentThread().contextClassLoader, "thread context classloader should not be null during service initialisation") + } + } } \ No newline at end of file From 0ab6b4de892e3460c1e51aecf5b284bc4ca26fd2 Mon Sep 17 00:00:00 2001 From: Dominic Fox <40790090+r3domfox@users.noreply.github.com> Date: Wed, 20 Mar 2019 17:33:37 +0000 Subject: [PATCH 106/159] CORDA-2765 Make TransactionVerificationExceptions serializable (#4908) --- .../TransactionVerificationException.kt | 15 ++++-- ...VerificationExceptionSerialisationTests.kt | 52 +++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt index 72567d72e5..760bd4b441 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt @@ -269,7 +269,7 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S */ @CordaSerializable @KeepForDJVM - class OverlappingAttachmentsException(txId: SecureHash, path: String) : TransactionVerificationException(txId, "Multiple attachments define a file at $path.", null) + class OverlappingAttachmentsException(txId: SecureHash, val path: String) : TransactionVerificationException(txId, "Multiple attachments define a file at $path.", null) /** * Thrown to indicate that a contract attachment is not signed by the network-wide package owner. Please note that @@ -277,22 +277,29 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S * and because attachment classloaders are reused this is independent of any particular transaction. */ @CordaSerializable - class PackageOwnershipException(txId: SecureHash, val attachmentHash: AttachmentId, val invalidClassName: String, val packageName: String) : TransactionVerificationException(txId, + class PackageOwnershipException(txId: SecureHash, @Suppress("unused") val attachmentHash: AttachmentId, @Suppress("unused") val invalidClassName: String, val packageName: String) : TransactionVerificationException(txId, """The attachment JAR: $attachmentHash containing the class: $invalidClassName is not signed by the owner of package $packageName specified in the network parameters. Please check the source of this attachment and if it is malicious contact your zone operator to report this incident. For details see: https://docs.corda.net/network-map.html#network-parameters""".trimIndent(), null) @CordaSerializable - class InvalidAttachmentException(txId: SecureHash, attachmentHash: AttachmentId) : TransactionVerificationException(txId, + class InvalidAttachmentException(txId: SecureHash, @Suppress("unused") val attachmentHash: AttachmentId) : TransactionVerificationException(txId, "The attachment $attachmentHash is not a valid ZIP or JAR file.".trimIndent(), null) // TODO: Make this descend from TransactionVerificationException so that untrusted attachments cause flows to be hospitalized. /** Thrown during classloading upon encountering an untrusted attachment (eg. not in the [TRUSTED_UPLOADERS] list) */ @KeepForDJVM @CordaSerializable - class UntrustedAttachmentsException(txId: SecureHash, val ids: List) : + class UntrustedAttachmentsException(val txId: SecureHash, val ids: List) : CordaException("Attempting to load untrusted transaction attachments: $ids. " + "At this time these are not loadable because the DJVM sandbox has not yet been integrated. " + "You will need to install that app version yourself, to whitelist it for use. " + "Please follow the operational steps outlined in https://docs.corda.net/cordapp-build-systems.html#cordapp-contract-attachments to learn more and continue.") + + /* + If you add a new class extending [TransactionVerificationException], please add a test in `TransactionVerificationExceptionSerializationTests` + proving that it can actually be serialised. As a rule, exceptions intended to be serialised _must_ have a corresponding readable property + for every named constructor parameter - so make your constructor parameters `val`s even if nothing other than the serializer is ever + going to read them. + */ } diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt b/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt index 6c10861468..4ece674122 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt @@ -24,6 +24,7 @@ class TransactionVerificationExceptionSerialisationTests { private val context get() = AMQP_RPC_CLIENT_CONTEXT private val txid = SecureHash.allOnesHash + private val attachmentHash = SecureHash.allOnesHash private val factory = defaultFactory() @Test @@ -123,4 +124,55 @@ class TransactionVerificationExceptionSerialisationTests { assertEquals(exception.cause?.message, exception2.cause?.message) assertEquals(exception.txId, exception2.txId) } + + @Test + fun overlappingAttachmentsExceptionTest() { + val exc = TransactionVerificationException.OverlappingAttachmentsException(txid, "foo/bar/baz") + val exc2 = DeserializationInput(factory).deserialize( + SerializationOutput(factory).serialize(exc, context), + context) + + assertEquals(exc.message, exc2.message) + } + + @Test + fun packageOwnershipExceptionTest() { + val exc = TransactionVerificationException.PackageOwnershipException( + txid, + attachmentHash, + "InvalidClass", + "com.invalid") + + val exc2 = DeserializationInput(factory).deserialize( + SerializationOutput(factory).serialize(exc, context), + context) + + assertEquals(exc.message, exc2.message) + } + + @Test + fun invalidAttachmentExceptionTest() { + val exc = TransactionVerificationException.InvalidAttachmentException( + txid, + attachmentHash) + + val exc2 = DeserializationInput(factory).deserialize( + SerializationOutput(factory).serialize(exc, context), + context) + + assertEquals(exc.message, exc2.message) + } + + @Test + fun untrustedAttachmentsExceptionTest() { + val exc = TransactionVerificationException.UntrustedAttachmentsException( + txid, + listOf(attachmentHash)) + + val exc2 = DeserializationInput(factory).deserialize( + SerializationOutput(factory).serialize(exc, context), + context) + + assertEquals(exc.message, exc2.message) + } } \ No newline at end of file From eabd0565117dad85d73b13c6afcd3bad63a13fc4 Mon Sep 17 00:00:00 2001 From: Jonathan Locke <36930160+lockathan@users.noreply.github.com> Date: Thu, 21 Mar 2019 11:15:32 +0000 Subject: [PATCH 107/159] ENT-3333: Return error code for invalid cmdline options (#4910) If an invalid command line option is specified and the parser is unable to parse the input, return an error code to the caller. This is useful in scripting scenarios where it might be important to detect that Corda failed to start due to invalid command line parameters --- .../src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt index 0b219e67a5..0c12a2dcd5 100644 --- a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt +++ b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt @@ -71,7 +71,7 @@ fun CordaCliWrapper.start(args: Array) { Help.Ansi.AUTO } val results = cmd.parseWithHandlers(RunLast().useOut(System.out).useAnsi(defaultAnsiMode), - DefaultExceptionHandler>().useErr(System.err).useAnsi(defaultAnsiMode), + DefaultExceptionHandler>().useErr(System.err).useAnsi(defaultAnsiMode).andExit(ExitCodes.FAILURE), *args) // If an error code has been returned, use this and exit results?.firstOrNull()?.let { From e6804aead6692b5602cd87046fe3e07fba9a5e53 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Thu, 21 Mar 2019 14:13:15 +0000 Subject: [PATCH 108/159] CORDA-2750: Fix published names for DJVM artifacts. (#4911) --- djvm/djvm/build.gradle | 4 ++-- djvm/djvm/cli/build.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/djvm/djvm/build.gradle b/djvm/djvm/build.gradle index d5535ba8ee..8dcd60ef51 100644 --- a/djvm/djvm/build.gradle +++ b/djvm/djvm/build.gradle @@ -44,8 +44,8 @@ dependencies { jar.enabled = false shadowJar { - archiveBaseName = 'corda-djvm' - archiveClassifier = '' + baseName'corda-djvm' + classifier '' relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm' // These particular classes are only needed to "bootstrap" diff --git a/djvm/djvm/cli/build.gradle b/djvm/djvm/cli/build.gradle index 403356ab2d..3c3562bbd0 100644 --- a/djvm/djvm/cli/build.gradle +++ b/djvm/djvm/cli/build.gradle @@ -25,8 +25,8 @@ dependencies { jar.enabled = false shadowJar { - archiveBaseName = djvmName - archiveClassifier = '' + baseName djvmName + classifier '' manifest { attributes( 'Automatic-Module-Name': 'net.corda.djvm.cli', From 2777e32a1c3c6aab7d08a1446d18cae6a6037494 Mon Sep 17 00:00:00 2001 From: Jonathan Locke <36930160+lockathan@users.noreply.github.com> Date: Thu, 21 Mar 2019 16:56:18 +0000 Subject: [PATCH 109/159] ENT-3327: Check for missing certificates directory (#4905) While the node is starting up, we now check for the presence of the certificates directory. This allows us to print out an easily understandable error message if the directory is not present. An exception is made for devMode, as devMode will result in the directory being created in any case. --- .../net/corda/node/internal/NodeStartup.kt | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index ba8a87da89..98067cb5e4 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -149,19 +149,21 @@ open class NodeStartup : NodeStartupLogging { // Step 5. Load and validate node configuration. val rawConfig = cmdLineOptions.rawConfiguration().doOnErrors(cmdLineOptions::logRawConfigurationErrors).optional ?: return ExitCodes.FAILURE val configuration = cmdLineOptions.parseConfiguration(rawConfig).doIfValid { logRawConfig(rawConfig) }.doOnErrors(::logConfigurationErrors).optional ?: return ExitCodes.FAILURE + + // Step 6. Check if we can access the certificates directory + if (!canReadCertificatesDirectory(configuration.certificatesDirectory, configuration.devMode)) return ExitCodes.FAILURE - // Step 6. Configuring special serialisation requirements, i.e., bft-smart relies on Java serialization. + // Step 7. Configuring special serialisation requirements, i.e., bft-smart relies on Java serialization. if (attempt { banJavaSerialisation(configuration) }.doOnFailure(Consumer { error -> error.logAsUnexpected("Exception while configuring serialisation") }) !is Try.Success) return ExitCodes.FAILURE - // Step 7. Any actions required before starting up the Corda network layer. + // Step 8. Any actions required before starting up the Corda network layer. if (attempt { preNetworkRegistration(configuration) }.doOnFailure(Consumer(::handleRegistrationError)) !is Try.Success) return ExitCodes.FAILURE - // Step 8. Log startup info. + // Step 9. Log startup info. logStartupInfo(versionInfo, configuration) - // Step 9. Start node: create the node, check for other command-line options, add extra logging etc. + // Step 10. Start node: create the node, check for other command-line options, add extra logging etc. if (attempt { - cmdLineOptions.baseDirectory.createDirectories() afterNodeInitialisation.run(createNode(configuration, versionInfo)) }.doOnFailure(Consumer(::handleStartError)) !is Try.Success) return ExitCodes.FAILURE @@ -297,6 +299,20 @@ open class NodeStartup : NodeStartupLogging { return true } + private fun canReadCertificatesDirectory(certDirectory: Path, devMode: Boolean): Boolean { + //Test for access to the certificates path and shutdown if we are unable to reach it. + //We don't do this if devMode==true because the certificates would be created anyway + if (devMode) return true + + if (!certDirectory.isDirectory()) { + printError("Unable to access certificates directory ${certDirectory}. This could be because the node has not been registered with the Identity Operator.") + printError("Please see https://docs.corda.net/joining-a-compatibility-zone.html for more information.") + printError("Node will now shutdown.") + return false + } + return true + } + private fun lookupMachineNameAndMaybeWarn(): String { val start = System.currentTimeMillis() val hostName: String = InetAddress.getLocalHost().hostName From 9ebe464f636425d3b351785da9f4aa81b3be999a Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Fri, 22 Mar 2019 09:27:32 +0000 Subject: [PATCH 110/159] CORDA-2775: Simplify SourceClassLoader logic for DJVM. (#4913) --- djvm/djvm/build.gradle | 2 +- .../djvm/analysis/AnalysisConfiguration.kt | 5 +- .../net/corda/djvm/analysis/ClassResolver.kt | 2 +- .../net/corda/djvm/rewiring/ClassRewriter.kt | 4 +- .../corda/djvm/rewiring/SandboxClassLoader.kt | 16 +-- .../corda/djvm/rewiring/SandboxClassWriter.kt | 6 +- .../corda/djvm/source/SourceClassLoader.kt | 103 ++++++------------ .../execution/SandboxExecutorJavaTest.java | 2 + .../test/kotlin/net/corda/djvm/TestBase.kt | 4 +- .../djvm/execution/SandboxExecutorTest.kt | 1 + 10 files changed, 58 insertions(+), 87 deletions(-) diff --git a/djvm/djvm/build.gradle b/djvm/djvm/build.gradle index 8dcd60ef51..1c329cf704 100644 --- a/djvm/djvm/build.gradle +++ b/djvm/djvm/build.gradle @@ -44,7 +44,7 @@ dependencies { jar.enabled = false shadowJar { - baseName'corda-djvm' + baseName 'corda-djvm' classifier '' relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm' diff --git a/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisConfiguration.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisConfiguration.kt index f17059dd18..7b3988d0ad 100644 --- a/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisConfiguration.kt +++ b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/AnalysisConfiguration.kt @@ -8,7 +8,6 @@ import net.corda.djvm.references.ClassModule import net.corda.djvm.references.Member import net.corda.djvm.references.MemberModule import net.corda.djvm.references.MethodBody -import net.corda.djvm.source.AbstractSourceClassLoader import net.corda.djvm.source.BootstrapClassLoader import net.corda.djvm.source.SourceClassLoader import org.objectweb.asm.Opcodes.* @@ -48,7 +47,7 @@ class AnalysisConfiguration private constructor( val classModule: ClassModule, val memberModule: MemberModule, private val bootstrapClassLoader: BootstrapClassLoader?, - val supportingClassLoader: AbstractSourceClassLoader, + val supportingClassLoader: SourceClassLoader, private val isRootConfiguration: Boolean ) : Closeable { @@ -299,7 +298,7 @@ class AnalysisConfiguration private constructor( classModule: ClassModule = ClassModule(), memberModule: MemberModule = MemberModule(), bootstrapClassLoader: BootstrapClassLoader? = null, - sourceClassLoaderFactory: (ClassResolver, BootstrapClassLoader?) -> AbstractSourceClassLoader = { classResolver, bootstrapCL -> + sourceClassLoaderFactory: (ClassResolver, BootstrapClassLoader?) -> SourceClassLoader = { classResolver, bootstrapCL -> SourceClassLoader(emptyList(), classResolver, bootstrapCL) } ): AnalysisConfiguration { diff --git a/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt index 0609506b66..8c42acb039 100644 --- a/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt +++ b/djvm/djvm/src/main/kotlin/net/corda/djvm/analysis/ClassResolver.kt @@ -118,7 +118,7 @@ class ClassResolver( /** * Maps a class name to its equivalent class outside the sandbox. - * Needed by [net.corda.djvm.source.AbstractSourceClassLoader]. + * Needed by [net.corda.djvm.source.SourceClassLoader]. */ private fun toSource(className: String): String { return if (className in pinnedClasses) { diff --git a/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt index 7c732616d8..1722958156 100644 --- a/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt +++ b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/ClassRewriter.kt @@ -7,7 +7,7 @@ import net.corda.djvm.code.ClassMutator import net.corda.djvm.code.EmitterModule import net.corda.djvm.code.emptyAsNull import net.corda.djvm.references.Member -import net.corda.djvm.source.AbstractSourceClassLoader +import net.corda.djvm.source.SourceClassLoader import net.corda.djvm.utilities.loggerFor import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassVisitor @@ -22,7 +22,7 @@ import org.objectweb.asm.MethodVisitor */ open class ClassRewriter( private val configuration: SandboxConfiguration, - private val classLoader: AbstractSourceClassLoader + private val classLoader: SourceClassLoader ) { private val analysisConfig = configuration.analysisConfiguration diff --git a/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt index 16ed666419..905acfbff9 100644 --- a/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt +++ b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt @@ -9,8 +9,8 @@ import net.corda.djvm.analysis.ExceptionResolver.Companion.isDJVMException import net.corda.djvm.code.asPackagePath import net.corda.djvm.code.asResourcePath import net.corda.djvm.references.ClassReference -import net.corda.djvm.source.AbstractSourceClassLoader import net.corda.djvm.source.ClassSource +import net.corda.djvm.source.SourceClassLoader import net.corda.djvm.utilities.loggerFor import net.corda.djvm.validation.RuleValidator import org.objectweb.asm.Type @@ -27,13 +27,13 @@ import org.objectweb.asm.Type * @param parent This classloader's parent classloader. */ class SandboxClassLoader private constructor( - private val analysisConfiguration: AnalysisConfiguration, - private val ruleValidator: RuleValidator, - private val supportingClassLoader: AbstractSourceClassLoader, - private val rewriter: ClassRewriter, - private val context: AnalysisContext, - throwableClass: Class<*>?, - parent: ClassLoader? + private val analysisConfiguration: AnalysisConfiguration, + private val ruleValidator: RuleValidator, + private val supportingClassLoader: SourceClassLoader, + private val rewriter: ClassRewriter, + private val context: AnalysisContext, + throwableClass: Class<*>?, + parent: ClassLoader? ) : ClassLoader(parent ?: getSystemClassLoader()) { /** diff --git a/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt index edc95d4a48..489dd719ef 100644 --- a/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt +++ b/djvm/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassWriter.kt @@ -1,7 +1,7 @@ package net.corda.djvm.rewiring import net.corda.djvm.code.asPackagePath -import net.corda.djvm.source.AbstractSourceClassLoader +import net.corda.djvm.source.SourceClassLoader import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter import org.objectweb.asm.ClassWriter.COMPUTE_FRAMES @@ -23,11 +23,11 @@ import org.objectweb.asm.Type */ open class SandboxClassWriter( classReader: ClassReader, - private val cloader: AbstractSourceClassLoader, + private val cloader: SourceClassLoader, flags: Int = COMPUTE_FRAMES or COMPUTE_MAXS ) : ClassWriter(classReader, flags) { - override fun getClassLoader(): AbstractSourceClassLoader = cloader + override fun getClassLoader(): SourceClassLoader = cloader /** * Get the common super type of [type1] and [type2]. diff --git a/djvm/djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt b/djvm/djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt index f4c1bb108c..a18bae71fc 100644 --- a/djvm/djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt +++ b/djvm/djvm/src/main/kotlin/net/corda/djvm/source/SourceClassLoader.kt @@ -1,7 +1,6 @@ @file:JvmName("SourceClassLoaderTools") package net.corda.djvm.source -import net.corda.djvm.analysis.AnalysisConfiguration.Companion.SANDBOX_PREFIX import net.corda.djvm.analysis.AnalysisContext import net.corda.djvm.analysis.ClassResolver import net.corda.djvm.analysis.ExceptionResolver.Companion.getDJVMExceptionOwner @@ -22,17 +21,51 @@ import java.nio.file.Path import java.nio.file.Paths import kotlin.streams.toList -abstract class AbstractSourceClassLoader( +/** + * Class loader to manage an optional JAR of replacement Java APIs. + * @param bootstrapJar The location of the JAR containing the Java APIs. + */ +class BootstrapClassLoader( + bootstrapJar: Path +) : URLClassLoader(resolvePaths(listOf(bootstrapJar)), null) { + + /** + * Only search our own jars for the given resource. + */ + override fun getResource(name: String): URL? = findResource(name) +} + +/** + * Customizable class loader that allows the user to specify explicitly additional JARs and directories to scan. + * + * @param paths The directories and explicit JAR files to scan. + * @property classResolver The resolver to use to derive the original name of a requested class. + * @property bootstrap The [BootstrapClassLoader] containing the Java APIs for the sandbox. + */ +class SourceClassLoader private constructor( paths: List, private val classResolver: ClassResolver, + private val bootstrap: BootstrapClassLoader?, parent: ClassLoader? ) : URLClassLoader(resolvePaths(paths), parent) { + private companion object { + private val logger = loggerFor() + } + + constructor(paths: List, classResolver: ClassResolver, bootstrap: BootstrapClassLoader? = null) + :this(paths, classResolver, bootstrap, SourceClassLoader::class.java.classLoader) + + /** + * An empty [SourceClassLoader] that can only delegate to its [BootstrapClassLoader]. + */ + constructor(classResolver: ClassResolver, bootstrap: BootstrapClassLoader) + : this(emptyList(), classResolver, bootstrap, null) /** * Open a [ClassReader] for the provided class name. */ fun classReader( - className: String, context: AnalysisContext, origin: String? = null + className: String, context: AnalysisContext, origin: String? = null ): ClassReader { val originalName = classResolver.reverse(className.asResourcePath) @@ -73,70 +106,6 @@ abstract class AbstractSourceClassLoader( return loadClass(originalName) } - protected companion object { - @JvmStatic - protected val logger = loggerFor() - } -} - -/** - * Class loader to manage an optional JAR of replacement Java APIs. - * @param bootstrapJar The location of the JAR containing the Java APIs. - */ -class BootstrapClassLoader( - bootstrapJar: Path -) : URLClassLoader(resolvePaths(listOf(bootstrapJar)), null) { - - /** - * Only search our own jars for the given resource. - */ - override fun getResource(name: String): URL? = findResource(name) -} - -/** - * Class loader that only provides our built-in sandbox classes. - * @param classResolver The resolver to use to derive the original name of a requested class. - */ -class SandboxSourceClassLoader( - classResolver: ClassResolver, - private val bootstrap: BootstrapClassLoader -) : AbstractSourceClassLoader(emptyList(), classResolver, SandboxSourceClassLoader::class.java.classLoader) { - - /** - * Always check the bootstrap classloader first. If we're requesting - * built-in sandbox classes then delegate to our parent classloader, - * otherwise deny the request. - */ - override fun getResource(name: String): URL? { - val resource = bootstrap.findResource(name) - if (resource != null) { - return resource - } else if (isJvmInternal(name)) { - logger.error("Denying request for actual {}", name) - return null - } - - return if (name.startsWith(SANDBOX_PREFIX)) { - parent.getResource(name) - } else { - null - } - } -} - -/** - * Customizable class loader that allows the user to explicitly specify additional JARs and directories to scan. - * - * @param paths The directories and explicit JAR files to scan. - * @property classResolver The resolver to use to derive the original name of a requested class. - * @property bootstrap The [BootstrapClassLoader] containing the Java APIs for the sandbox. - */ -class SourceClassLoader( - paths: List, - classResolver: ClassResolver, - private val bootstrap: BootstrapClassLoader? = null -) : AbstractSourceClassLoader(paths, classResolver, SourceClassLoader::class.java.classLoader) { - /** * First check the bootstrap classloader, if we have one. * Otherwise check our parent classloader, followed by diff --git a/djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxExecutorJavaTest.java b/djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxExecutorJavaTest.java index 78d1dff711..802288de3a 100644 --- a/djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxExecutorJavaTest.java +++ b/djvm/djvm/src/test/java/net/corda/djvm/execution/SandboxExecutorJavaTest.java @@ -16,6 +16,8 @@ public class SandboxExecutorJavaTest extends TestBase { @Test public void testTransaction() { + //TODO: Transaction should not be a pinned class! It needs + // to be marshalled into and out of the sandbox. Set> pinnedClasses = singleton(Transaction.class); sandbox(new Object[0], pinnedClasses, WARNING, true, ctx -> { SandboxExecutor contractExecutor = new DeterministicSandboxExecutor<>(ctx.getConfiguration()); diff --git a/djvm/djvm/src/test/kotlin/net/corda/djvm/TestBase.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/TestBase.kt index 514a493567..62848d10cd 100644 --- a/djvm/djvm/src/test/kotlin/net/corda/djvm/TestBase.kt +++ b/djvm/djvm/src/test/kotlin/net/corda/djvm/TestBase.kt @@ -17,7 +17,7 @@ import net.corda.djvm.rules.Rule import net.corda.djvm.rules.implementation.* import net.corda.djvm.source.BootstrapClassLoader import net.corda.djvm.source.ClassSource -import net.corda.djvm.source.SandboxSourceClassLoader +import net.corda.djvm.source.SourceClassLoader import net.corda.djvm.utilities.Discovery import net.corda.djvm.validation.RuleValidator import org.junit.After @@ -88,7 +88,7 @@ abstract class TestBase { Whitelist.MINIMAL, bootstrapClassLoader = BootstrapClassLoader(DETERMINISTIC_RT), sourceClassLoaderFactory = { classResolver, bootstrapClassLoader -> - SandboxSourceClassLoader(classResolver, bootstrapClassLoader!!) + SourceClassLoader(classResolver, bootstrapClassLoader!!) }, additionalPinnedClasses = setOf( Utilities::class.java diff --git a/djvm/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxExecutorTest.kt b/djvm/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxExecutorTest.kt index 892d896970..91faffcacd 100644 --- a/djvm/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxExecutorTest.kt +++ b/djvm/djvm/src/test/kotlin/net/corda/djvm/execution/SandboxExecutorTest.kt @@ -37,6 +37,7 @@ class SandboxExecutorTest : TestBase() { @Test fun `can load and execute contract`() = sandbox(DEFAULT, pinnedClasses = setOf(Transaction::class.java)) { val contractExecutor = DeterministicSandboxExecutor(configuration) + //TODO: Transaction should not be a pinned class! It needs to be marshalled into and out of the sandbox. val tx = Transaction(1) assertThatExceptionOfType(SandboxException::class.java) .isThrownBy { contractExecutor.run(tx) } From a5dd23dd43574242b9f406cdcda9a41aa37d9d0d Mon Sep 17 00:00:00 2001 From: dazraf Date: Fri, 22 Mar 2019 09:42:29 +0000 Subject: [PATCH 111/159] CORDA-2770 - file watcher subscription in `NetworkMapUpdater` should be unsubscribed on close (#4914) --- .../services/network/NetworkMapUpdater.kt | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt index bb8cce5116..7d54ac55c0 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt @@ -29,6 +29,7 @@ import java.time.Duration import java.util.* import java.util.concurrent.ScheduledThreadPoolExecutor import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicReference import kotlin.reflect.KProperty1 import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.full.findAnnotation @@ -52,7 +53,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, executeExistingDelayedTasksAfterShutdownPolicy = false } private var newNetworkParameters: Pair? = null - private var fileWatcherSubscription: Subscription? = null + private val fileWatcherSubscription = AtomicReference() private var autoAcceptNetworkParameters: Boolean = true private lateinit var trustRoot: X509Certificate private lateinit var currentParametersHash: SecureHash @@ -63,7 +64,14 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, private lateinit var excludedAutoAcceptNetworkParameters: Set override fun close() { - fileWatcherSubscription?.unsubscribe() + fileWatcherSubscription.updateAndGet { subscription -> + subscription?.apply { + if (!isUnsubscribed) { + unsubscribe() + } + } + null // sets the atomic ref to null + } MoreExecutors.shutdownAndAwaitTermination(networkMapPoller, 50, TimeUnit.SECONDS) } @@ -73,28 +81,31 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, networkParameters: NetworkParameters, keyManagementService: KeyManagementService, networkParameterAcceptanceSettings: NetworkParameterAcceptanceSettings) { - require(fileWatcherSubscription == null) { "Should not call this method twice." } - this.trustRoot = trustRoot - this.currentParametersHash = currentParametersHash - this.ourNodeInfo = ourNodeInfo - this.ourNodeInfoHash = ourNodeInfo.raw.hash - this.networkParameters = networkParameters - this.keyManagementService = keyManagementService - this.autoAcceptNetworkParameters = networkParameterAcceptanceSettings.autoAcceptEnabled - this.excludedAutoAcceptNetworkParameters = networkParameterAcceptanceSettings.excludedAutoAcceptableParameters + fileWatcherSubscription.updateAndGet { subscription -> + require(subscription == null) { "Should not call this method twice" } + this.trustRoot = trustRoot + this.currentParametersHash = currentParametersHash + this.ourNodeInfo = ourNodeInfo + this.ourNodeInfoHash = ourNodeInfo.raw.hash + this.networkParameters = networkParameters + this.keyManagementService = keyManagementService + this.autoAcceptNetworkParameters = networkParameterAcceptanceSettings.autoAcceptEnabled + this.excludedAutoAcceptNetworkParameters = networkParameterAcceptanceSettings.excludedAutoAcceptableParameters - val autoAcceptNetworkParametersNames = autoAcceptablePropertyNames - excludedAutoAcceptNetworkParameters - if (autoAcceptNetworkParameters && autoAcceptNetworkParametersNames.isNotEmpty()) { - logger.info("Auto-accept enabled for network parameter changes which modify only: $autoAcceptNetworkParametersNames") - } - watchForNodeInfoFiles() - if (networkMapClient != null) { - watchHttpNetworkMap() + val autoAcceptNetworkParametersNames = autoAcceptablePropertyNames - excludedAutoAcceptNetworkParameters + if (autoAcceptNetworkParameters && autoAcceptNetworkParametersNames.isNotEmpty()) { + logger.info("Auto-accept enabled for network parameter changes which modify only: $autoAcceptNetworkParametersNames") + } + watchForNodeInfoFiles().also { + if (networkMapClient != null) { + watchHttpNetworkMap() + } + } } } - private fun watchForNodeInfoFiles() { - nodeInfoWatcher + private fun watchForNodeInfoFiles(): Subscription { + return nodeInfoWatcher .nodeInfoUpdates() .subscribe { for (update in it) { From 5eea37679f77b08bc7e86e1f2f7ab1d66ac6e62e Mon Sep 17 00:00:00 2001 From: josecoll Date: Fri, 22 Mar 2019 13:48:09 +0000 Subject: [PATCH 112/159] Minor clarification on implication of setting 'checkSufficientSignatures' parameter. (#4919) --- .../main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt b/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt index 41b61ff15e..b45f70d767 100644 --- a/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt @@ -18,7 +18,8 @@ import java.security.SignatureException * [SignedTransaction] and perform the resolution back-and-forth required to check the dependencies and download any missing * attachments. The flow will return the [SignedTransaction] after it is resolved and then verified using [SignedTransaction.verify]. * - * Please note that it will *not* store the transaction to the vault unless that is explicitly requested. + * Please note that it will *not* store the transaction to the vault unless that is explicitly requested and checkSufficientSignatures is true. + * Setting statesToRecord to anything else when checkSufficientSignatures is false will *not* update the vault. * * @property otherSideSession session to the other side which is calling [SendTransactionFlow]. * @property checkSufficientSignatures if true checks all required signatures are present. See [SignedTransaction.verify]. From bc74ba4be4bda93664836a48dda97900dc0d2444 Mon Sep 17 00:00:00 2001 From: cburlinchon Date: Wed, 20 Mar 2019 16:49:10 +0000 Subject: [PATCH 113/159] Add missing cliutils dependency to explorer --- tools/explorer/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/explorer/build.gradle b/tools/explorer/build.gradle index 0f2951c132..4f5a4c2553 100644 --- a/tools/explorer/build.gradle +++ b/tools/explorer/build.gradle @@ -21,6 +21,7 @@ dependencies { // Corda Core: Data structures and basic types needed to work with Corda. compile project(':core') + compile project(':tools:cliutils') compile project(':client:jfx') compile project(':finance:contracts') compile project(':finance:workflows') From c33ccc1e1b114bca30220f6b418264b70910fd30 Mon Sep 17 00:00:00 2001 From: cburlinchon Date: Thu, 21 Mar 2019 10:40:10 +0000 Subject: [PATCH 114/159] Dont add cliutils to non cli app, remove addition of log4j2 from config/dev --- tools/explorer/build.gradle | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tools/explorer/build.gradle b/tools/explorer/build.gradle index 4f5a4c2553..8f6f77bfa4 100644 --- a/tools/explorer/build.gradle +++ b/tools/explorer/build.gradle @@ -5,12 +5,6 @@ apply plugin: 'application' sourceCompatibility = 1.8 mainClassName = 'net.corda.explorer.Main' -// Use manual resource copying of log4j2.xml rather than source sets. -// This prevents problems in IntelliJ with regard to duplicate source roots. -processResources { - from file("$rootDir/config/dev/log4j2.xml") -} - dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" @@ -21,7 +15,6 @@ dependencies { // Corda Core: Data structures and basic types needed to work with Corda. compile project(':core') - compile project(':tools:cliutils') compile project(':client:jfx') compile project(':finance:contracts') compile project(':finance:workflows') From b6f9b86998b29ecfd374fe860b77fdc72e1e411a Mon Sep 17 00:00:00 2001 From: dazraf Date: Thu, 21 Mar 2019 16:05:53 +0000 Subject: [PATCH 115/159] CORDA-2772 - fix edge case when `stickTo.hashCode` return `Int.MIN_VALUE` --- .../main/kotlin/net/corda/core/internal/LazyStickyPool.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/net/corda/core/internal/LazyStickyPool.kt b/core/src/main/kotlin/net/corda/core/internal/LazyStickyPool.kt index 52ec2226ae..868997bc56 100644 --- a/core/src/main/kotlin/net/corda/core/internal/LazyStickyPool.kt +++ b/core/src/main/kotlin/net/corda/core/internal/LazyStickyPool.kt @@ -25,7 +25,12 @@ class LazyStickyPool( private val boxes = Array(size) { InstanceBox() } private fun toIndex(stickTo: Any): Int { - return Math.abs(stickTo.hashCode()) % boxes.size + return stickTo.hashCode().let { hashCode -> + when (hashCode) { + Int.MIN_VALUE -> 0 + else -> Math.abs(hashCode) % boxes.size + } + } } fun borrow(stickTo: Any): A { From b5f000418563c1a7b1133c1c0d66404ccffbf1d1 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Fri, 22 Mar 2019 11:49:42 +0000 Subject: [PATCH 116/159] BUILD: Tidy up Gradle for irs-demo:web. --- samples/irs-demo/web/build.gradle | 127 +++++++++++++++--------------- 1 file changed, 65 insertions(+), 62 deletions(-) diff --git a/samples/irs-demo/web/build.gradle b/samples/irs-demo/web/build.gradle index 562278d819..2f23adea3a 100644 --- a/samples/irs-demo/web/build.gradle +++ b/samples/irs-demo/web/build.gradle @@ -1,28 +1,32 @@ import java.nio.charset.StandardCharsets import java.nio.file.Files - - -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion" - classpath "io.spring.gradle:dependency-management-plugin:1.0.4.RELEASE" - classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" - classpath 'com.bmuschko:gradle-docker-plugin:3.2.1' - classpath "org.yaml:snakeyaml:1.19" - } -} - import org.yaml.snakeyaml.DumperOptions +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath "org.yaml:snakeyaml:1.19" + } +} + plugins { - id 'com.craigburke.client-dependencies' version '1.4.0' + id 'io.spring.dependency-management' + id 'com.bmuschko.docker-remote-api' version '3.2.1' + id 'com.craigburke.client-dependencies' version '1.4.0' } group = "${parent.group}.irs-demo" +dependencyManagement { + dependencies { + dependency "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" + dependency "org.apache.logging.log4j:log4j-core:$log4j_version" + dependency "org.apache.logging.log4j:log4j-api:$log4j_version" + } +} + clientDependencies { registry 'realBower', type:'bower', url:'https://registry.bower.io' realBower { @@ -45,9 +49,9 @@ clientDependencies { // Spring Boot plugin adds a numerous hardcoded dependencies in the version much lower then Corda expects // causing the problems in runtime. Those can be changed by manipulating above properties // See https://github.com/spring-gradle-plugins/dependency-management-plugin/blob/master/README.md#changing-the-value-of-a-version-property -ext['artemis.version'] = "$artemis_version" -ext['hibernate.version'] = "$hibernate_version" -ext['jackson.version'] = "$jackson_version" +ext['artemis.version'] = artemis_version +ext['hibernate.version'] = hibernate_version +ext['jackson.version'] = jackson_version apply plugin: 'kotlin' apply plugin: 'kotlin-spring' @@ -65,19 +69,19 @@ dependencies { exclude module: "spring-boot-starter-logging" exclude module: "logback-classic" } - compile('org.springframework.boot:spring-boot-starter-log4j2') - runtime("org.apache.logging.log4j:log4j-web:$log4j_version") + compile('org.springframework.boot:spring-boot-starter-log4j2') + runtimeOnly("org.apache.logging.log4j:log4j-web:$log4j_version") compile("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version") compile project(":client:rpc") compile project(":client:jackson") - compile project(":finance:workflows") - // TODO In the future remove -irs bit from the directory name. Currently it clashes with :finance:workflows (same for contracts). - compile project(":samples:irs-demo:cordapp:workflows-irs") + compile project(":finance:workflows") + // TODO In the future remove -irs bit from the directory name. Currently it clashes with :finance:workflows (same for contracts). + compile project(":samples:irs-demo:cordapp:workflows-irs") testCompile project(":test-utils") testCompile project(path: ":samples:irs-demo:cordapp:workflows-irs", configuration: "demoArtifacts") - // JOpt: for command line flags. + // JOpt: for command line flags. compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version" testCompile('org.springframework.boot:spring-boot-starter-test') { @@ -116,55 +120,54 @@ artifacts { } task createDockerfile(type: com.bmuschko.gradle.docker.tasks.image.Dockerfile, dependsOn: [bootRepackage]) { - destFile = file("$docker_dir/Dockerfile") + destFile = file("$docker_dir/Dockerfile") - from 'azul/zulu-openjdk-alpine:8u152' - copyFile jar.archiveName, "/opt/irs/web/" - workingDir "/opt/irs/web/" - defaultCommand "sh", "-c", "java -Dcorda.host=\$CORDA_HOST -jar ${jar.archiveName}" + from 'azul/zulu-openjdk-alpine:8u152' + copyFile jar.archiveName, "/opt/irs/web/" + workingDir "/opt/irs/web/" + defaultCommand "sh", "-c", "java -Dcorda.host=\$CORDA_HOST -jar ${jar.archiveName}" } task prepareDockerDir(type: Copy, dependsOn: [bootRepackage, createDockerfile]) { - from jar - into docker_dir + from jar + into docker_dir } task generateDockerCompose(dependsOn: [prepareDockerDir]) { - def outFile = new File(project.buildDir, "docker-compose.yml") + def outFile = new File(project.buildDir, "docker-compose.yml") - ext['dockerComposePath'] = outFile + ext['dockerComposePath'] = outFile - doLast { - def dockerComposeObject = [ - "version": "3", - "services": [ - "web-a": [ - "build": "$docker_dir".toString(), - "environment": [ - "CORDA_HOST": "bank-a:10003" - ], - "ports": ["8080"] - ], - "web-b": [ - "build": "$docker_dir".toString(), - "environment": [ - "CORDA_HOST": "bank-b:10003" - ], - "ports": ["8080"] - ] - ] - ] + doLast { + def dockerComposeObject = [ + "version": "3", + "services": [ + "web-a": [ + "build": "$docker_dir".toString(), + "environment": [ + "CORDA_HOST": "bank-a:10003" + ], + "ports": ["8080"] + ], + "web-b": [ + "build": "$docker_dir".toString(), + "environment": [ + "CORDA_HOST": "bank-b:10003" + ], + "ports": ["8080"] + ] + ] + ] + def options = new org.yaml.snakeyaml.DumperOptions() + options.indent = 2 + options.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK - def options = new org.yaml.snakeyaml.DumperOptions() - options.indent = 2 - options.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK + def dockerComposeContent = new org.yaml.snakeyaml.Yaml(options).dump(dockerComposeObject) - def dockerComposeContent = new org.yaml.snakeyaml.Yaml(options).dump(dockerComposeObject) - - Files.write(outFile.toPath(), dockerComposeContent.getBytes(StandardCharsets.UTF_8)) - } + Files.write(outFile.toPath(), dockerComposeContent.getBytes(StandardCharsets.UTF_8)) + } outputs.file(outFile) -} \ No newline at end of file +} From 4f812bbd9035638489a9d8ed0dbc6dadd560a17a Mon Sep 17 00:00:00 2001 From: alexeykorening Date: Mon, 18 Mar 2019 20:09:35 +0100 Subject: [PATCH 117/159] Preconditions draft --- .../main/resources/migration/common.changelog-init.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/node/src/main/resources/migration/common.changelog-init.xml b/node/src/main/resources/migration/common.changelog-init.xml index d8abdbfab3..454b1879ec 100644 --- a/node/src/main/resources/migration/common.changelog-init.xml +++ b/node/src/main/resources/migration/common.changelog-init.xml @@ -5,11 +5,17 @@ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"> + + + - + + + + From 57d0040b7bb9003baccd59279836e8b9008ca6db Mon Sep 17 00:00:00 2001 From: alexeykorening Date: Tue, 19 Mar 2019 16:47:43 +0100 Subject: [PATCH 118/159] PR review fixes --- node/src/main/resources/migration/common.changelog-init.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/src/main/resources/migration/common.changelog-init.xml b/node/src/main/resources/migration/common.changelog-init.xml index 454b1879ec..177825f529 100644 --- a/node/src/main/resources/migration/common.changelog-init.xml +++ b/node/src/main/resources/migration/common.changelog-init.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"> - + @@ -13,8 +13,8 @@ - + From 78aee24f6942b1de66e99351585372c1060df80d Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Fri, 22 Mar 2019 14:11:35 +0000 Subject: [PATCH 119/159] Fixes link. --- docs/source/network-bootstrapper.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/network-bootstrapper.rst b/docs/source/network-bootstrapper.rst index 5bb02f7d2b..780c3057e0 100644 --- a/docs/source/network-bootstrapper.rst +++ b/docs/source/network-bootstrapper.rst @@ -24,7 +24,7 @@ You can find out more about network maps and network parameters from :doc:`netwo Bootstrapping a test network ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The Corda Network Bootstrapper can be downloaded from `here `_. +The Corda Network Bootstrapper can be downloaded from `here `_. Create a directory containing a node config file, ending in "_node.conf", for each node you want to create. "devMode" must be set to true. Then run the following command: @@ -434,4 +434,4 @@ The Network Bootstrapper can be started with the following command line options: Sub-commands ------------ -``install-shell-extensions``: Install ``bootstrapper`` alias and auto completion for bash and zsh. See :doc:`cli-application-shell-extensions` for more info. \ No newline at end of file +``install-shell-extensions``: Install ``bootstrapper`` alias and auto completion for bash and zsh. See :doc:`cli-application-shell-extensions` for more info. From 803ea3e65267bd22ac87af66438161d71387fe88 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 21 Mar 2019 18:07:56 +0000 Subject: [PATCH 120/159] changing substitution mechanism so that it can work with code blocks --- docs/source/app-upgrade-notes.rst | 2 +- docs/source/conf.py | 28 +++++++++++++++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index 67b714c1e9..e127e2f14d 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -37,7 +37,7 @@ Step 2. Adjust the version numbers in your Gradle build files Alter the versions you depend on in your Gradle file like so: -.. parsed-literal:: +.. code:: groovy ext.corda_release_version = '|corda_version|' ext.corda_gradle_plugins_version = '|gradle_plugins_version|' diff --git a/docs/source/conf.py b/docs/source/conf.py index 95e9626abb..df13b03aca 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -11,17 +11,23 @@ with open("../../constants.properties", "r") as f: constants_properties_lines = f.readlines() constants_properties_dict = dict([l.strip().split('=') for l in constants_properties_lines if not l.startswith("#") and not l.strip() == ""]) -rst_epilog = """ -.. |java_version| replace:: 8u%s -.. |kotlin_version| replace:: %s -.. |gradle_plugins_version| replace:: %s -.. |quasar_version| replace:: %s -.. |corda_version| replace:: %s -""" % (constants_properties_dict["java8MinUpdateVersion"], - constants_properties_dict["kotlinVersion"], - constants_properties_dict["gradlePluginsVersion"], - constants_properties_dict["quasarVersion"], - constants_properties_dict["cordaVersion"]) +def cordaSourceReadReplace(app, docname, source): + result = source[0] + for key in app.config.corda_substitutions: + result = result.replace(key, app.config.corda_substitutions[key]) + source[0] = result + +corda_substitutions = { + "|corda_version|" : constants_properties_dict["cordaVersion"], + "|java_version|" : "8u"+constants_properties_dict["java8MinUpdateVersion"], + "|kotlin_version|" : constants_properties_dict["kotlinVersion"], + "|gradle_plugins_version|" : constants_properties_dict["gradlePluginsVersion"], + "|quasar_version|" : constants_properties_dict["quasarVersion"] +} + +def setup(app): + app.add_config_value('corda_substitutions', {}, True) + app.connect('source-read', cordaSourceReadReplace) ############################################################################ From 8ac4b33b0813fae2b8b8a5d7d0e03bec6352b477 Mon Sep 17 00:00:00 2001 From: ben Date: Fri, 22 Mar 2019 11:59:19 +0000 Subject: [PATCH 121/159] put some version related notes in the readme --- docs/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/README.md b/docs/README.md index 67931ccfb2..216274c94d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,3 +12,22 @@ agent, make sure the relevant drive is shared, and click 'Reset credentials'. It's probably worth reading [this](http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) to get your head around the rst syntax we're using. +## version placeholders + +Currently we support five placeholders that get substituted at build time: + +```groovy + "|corda_version|" + "|java_version|" + "|kotlin_version|" + "|gradle_plugins_version|" + "|quasar_version|" +``` + +If you put one of these in an rst file anywhere (including in a code tag) then it will be substituted with the value in constants.properties +(which is in the root of the project) at build time. + +The code for this can be found near the top of the conf.py file in the `docs/source` directory. + + + From acd2f8bce4dfaf63e28f749d0b15f43a413fb0e7 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Fri, 22 Mar 2019 14:46:02 +0000 Subject: [PATCH 122/159] CORDA-2683: Requests to start unknown flows can be deleted using killFlow (#4903) Normally, these requests are left unacknowledged in the MQ broker to allow recovery via installing the missing CorDapp. If this is not applicable then the request be dropped (and the other side informed of the error). --- .../kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt | 2 +- .../node/services/statemachine/StaffedFlowHospital.kt | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index ec15ef7b26..9019e0b7e3 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -136,7 +136,7 @@ internal class CordaRPCOpsImpl( return snapshot } - override fun killFlow(id: StateMachineRunId) = smm.killFlow(id) + override fun killFlow(id: StateMachineRunId): Boolean = if (smm.killFlow(id)) true else smm.flowHospital.dropSessionInit(id.uuid) override fun stateMachinesFeed(): DataFeed, StateMachineUpdate> { val (allStateMachines, changes) = smm.track() diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt index bf5eaacbf1..48673989d8 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt @@ -55,6 +55,11 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, private val mutex.locked { if (outcome != Outcome.UNTREATABLE) { treatableSessionInits[id] = InternalSessionInitRecord(sessionMessage, event, record) + log.warn("$sender has sent a flow request for an unknown flow ${sessionMessage.initiatorFlowClassName}. Install the missing " + + "CorDapp this flow belongs to and restart.") + log.warn("If you know it's safe to ignore this flow request then it can be deleted permanently using the killFlow RPC and " + + "the UUID $id (from the node shell you can run 'flow kill $id'). BE VERY CAUTIOUS OF THIS SECOND APPROACH AS THE " + + "REQUEST MAY CONTAIN A NOTARISED TRANSACTION THAT NEEDS TO BE RECORDED IN YOUR VAULT.") } recordsPublisher.onNext(record) } @@ -78,12 +83,13 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, private val * to send back the relevant session error to the initiator party and acknowledge its receipt from the message broker * so that it never gets redelivered. */ - fun dropSessionInit(id: UUID) { + fun dropSessionInit(id: UUID): Boolean { val (sessionMessage, event, publicRecord) = mutex.locked { - requireNotNull(treatableSessionInits.remove(id)) { "$id does not refer to any session init message" } + treatableSessionInits.remove(id) ?: return false } log.info("Errored session-init permanently dropped: $publicRecord") sendBackError(publicRecord.error, sessionMessage, publicRecord.sender, event) + return true } /** From 738f92de0a62f3c1b11c8fdf424bcd0f68beb074 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Fri, 22 Mar 2019 15:57:55 +0000 Subject: [PATCH 123/159] CORDA-2779 - Fix Netmap REST endpoint docs (#4918) * CORDA-2779 - Fix Netmap REST endpoint docs * Review comments --- docs/source/network-map.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/source/network-map.rst b/docs/source/network-map.rst index 966b43c643..cd2340626c 100644 --- a/docs/source/network-map.rst +++ b/docs/source/network-map.rst @@ -48,6 +48,25 @@ The set of REST end-points for the network map service are as follows. | GET | /network-map/my-hostname | Retrieve the IP address of the caller (and **not** of the network map). | +----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+ +Network maps hosted by R3 or other parties using R3's commercial network management tools typically also provide the following endpoints as a convenience to operators and other users + +.. note:: we include them here as they can aid debugging but, for the avoidance of doubt, they are not a formal part of the spec and the node will operate even in their absence. + ++----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+ +| Request method | Path | Description | ++================+=========================================+==============================================================================================================================================+ +| GET | /network-map/json | Retrieve the current public network map formatted as a JSON document. | ++----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+ +| GET | /network-map/json/{uuid} | Retrieve the current network map for a private network indicated by the uuid parameter formatted as a JSON document. | ++----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+ +| GET | /network-map/json/node-infos | Retrieve a human readable list of the currently registered ``NodeInfo`` files in the public network formatted as a JSON document. | ++----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+ +| GET | /network-map/json/node-infos/{uid} | Retrieve a human readable list of the currently registered ``NodeInfo`` files in the specified private network map. | ++----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+ +| GET | /network-map/json/node-info/{hash} | Retrieve a human readable version of a ``NodeInfo`` formatted as a JSON document. | ++----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+ + + HTTP is used for the network map service instead of Corda's own AMQP based peer to peer messaging protocol to enable the server to be placed behind caching content delivery networks like Cloudflare, Akamai, Amazon Cloudfront and so on. By using industrial HTTP cache networks the map server can be shielded from DoS attacks more effectively. Additionally, From d9a67d74263ce68290505e77bed2332ea0e9696e Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Sat, 23 Mar 2019 19:32:39 +0000 Subject: [PATCH 124/159] BUILD: Use global port allocator for flaky HardRestartTest. (#4921) --- .../services/statemachine/HardRestartTest.kt | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/HardRestartTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/HardRestartTest.kt index 1ef0c156a2..10e5f4ba2c 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/HardRestartTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/HardRestartTest.kt @@ -19,6 +19,7 @@ import net.corda.testing.core.singleIdentity import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.OutOfProcess import net.corda.testing.driver.driver +import net.corda.testing.driver.internal.incrementalPortAllocation import net.corda.testing.node.User import org.junit.Test import java.util.* @@ -62,14 +63,15 @@ class HardRestartTest { fun restartShortPingPongFlowRandomly() { val demoUser = User("demo", "demo", setOf(Permissions.startFlow(), Permissions.all())) driver(DriverParameters( + portAllocation = incrementalPortAllocation(10000), startNodesInProcess = false, inMemoryDB = false, notarySpecs = emptyList(), systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()) )) { val (a, b) = listOf( - startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")), - startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) + startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser)), + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser)) ).transpose().getOrThrow() val latch = CountDownLatch(1) @@ -82,7 +84,7 @@ class HardRestartTest { Thread.sleep(ms.toLong()) (b as OutOfProcess).process.destroyForcibly() b.stop() - startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:${b.rpcAddress.port}")) } CordaRPCClient(a.rpcAddress).use(demoUser.username, demoUser.password) { val returnValue = it.proxy.startFlow(::Ping, b.nodeInfo.singleIdentity(), 1).returnValue @@ -99,14 +101,15 @@ class HardRestartTest { fun restartLongPingPongFlowRandomly() { val demoUser = User("demo", "demo", setOf(Permissions.startFlow(), Permissions.all())) driver(DriverParameters( + portAllocation = incrementalPortAllocation(10000), startNodesInProcess = false, inMemoryDB = false, notarySpecs = emptyList(), systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()) )) { val (a, b) = listOf( - startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")), - startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) + startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser)), + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser)) ).transpose().getOrThrow() val latch = CountDownLatch(1) @@ -119,7 +122,7 @@ class HardRestartTest { Thread.sleep(ms.toLong()) (b as OutOfProcess).process.destroyForcibly() b.stop() - startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:${b.rpcAddress.port}")) } CordaRPCClient(a.rpcAddress).use(demoUser.username, demoUser.password) { val returnValue = it.proxy.startFlow(::Ping, b.nodeInfo.singleIdentity(), 100).returnValue @@ -136,14 +139,15 @@ class HardRestartTest { fun softRestartLongPingPongFlowRandomly() { val demoUser = User("demo", "demo", setOf(Permissions.startFlow(), Permissions.all())) driver(DriverParameters( + portAllocation = incrementalPortAllocation(10000), startNodesInProcess = false, inMemoryDB = false, notarySpecs = emptyList(), systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()) )) { val (a, b) = listOf( - startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")), - startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) + startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser)), + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser)) ).transpose().getOrThrow() val latch = CountDownLatch(1) @@ -155,7 +159,7 @@ class HardRestartTest { println("Sleeping $ms ms before kill") Thread.sleep(ms.toLong()) b.stop() - startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:${b.rpcAddress.port}")) } CordaRPCClient(a.rpcAddress).use(demoUser.username, demoUser.password) { val returnValue = it.proxy.startFlow(::Ping, b.nodeInfo.singleIdentity(), 100).returnValue @@ -217,14 +221,15 @@ class HardRestartTest { fun restartRecursiveFlowRandomly() { val demoUser = User("demo", "demo", setOf(Permissions.startFlow(), Permissions.all())) driver(DriverParameters( + portAllocation = incrementalPortAllocation(10000), startNodesInProcess = false, inMemoryDB = false, notarySpecs = emptyList(), systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()) )) { val (a, b) = listOf( - startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")), - startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) + startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser)), + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser)) ).transpose().getOrThrow() val latch = CountDownLatch(1) @@ -237,7 +242,7 @@ class HardRestartTest { Thread.sleep(ms.toLong()) (b as OutOfProcess).process.destroyForcibly() b.stop() - startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:${b.rpcAddress.port}")) } val executor = Executors.newFixedThreadPool(8) try { @@ -253,7 +258,7 @@ class HardRestartTest { bRestartThread.join() } finally { - executor.shutdown() + executor.shutdownNow() } } } From 12e78973165682d41f2e07314f944e87a134f193 Mon Sep 17 00:00:00 2001 From: josecoll Date: Mon, 25 Mar 2019 11:59:39 +0000 Subject: [PATCH 125/159] CORDA-2782 Add Comparable to default whitelist for vault query criteria using comparables (#4920) * CORDA-2782 Add Comparable to default whitelist for vault query criteria using comparables * Commit the java.lang.Comparable type. * Fix broken unit test in serialization-deterministic --- .../net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt | 8 ++++++++ .../net/corda/serialization/internal/DefaultWhitelist.kt | 4 +++- .../net/corda/serialization/internal/DefaultWhitelist.kt | 5 ++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt index 7cd3d6ac46..62dd1c5afc 100644 --- a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt +++ b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt @@ -25,6 +25,7 @@ import net.corda.finance.workflows.getCashBalance import net.corda.finance.workflows.getCashBalances import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow +import net.corda.finance.schemas.CashSchemaV1 import net.corda.java.rpc.StandaloneCordaRPCJavaClientTest import net.corda.nodeapi.internal.config.User import net.corda.smoketesting.NodeConfig @@ -218,6 +219,13 @@ class StandaloneCordaRPClientTest { log.info("Cash Balances: $cashBalances") assertEquals(1, cashBalances.size) assertEquals(629.POUNDS, cashBalances[Currency.getInstance("GBP")]) + + // Check for cash states using a query criteria comparator + val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.greaterThan(10000L) } + val customCriteria = QueryCriteria.VaultCustomQueryCriteria(logicalExpression) + val customCriteriaResults = rpcProxy.vaultQueryBy(customCriteria) + assertEquals(1, customCriteriaResults.states.size) + assertEquals(customCriteriaResults.states.first().state.data.amount.quantity, 529.POUNDS.quantity) } @Test diff --git a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt b/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt index b1435f6c2f..c41625084b 100644 --- a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt +++ b/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt @@ -6,5 +6,7 @@ import net.corda.core.serialization.SerializationWhitelist * The DJVM does not need whitelisting, by definition. */ object DefaultWhitelist : SerializationWhitelist { - override val whitelist: List> get() = emptyList() + override val whitelist = listOf( + java.lang.Comparable::class.java // required for forwards compatibility with default whitelist + ) } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt index 1f4db775c4..7936982be3 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt @@ -63,6 +63,9 @@ object DefaultWhitelist : SerializationWhitelist { // Implementation of X509Certificate. X509CertImpl::class.java, - CRLReason::class.java + CRLReason::class.java, + + // used in Vault Query criteria comparators (see QueryCriteriaUtils.Builder) + java.lang.Comparable::class.java ) } From a1f7c4f2f4d43ba82a5d027940fd59b1df840402 Mon Sep 17 00:00:00 2001 From: Ben Wyeth Date: Mon, 25 Mar 2019 12:20:26 +0000 Subject: [PATCH 126/159] corda-2781 replace manual versions with substitutions (#4927) * been through the docs manually and picked out substitutions * hopefully addressing the url inconsistencies --- docs/source/api-scanner.rst | 2 +- docs/source/api-testing.rst | 8 +++---- docs/source/app-upgrade-notes.rst | 2 +- docs/source/azure-vm.rst | 23 +------------------ .../cli-application-shell-extensions.rst | 22 ++++++++---------- docs/source/conf.py | 3 ++- docs/source/cordapp-build-systems.rst | 2 +- docs/source/deploying-a-node.rst | 8 +++---- docs/source/generating-a-node.rst | 8 +++---- docs/source/network-bootstrapper.rst | 10 ++++---- docs/source/running-a-notary.rst | 4 ++-- docs/source/testnet-explorer-corda.rst | 12 +++++----- docs/source/tutorial-cordapp.rst | 8 +++---- docs/source/writing-a-cordapp.rst | 2 +- 14 files changed, 45 insertions(+), 69 deletions(-) diff --git a/docs/source/api-scanner.rst b/docs/source/api-scanner.rst index c0fb8ce8b3..e9446001cd 100644 --- a/docs/source/api-scanner.rst +++ b/docs/source/api-scanner.rst @@ -36,7 +36,7 @@ broken Corda's API. How it works ------------ -The ``generateApi`` Gradle task writes a summary of Corda's public API into the file ``build/api/api-corda-.txt``. +The ``generateApi`` Gradle task writes a summary of Corda's public API into the file ``build/api/api-corda-|corda_version|.txt``. The ``.ci/check-api-changes.sh`` script then compares this file with the contents of ``.ci/api-current.txt``, which is a managed file within the Corda repository. diff --git a/docs/source/api-testing.rst b/docs/source/api-testing.rst index 9a6004bdb4..9d7aa36bdc 100644 --- a/docs/source/api-testing.rst +++ b/docs/source/api-testing.rst @@ -161,8 +161,8 @@ Further examples * See the flow testing tutorial :doc:`here ` * See the oracle tutorial :doc:`here ` for information on testing ``@CordaService`` classes * Further examples are available in the Example CorDapp in - `Java `_ and - `Kotlin `_ + `Java `_ and + `Kotlin `_ Contract testing ---------------- @@ -380,5 +380,5 @@ Further examples * See the flow testing tutorial :doc:`here ` * Further examples are available in the Example CorDapp in - `Java `_ and - `Kotlin `_ + `Java `_ and + `Kotlin `_ diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index e127e2f14d..e9a104b0a8 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -445,7 +445,7 @@ been removed. You have some choices here: -* Upgrade your ``quasar.jar`` to the version consistent with your Corda version +* Upgrade your ``quasar.jar`` to ``|quasar_version|`` * Delete your ``lib`` directory and switch to using the Gradle test runner Instructions for both options can be found in :ref:`Running tests in Intellij `. diff --git a/docs/source/azure-vm.rst b/docs/source/azure-vm.rst index d63e6ce79f..a5793d4872 100644 --- a/docs/source/azure-vm.rst +++ b/docs/source/azure-vm.rst @@ -101,28 +101,7 @@ The nodes you will use to send and receive Yo messages require the Yo! CorDapp j Connect to one of your Corda nodes (make sure this is not the Notary node) using an SSH client of your choice (e.g. Putty) and log into the virtual machine using the public IP address and your SSH key or username / password combination you defined in Step 1 of the Azure build process. Type the following command: -For Corda nodes running release M10 - -.. sourcecode:: shell - - cd /opt/corda/cordapps - wget http://downloads.corda.net/cordapps/net/corda/yo/0.10.1/yo-0.10.1.jar - -For Corda nodes running release M11 - -.. sourcecode:: shell - - cd /opt/corda/cordapps - wget http://downloads.corda.net/cordapps/net/corda/yo/0.11.0/yo-0.11.0.jar - -For Corda nodes running version 2 - -.. sourcecode:: shell - - cd /opt/corda/plugins - wget http://ci-artifactory.corda.r3cev.com/artifactory/cordapp-showcase/yo-4.jar - - +Build the yo cordapp sample which you can find here: https://github.com/corda/samples/tree/release-V|platform_version|/yo-cordapp and install it in the cordapp directory. Now restart Corda and the Corda webserver using the following commands or restart your Corda VM from the Azure portal: diff --git a/docs/source/cli-application-shell-extensions.rst b/docs/source/cli-application-shell-extensions.rst index ba765d73ed..ea38827818 100644 --- a/docs/source/cli-application-shell-extensions.rst +++ b/docs/source/cli-application-shell-extensions.rst @@ -34,7 +34,7 @@ For example, for the Corda node, install the shell extensions using .. code-block:: shell - java -jar corda-.jar install-shell-extensions + java -jar corda-|corda_version|.jar install-shell-extensions And then run the node by running: @@ -62,15 +62,11 @@ Once the shell extensions have been installed, you can upgrade them in one of tw List of existing CLI applications ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+----------------------------------------------------------------+--------------------------------------------------------------+--------------------------------+ -| Description | JAR name | Alias | -+----------------------------------------------------------------+--------------------------------------------------------------+--------------------------------+ -| :ref:`Corda node` | ``corda-.jar`` | ``corda --