diff --git a/build.gradle b/build.gradle index a208dc801e..9ddd9e039f 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,7 @@ buildscript { ext.capsule_version = '1.0.1' ext.asm_version = '5.0.4' - ext.artemis_version = '2.5.0' + ext.artemis_version = '2.6.2' ext.jackson_version = '2.9.5' ext.jetty_version = '9.4.7.v20170914' ext.jersey_version = '2.25' @@ -79,8 +79,8 @@ buildscript { ext.curator_version = '4.0.1' ext.proguard_version = constants.getProperty('proguardVersion') ext.jsch_version = '0.1.54' - ext.protonj_version = '0.27.1' ext.commons_cli_version = '1.4' + ext.protonj_version = '0.27.1' // This is now aligned with the Artemis version, but retaining in case we ever need to diverge again for a bug fix. ext.snappy_version = '0.4' ext.fast_classpath_scanner_version = '2.12.3' ext.jcabi_manifests_version = '1.1' @@ -118,7 +118,6 @@ buildscript { classpath 'com.github.ben-manes:gradle-versions-plugin:0.15.0' classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version" classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" - classpath "org.ajoberstar:grgit:1.1.0" classpath "net.i2p.crypto:eddsa:$eddsa_version" // Needed for ServiceIdentityGenerator in the build environment. classpath "org.owasp:dependency-check-gradle:${dependency_checker_version}" classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$artifactory_plugin_version" @@ -133,7 +132,7 @@ plugins { } ext { - corda_revision = org.ajoberstar.grgit.Grgit.open(file('.')).head().id + corda_revision = "git rev-parse HEAD".execute().text.trim() } apply plugin: 'project-report' diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt index 7b338ddd0e..327e8cccbd 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt @@ -13,7 +13,7 @@ package net.corda.client.rpc import net.corda.core.CordaRuntimeException import net.corda.core.concurrent.CordaFuture import net.corda.core.internal.concurrent.openFuture -import net.corda.core.messaging.* +import net.corda.core.messaging.RPCOps import net.corda.core.utilities.getOrThrow import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.node.internal.rpcDriver @@ -70,10 +70,10 @@ class RPCFailureTests { } @Test - fun `unserializable`() = rpc { + fun unserializable() = rpc { assertThatThrownBy { it.getUnserializable() }.isInstanceOf(CordaRuntimeException::class.java) .hasMessageContaining("java.io.NotSerializableException:") - .hasMessageContaining("Unserializable is not on the whitelist or annotated with @CordaSerializable.") + .hasMessageContaining("Unserializable\" is not on the whitelist or annotated with @CordaSerializable.") } @Test @@ -81,6 +81,6 @@ class RPCFailureTests { val future = it.getUnserializableAsync() assertThatThrownBy { future.getOrThrow() }.isInstanceOf(CordaRuntimeException::class.java) .hasMessageContaining("java.io.NotSerializableException:") - .hasMessageContaining("Unserializable is not on the whitelist or annotated with @CordaSerializable.") + .hasMessageContaining("Unserializable\" is not on the whitelist or annotated with @CordaSerializable.") } } diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt index 07f9b05eac..f4db3bf103 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt @@ -25,7 +25,7 @@ class SwapIdentitiesFlowTests { @Before fun setup() { // We run this in parallel threads to help catch any race conditions that may exist. - mockNet = InternalMockNetwork(emptyList(), networkSendManuallyPumped = false, threadPerNode = true) + mockNet = InternalMockNetwork(networkSendManuallyPumped = false, threadPerNode = true) } @Test diff --git a/core-deterministic/build.gradle b/core-deterministic/build.gradle index a298578595..fb296cabce 100644 --- a/core-deterministic/build.gradle +++ b/core-deterministic/build.gradle @@ -26,8 +26,6 @@ configurations { dependencies { compileOnly project(':core') - compileOnly "com.google.guava:guava:$guava_version" - compileOnly "$quasar_group:quasar-core:$quasar_version:jdk8" // Configure these by hand. It should be a minimal subset of core's dependencies, // and without any obviously non-deterministic ones such as Hibernate. @@ -37,7 +35,6 @@ dependencies { runtimeLibraries "org.bouncycastle:bcprov-jdk15on:$bouncycastle_version" runtimeLibraries "org.bouncycastle:bcpkix-jdk15on:$bouncycastle_version" runtimeLibraries "com.google.code.findbugs:jsr305:$jsr305_version" - runtimeLibraries "com.google.guava:guava:$guava_version" runtimeLibraries "net.i2p.crypto:eddsa:$eddsa_version" runtimeLibraries "org.slf4j:slf4j-api:$slf4j_version" } diff --git a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt index 74625b2ba9..535e659ec7 100644 --- a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt +++ b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt @@ -10,7 +10,6 @@ package net.corda.core.identity -import com.google.common.collect.ImmutableSet import net.corda.core.KeepForDJVM import net.corda.core.internal.LegalNameValidator import net.corda.core.internal.toAttributesMap @@ -89,7 +88,7 @@ data class CordaX500Name(val commonName: String?, const val MAX_LENGTH_COMMON_NAME = 64 private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU) - private val countryCodes: Set = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry) + private val countryCodes: Set = setOf(*Locale.getISOCountries(), unspecifiedCountry) @JvmStatic fun build(principal: X500Principal): CordaX500Name { 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 2464734ef7..39a14c4d94 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -12,8 +12,6 @@ @file:KeepForDJVM package net.corda.core.internal -import com.google.common.hash.Hashing -import com.google.common.hash.HashingInputStream import net.corda.core.DeleteForDJVM import net.corda.core.KeepForDJVM import net.corda.core.cordapp.Cordapp @@ -22,6 +20,7 @@ import net.corda.core.cordapp.CordappContext import net.corda.core.crypto.* import net.corda.core.flows.FlowLogic import net.corda.core.node.ServicesForResolution +import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.* import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction @@ -53,6 +52,7 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths import java.security.KeyPair +import java.security.MessageDigest import java.security.PrivateKey import java.security.PublicKey import java.security.cert.* @@ -142,9 +142,16 @@ fun InputStream.readFully(): ByteArray = use { it.readBytes() } /** Calculate the hash of the remaining bytes in this input stream. The stream is closed at the end. */ fun InputStream.hash(): SecureHash { return use { - val his = HashingInputStream(Hashing.sha256(), it) - his.copyTo(NullOutputStream) // To avoid reading in the entire stream into memory just write out the bytes to /dev/null - SecureHash.SHA256(his.hash().asBytes()) + val md = MessageDigest.getInstance("SHA-256") + val buffer = ByteArray(DEFAULT_BUFFER_SIZE) + while (true) { + val count = it.read(buffer) + if (count == -1) { + break + } + md.update(buffer, 0, count) + } + SecureHash.SHA256(md.digest()) } } @@ -536,3 +543,8 @@ fun SerializedBytes.checkPayloadIs(type: Class): Untrustworthy return type.castIfPossible(payloadData)?.let { UntrustworthyData(it) } ?: throw IllegalArgumentException("We were expecting a ${type.name} but we instead got a ${payloadData.javaClass.name} ($payloadData)") } + +/** + * Extension method to make this method visible to nodeapi module. + */ +fun MappedSchema.getMigrationResource(): String? = this.internalGetMigrationResource() \ No newline at end of file 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 2c376a5d36..909c253375 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -44,13 +44,13 @@ import java.time.Instant */ @CordaSerializable data class StateMachineInfo @JvmOverloads constructor( - /** A univerally unique ID ([java.util.UUID]) representing this particular instance of the named flow. */ + /** A universally unique ID ([java.util.UUID]) representing this particular instance of the named flow. */ val id: StateMachineRunId, /** The JVM class name of the flow code. */ val flowLogicClassName: String, /** * An object representing information about the initiator of the flow. Note that this field is - * superceded by the [invocationContext] property, which has more detail. + * superseded by the [invocationContext] property, which has more detail. */ @Deprecated("There is more info available using 'invocationContext'") val initiator: FlowInitiator, /** A [DataFeed] of the current progress step as a human readable string, and updates to that string. */ @@ -378,9 +378,24 @@ interface CordaRPCOps : RPCOps { */ fun nodeInfoFromParty(party: AbstractParty): NodeInfo? - /** Clear all network map data from local node cache. */ + /** + * Clear all network map data from local node cache. Notice that after invoking this method your node will lose + * network map data and effectively won't be able to start any flow with the peers until network map is downloaded + * again on next poll - from `additional-node-infos` directory or from network map server. It depends on the + * polling interval when it happens. You can also use [refreshNetworkMapCache] to force next fetch from network map server + * (not from directory - it will happen automatically). + * If you run local test deployment and want clear view of the network, you may want to clear also `additional-node-infos` + * directory, because cache can be repopulated from there. + */ fun clearNetworkMapCache() + /** + * Poll network map server if available for the network map. Notice that you need to have `compatibilityZone` + * or `networkServices` configured. This is normally done automatically on the regular time interval, but you may wish to + * have the fresh view of network earlier. + */ + fun refreshNetworkMapCache() + /** Sets the value of the node's flows draining mode. * If this mode is [enabled], the node will reject new flows through RPC, ignore scheduled flows, and do not process * initial session messages, meaning that P2P counterparties will not be able to initiate new flows involving the node. diff --git a/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt b/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt index 55bf763716..a21b5c645c 100644 --- a/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt +++ b/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt @@ -61,7 +61,7 @@ open class MappedSchema(schemaFamily: Class<*>, */ protected open val migrationResource: String? = null - internal fun getMigrationResource(): String? = migrationResource + internal fun internalGetMigrationResource(): String? = migrationResource override fun toString(): String = "${this.javaClass.simpleName}(name=$name, version=$version)" diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index 589f16c3be..5482bfdda7 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -40,7 +40,7 @@ class AttachmentTests { @Before fun setUp() { - mockNet = InternalMockNetwork(emptyList()) + mockNet = InternalMockNetwork() } @After diff --git a/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt index b75a747068..b70698005d 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt @@ -23,7 +23,7 @@ import org.junit.After import org.junit.Test class ReceiveMultipleFlowTests { - private val mockNet = InternalMockNetwork(emptyList()) + private val mockNet = InternalMockNetwork() private val nodes = (0..2).map { mockNet.createPartyNode() } @After fun stopNodes() { diff --git a/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt b/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt index 02645ebb09..5603cceb36 100644 --- a/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt @@ -11,9 +11,11 @@ package net.corda.core.internal import net.corda.core.contracts.TimeWindow +import net.corda.core.crypto.SecureHash import org.assertj.core.api.Assertions.assertThat import org.junit.Assert.assertArrayEquals import org.junit.Test +import java.util.* import java.util.stream.IntStream import java.util.stream.Stream import kotlin.test.assertEquals @@ -109,6 +111,19 @@ open class InternalUtilsTest { assertThat(PrivateClass::class.java.kotlinObjectInstance).isNull() } + @Test + fun `test SHA-256 hash for InputStream`() { + val contents = arrayOfJunk(DEFAULT_BUFFER_SIZE * 2 + DEFAULT_BUFFER_SIZE / 2) + assertThat(contents.inputStream().hash()) + .isEqualTo(SecureHash.parse("A4759E7AA20338328866A2EA17EAF8C7FE4EC6BBE3BB71CEE7DF7C0461B3C22F")) + } + + private fun arrayOfJunk(size: Int) = ByteArray(size).apply { + for (i in 0 until size) { + this[i] = (i and 0xFF).toByte() + } + } + object PublicObject private object PrivateObject protected object ProtectedObject diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index cc8e1bfa8e..4d64c3749e 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -82,7 +82,7 @@ class AttachmentSerializationTest { @Before fun setUp() { - mockNet = InternalMockNetwork(emptyList()) + mockNet = InternalMockNetwork() server = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME)) client = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME)) client.internals.disableDBCloseOnStop() // Otherwise the in-memory database may disappear (taking the checkpoint with it) while we reboot the client. diff --git a/docs/source/blob-inspector.rst b/docs/source/blob-inspector.rst index d70063865d..0814b2ef88 100644 --- a/docs/source/blob-inspector.rst +++ b/docs/source/blob-inspector.rst @@ -25,15 +25,38 @@ When inspecting your custom data structures, there is no need to include the jar in the classpath. The blob inspector (or rather the serialization framework) is able to synthesis any classes found in the blob that are not on the classpath. -SerializedBytes -~~~~~~~~~~~~~~~ +Supported formats +~~~~~~~~~~~~~~~~~ -One thing to note is that the binary blob may contain embedded ``SerializedBytes`` objects. Rather than printing these -out as a Base64 string, the blob inspector will first materialise them into Java objects and then output those. You will -see this when dealing with classes such as ``SignedData`` or other structures that attach a signature, such as the -``nodeInfo-*`` files or the ``network-parameters`` file in the node's directory. For example, the output of a node-info -file may look like: +The inspector can read **input data** in three formats: raw binary, hex encoded text and base64 encoded text. For instance +if you have retrieved your binary data and it looks like this:: + 636f7264610100000080c562000000000001d0000030720000000300a3226e65742e636f7264613a38674f537471464b414a5055... + +then you have hex encoded data. If it looks like this it's base64 encoded:: + + Y29yZGEBAAAAgMViAAAAAAAB0AAAMHIAAAADAKMibmV0LmNvcmRhOjhnT1N0cUZLQUpQVWVvY2Z2M1NlU1E9PdAAACc1AAAAAgCjIm5l... + +And if it looks like something vomited over your screen it's raw binary. You don't normally need to care about these +differences because the tool will try every format until it works. + +Something that's useful to know about Corda's format is that it always starts with the word "corda" in binary. Try +hex decoding 636f726461 using the `online hex decoder tool here `_ +to see for yourself. + +**Output data** can be in either a slightly extended form of YaML or JSON. YaML (Yet another markup language) is a bit +easier to read for humans and is the default. JSON can of course be parsed by any JSON library in any language. + +.. note:: One thing to note is that the binary blob may contain embedded ``SerializedBytes`` objects. Rather than printing these + out as a Base64 string, the blob inspector will first materialise them into Java objects and then output those. You will + see this when dealing with classes such as ``SignedData`` or other structures that attach a signature, such as the + ``nodeInfo-*`` files or the ``network-parameters`` file in the node's directory. + + +Example +~~~~~~~ + +Here's what a node-info file from the node's data directory may look like: **-\\-format=YAML** :: diff --git a/docs/source/building-a-cordapp-index.rst b/docs/source/building-a-cordapp-index.rst index 925d1cc535..ad7b5457e5 100644 --- a/docs/source/building-a-cordapp-index.rst +++ b/docs/source/building-a-cordapp-index.rst @@ -12,7 +12,6 @@ CorDapps cordapp-build-systems building-against-master corda-api - serialization-index secure-coding-guidelines flow-cookbook vault diff --git a/docs/source/building-container-images.rst b/docs/source/building-container-images.rst new file mode 100644 index 0000000000..58e02b3580 --- /dev/null +++ b/docs/source/building-container-images.rst @@ -0,0 +1,54 @@ +========================= +Building Container Images +========================= + +To build a container image of Corda you can use the Jib gradle tasks. See the `documentation of the Jib gradle plugin `_ for details. + +Building the image +================== + +To build an image locally you can use the following command. Note that you do not require Docker. + +.. sourcecode:: shell + + ./gradlew node:jib --image /: + +If you prefer building to a Docker deamon you can use + +.. sourcecode:: shell + + ./gradlew node:jibDockerBuild --image /: + +Running the image +================= + +The Corda application expects its config file in ``/config/node.conf``, make +sure you mount the config file to that location. You might also want to mount +``/credentials`` and ``/persistence.mv.db`` (if you're using H2) in order to +preserve the credentials and node data between container restarts. + +The JVM options are currently hardcoded in ``node/build.gradle`` in the +``jib.container`` section. + +Below is an example directory layout and command to run your image with Docker. +Make sure to run ``touch persistence.mv.db`` befor starting the container, +otherwise a new directory will be created by Docker. + +:: + + . + ├── additional-node-infos + ├── certificates + ├── config + │   └── node.conf + ├── network-parameters + └── persistence.mv.db + +.. sourcecode:: shell + + docker run --rm -it -v ${PWD}/certificates:/certificates \ + -v ${PWD}/config:/config \ + -v ${PWD}/network-parameters:/network-parameters \ + -v ${PWD}/persistence.mv.db:/persistence.mv.db \ + -v ${PWD}/additional-node-infos:/additional-node-infos \ + /: diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 850864eec5..8eeca6b96f 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -178,6 +178,8 @@ Unreleased The log entry starts with `Cross-reference between MappedSchemas.`. API: Persistence documentation no longer suggests mapping between different schemas. +* Upgraded Artemis to v2.6.2. + .. _changelog_v3.1: Version 3.1 diff --git a/docs/source/flow-state-machines.rst b/docs/source/flow-state-machines.rst index 5f7f2a8caa..c48481df3c 100644 --- a/docs/source/flow-state-machines.rst +++ b/docs/source/flow-state-machines.rst @@ -24,9 +24,7 @@ partially signed invalid transactions outside of the main network, and by doing traded asset are performed atomically by the same transaction. To perform such a trade involves a multi-step flow in which messages are passed back and forth privately between parties, checked, signed and so on. -Despite how useful these flows are, platforms such as Bitcoin and Ethereum do not assist the developer with the rather -tricky task of actually building them. That is unfortunate. There are many awkward problems in their implementation -that a good platform would take care of for you, problems like: +There are many benefits of this flow based design and some development complexities as well. Some of the development challenges include: * Avoiding "callback hell" in which code that should ideally be sequential is turned into an unreadable mess due to the desire to avoid using up a thread for every flow instantiation. @@ -517,4 +515,4 @@ the features we have planned: * Being able to interact with people, either via some sort of external ticketing system, or email, or a custom UI. For example to implement human transaction authorisations * A standard library of flows that can be easily sub-classed by local developers in order to integrate internal - reporting logic, or anything else that might be required as part of a communications lifecycle \ No newline at end of file + reporting logic, or anything else that might be required as part of a communications lifecycle diff --git a/docs/source/getting-set-up.rst b/docs/source/getting-set-up.rst index 70e9194bf0..e90766c415 100644 --- a/docs/source/getting-set-up.rst +++ b/docs/source/getting-set-up.rst @@ -24,9 +24,9 @@ Please note: `here `_. If you're unfamiliar with Kotlin, there is an official `getting started guide `_, and a series of - `Kotlin Koans `_. + `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 * If an HA Bridge/Float deployment is required then a ``Zookeeper 3.5.3-Beta`` cluster will be required. Refer to :doc:`Hot-cold deployment ` and :doc:`Bridge configuration ` @@ -37,9 +37,9 @@ others to provide support. However, if you do use other tools, we'd be intereste Set-up instructions ------------------- -The instructions below will allow you to set up a Corda development environment and run a basic CorDapp. If you have -any issues, please consult the :doc:`troubleshooting` page, or reach out on `Slack `_, -`Stack Overflow `_ or the `forums `_. +The instructions below will allow you to set up your development environment for running Corda and writing CorDapps. If +you have any issues, please reach out on `Stack Overflow `_ or via +`our Slack channels `_. The set-up instructions are available for the following platforms: @@ -250,8 +250,9 @@ The best way to check that everything is working fine is by taking a deeper look Next, you should read through :doc:`Corda Key Concepts ` to understand how Corda works. By then, you'll be ready to start writing your own CorDapps. Learn how to do this in the -:doc:`Hello, World tutorial `. You may want to refer to the API documentation, the -:doc:`flow cookbook ` and the `samples `_ along the way. +:doc:`Hello, World tutorial `. You may want to refer to the +:doc:`API documentation `, the :doc:`flow cookbook ` and the +`samples `_ along the way. -If you encounter any issues, please see the :doc:`troubleshooting` page, or ask on -`Stack Overflow `_ or via `our Slack channels `_. +If you encounter any issues, please ask on `Stack Overflow `_ or via +`our Slack channels `_. diff --git a/docs/source/serialization-index.rst b/docs/source/serialization-index.rst index f59f687048..a01f6b15db 100644 --- a/docs/source/serialization-index.rst +++ b/docs/source/serialization-index.rst @@ -8,4 +8,5 @@ Serialization serialization.rst cordapp-custom-serializers serialization-default-evolution.rst - serialization-enum-evolution.rst \ No newline at end of file + serialization-enum-evolution.rst + blob-inspector \ No newline at end of file diff --git a/docs/source/tools-index.rst b/docs/source/tools-index.rst index 2285b38d9d..02d2c9c5fb 100644 --- a/docs/source/tools-index.rst +++ b/docs/source/tools-index.rst @@ -1,10 +1,12 @@ Tools ===== +Corda provides various command line and GUI tools to help you as you work. Along with the three below, you may also +wish to try the :doc:`blob-inspector`. + .. toctree:: :maxdepth: 1 network-bootstrapper - blob-inspector demobench node-explorer diff --git a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt index d379f0918a..e69de29bb2 100644 --- a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt +++ b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt @@ -1,158 +0,0 @@ -/* - * R3 Proprietary and Confidential - * - * Copyright (c) 2018 R3 Limited. All rights reserved. - * - * The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law. - * - * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. - */ - -package net.corda.finance.flows - -import net.corda.core.contracts.TransactionState -import net.corda.core.contracts.withoutIssuer -import net.corda.core.identity.Party -import net.corda.core.messaging.startFlow -import net.corda.core.transactions.TransactionBuilder -import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.getOrThrow -import net.corda.finance.DOLLARS -import net.corda.finance.contracts.asset.Cash -import net.corda.finance.contracts.asset.cash.selection.AbstractCashSelection -import net.corda.finance.contracts.getCashBalance -import net.corda.finance.issuedBy -import net.corda.testing.core.* -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.driver -import net.corda.testing.driver.internal.InProcessImpl -import net.corda.testing.internal.IntegrationTest -import net.corda.testing.internal.IntegrationTestSchemas -import net.corda.testing.internal.toDatabaseSchemaName -import org.assertj.core.api.Assertions.assertThat -import org.junit.ClassRule -import org.junit.Test - -class CashSelectionTest : IntegrationTest() { - companion object { - @ClassRule - @JvmField - val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME, DUMMY_NOTARY_NAME) - .map { it.toDatabaseSchemaName() }.toTypedArray()) - } - - @Test - fun `unconsumed cash states`() { - driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { - val node = startNode().getOrThrow() as InProcessImpl - val issuerRef = OpaqueBytes.of(0) - val issuedAmount = 1000.DOLLARS - - node.rpc.startFlow(::CashIssueFlow, issuedAmount, issuerRef, defaultNotaryIdentity).returnValue.getOrThrow() - - val availableBalance = node.rpc.getCashBalance(issuedAmount.token) - - assertThat(availableBalance).isEqualTo(issuedAmount) - - val exitedAmount = 300.DOLLARS - node.rpc.startFlow(::CashExitFlow, exitedAmount, issuerRef).returnValue.getOrThrow() - - val availableBalanceAfterExit = node.rpc.getCashBalance(issuedAmount.token) - - assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount) - } - } - - @Test - fun `cash selection sees states added in the same transaction`() { - driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { - val node = startNode().getOrThrow() as InProcessImpl - val nodeIdentity = node.services.myInfo.singleIdentity() - val issuer = nodeIdentity.ref(1) - val coin = 1.DOLLARS.issuedBy(issuer) - val exitedAmount = 1.DOLLARS - val issuance = TransactionBuilder(null as Party?) - issuance.addOutputState(TransactionState(Cash.State(coin, nodeIdentity), Cash.PROGRAM_ID, defaultNotaryIdentity)) - issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) - - //insert ans select in the same transaction - val exitStates = node.database.transaction { - - val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) - node.services.recordTransactions(transaction) - - val builder = TransactionBuilder(notary = null) - AbstractCashSelection - .getInstance { node.services.jdbcSession().metaData } - .unconsumedCashStatesForSpending(node.services, exitedAmount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) - } - val returnedCoinsNumber = 1 - assertThat(exitStates.size).isEqualTo(returnedCoinsNumber) - } - } - - @Test - fun `dont return extra coins if the selected amount has been reached`() { - driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { - val node = startNode().getOrThrow() as InProcessImpl - val nodeIdentity = node.services.myInfo.singleIdentity() - - val issuer = nodeIdentity.ref(1) - - val exitStates = node.database.transaction { - //issue $1 coin twice - repeat(2, { - val coin = 1.DOLLARS.issuedBy(issuer) - val issuance = TransactionBuilder(null as Party?) - issuance.addOutputState(TransactionState(Cash.State(coin, nodeIdentity), Cash.PROGRAM_ID, defaultNotaryIdentity)) - issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) - - val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) - - node.services.recordTransactions(transaction) - }) - - val exitedAmount = 1.DOLLARS - val builder = TransactionBuilder(notary = null) - AbstractCashSelection - .getInstance { node.services.jdbcSession().metaData } - .unconsumedCashStatesForSpending(node.services, exitedAmount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) - } - val returnedCoinsNumber = 1 - assertThat(exitStates.size).isEqualTo(returnedCoinsNumber) - } - } - - @Test - fun `select cash states issued by single transaction and give change`() { - driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { - val node = startNode().getOrThrow() as InProcessImpl - val nodeIdentity = node.services.myInfo.singleIdentity() - - val coins = listOf(3.DOLLARS, 2.DOLLARS, 1.DOLLARS).map { it.issuedBy(nodeIdentity.ref(1)) } - - //create single transaction with 3 cash outputs - val issuance = TransactionBuilder(null as Party?) - coins.map { issuance.addOutputState(TransactionState(Cash.State(it, nodeIdentity), "net.corda.finance.contracts.asset.Cash", defaultNotaryIdentity)) } - issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) - - val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) - node.database.transaction { - node.services.recordTransactions(transaction) - } - - val issuedAmount = coins.reduce { sum, element -> sum + element }.withoutIssuer() - - val availableBalance = node.rpc.getCashBalance(issuedAmount.token) - - assertThat(availableBalance).isEqualTo(issuedAmount) - - val exitedAmount = 3.01.DOLLARS - node.rpc.startFlow(::CashExitFlow, exitedAmount, OpaqueBytes.of(1)).returnValue.getOrThrow() - - val availableBalanceAfterExit = node.rpc.getCashBalance(issuedAmount.token) - - assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount) - } - } -} \ No newline at end of file diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashSelectionTest.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashSelectionTest.kt new file mode 100644 index 0000000000..0f829fbee7 --- /dev/null +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashSelectionTest.kt @@ -0,0 +1,136 @@ +package net.corda.finance.flows + +import net.corda.core.contracts.TransactionState +import net.corda.core.contracts.withoutIssuer +import net.corda.core.identity.Party +import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.getOrThrow +import net.corda.finance.DOLLARS +import net.corda.finance.contracts.asset.Cash +import net.corda.finance.contracts.asset.cash.selection.AbstractCashSelection +import net.corda.finance.contracts.getCashBalance +import net.corda.finance.issuedBy +import net.corda.testing.core.singleIdentity +import net.corda.testing.node.internal.InternalMockNetwork +import net.corda.testing.node.internal.startFlow +import org.assertj.core.api.Assertions.assertThat +import org.junit.After +import org.junit.Test + +class CashSelectionTest { + private val mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.finance"), threadPerNode = true) + + @After + fun cleanUp() { + mockNet.stopNodes() + } + + @Test + fun `unconsumed cash states`() { + val issuerRef = OpaqueBytes.of(0) + val issuedAmount = 1000.DOLLARS + + val node = mockNet.createNode() + node.services.startFlow(CashIssueFlow(issuedAmount, issuerRef, mockNet.defaultNotaryIdentity)).resultFuture.getOrThrow() + + val availableBalance = node.services.getCashBalance(issuedAmount.token) + + assertThat(availableBalance).isEqualTo(issuedAmount) + + val exitedAmount = 300.DOLLARS + node.services.startFlow(CashExitFlow(exitedAmount, issuerRef)).resultFuture.getOrThrow() + + val availableBalanceAfterExit = node.services.getCashBalance(issuedAmount.token) + + assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount) + } + + @Test + fun `cash selection sees states added in the same transaction`() { + val node = mockNet.createNode() + val nodeIdentity = node.services.myInfo.singleIdentity() + val issuer = nodeIdentity.ref(1) + val coin = 1.DOLLARS.issuedBy(issuer) + val exitedAmount = 1.DOLLARS + val issuance = TransactionBuilder(null as Party?) + issuance.addOutputState(TransactionState(Cash.State(coin, nodeIdentity), Cash.PROGRAM_ID, mockNet.defaultNotaryIdentity)) + issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) + + // Insert and select in the same transaction + val exitStates = node.database.transaction { + val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) + node.services.recordTransactions(transaction) + + val builder = TransactionBuilder(notary = null) + AbstractCashSelection + .getInstance { node.services.jdbcSession().metaData } + .unconsumedCashStatesForSpending(node.services, exitedAmount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) + } + val returnedCoinsNumber = 1 + assertThat(exitStates.size).isEqualTo(returnedCoinsNumber) + } + + @Test + fun `don't return extra coins if the selected amount has been reached`() { + val node = mockNet.createNode() + val nodeIdentity = node.services.myInfo.singleIdentity() + + val issuer = nodeIdentity.ref(1) + + val exitStates = node.database.transaction { + //issue $1 coin twice + repeat(2) { + val coin = 1.DOLLARS.issuedBy(issuer) + val issuance = TransactionBuilder(null as Party?) + issuance.addOutputState(TransactionState(Cash.State(coin, nodeIdentity), Cash.PROGRAM_ID, mockNet.defaultNotaryIdentity)) + issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) + + val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) + + node.services.recordTransactions(transaction) + } + + val exitedAmount = 1.DOLLARS + val builder = TransactionBuilder(notary = null) + AbstractCashSelection + .getInstance { node.services.jdbcSession().metaData } + .unconsumedCashStatesForSpending(node.services, exitedAmount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) + } + val returnedCoinsNumber = 1 + assertThat(exitStates.size).isEqualTo(returnedCoinsNumber) + } + + @Test + fun `select cash states issued by single transaction and give change`() { + val node = mockNet.createNode() + val nodeIdentity = node.services.myInfo.singleIdentity() + + val coins = listOf(3.DOLLARS, 2.DOLLARS, 1.DOLLARS).map { it.issuedBy(nodeIdentity.ref(1)) } + + //create single transaction with 3 cash outputs + val issuance = TransactionBuilder(null as Party?) + coins.forEach { + issuance.addOutputState(TransactionState(Cash.State(it, nodeIdentity), "net.corda.finance.contracts.asset.Cash", mockNet.defaultNotaryIdentity)) + } + issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) + + val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) + node.database.transaction { + node.services.recordTransactions(transaction) + } + + val issuedAmount = coins.reduce { sum, element -> sum + element }.withoutIssuer() + + val availableBalance = node.services.getCashBalance(issuedAmount.token) + + assertThat(availableBalance).isEqualTo(issuedAmount) + + val exitedAmount = 3.01.DOLLARS + node.services.startFlow(CashExitFlow(exitedAmount, OpaqueBytes.of(1))).resultFuture.getOrThrow() + + val availableBalanceAfterExit = node.services.getCashBalance(issuedAmount.token) + + assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount) + } +} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/MessageSizeChecksInterceptor.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/MessageSizeChecksInterceptor.kt index 854ae0109f..5b813bff0a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/MessageSizeChecksInterceptor.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/MessageSizeChecksInterceptor.kt @@ -22,7 +22,7 @@ class ArtemisMessageSizeChecksInterceptor(maxMessageSize: Int) : MessageSizeChec } class AmqpMessageSizeChecksInterceptor(maxMessageSize: Int) : MessageSizeChecksInterceptor(maxMessageSize), AmqpInterceptor { - override fun getMessageSize(packet: AMQPMessage?): Int? = packet?.length + override fun getMessageSize(packet: AMQPMessage?): Int? = packet?.encodeSize } /** diff --git a/core/src/main/kotlin/net/corda/core/internal/MigrationHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/MigrationHelpers.kt similarity index 95% rename from core/src/main/kotlin/net/corda/core/internal/MigrationHelpers.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/MigrationHelpers.kt index b24a4124da..974f07ce83 100644 --- a/core/src/main/kotlin/net/corda/core/internal/MigrationHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/MigrationHelpers.kt @@ -8,9 +8,10 @@ * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. */ -package net.corda.core.internal +package net.corda.nodeapi.internal import com.google.common.base.CaseFormat +import net.corda.core.internal.getMigrationResource import net.corda.core.schemas.MappedSchema object MigrationHelpers { 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 1a6f04a6df..6a837de4d4 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 @@ -20,7 +20,7 @@ import liquibase.database.core.MSSQLDatabase import liquibase.database.jvm.JdbcConnection import liquibase.lockservice.LockServiceFactory import liquibase.resource.ClassLoaderResourceAccessor -import net.corda.core.internal.MigrationHelpers.getMigrationResource +import net.corda.nodeapi.internal.MigrationHelpers.getMigrationResource import net.corda.core.schemas.MappedSchema import net.corda.core.utilities.contextLogger import java.io.ByteArrayInputStream diff --git a/node/build.gradle b/node/build.gradle index de422edc46..390cba8d18 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -8,6 +8,18 @@ * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. */ +// Import private compile time constants +buildscript { + def properties = new Properties() + file("$projectDir/src/main/resources/build.properties").withInputStream { properties.load(it) } + + ext.jolokia_version = properties.getProperty('jolokiaAgentVersion') +} + +plugins { + id 'com.google.cloud.tools.jib' version '0.9.4' +} + apply plugin: 'kotlin' // Java Persistence API support: create no-arg constructor // see: http://stackoverflow.com/questions/32038177/kotlin-with-jpa-default-constructor-hell @@ -19,14 +31,6 @@ apply plugin: 'com.jfrog.artifactory' description 'Corda node modules' -// Import private compile time constants -buildscript { - def properties = new Properties() - file("$projectDir/src/main/resources/build.properties").withInputStream { properties.load(it) } - - ext.jolokia_version = properties.getProperty('jolokiaAgentVersion') -} - //noinspection GroovyAssignabilityCheck configurations { integrationTestCompile.extendsFrom testCompile @@ -51,6 +55,12 @@ sourceSets { } } +jib.container { + mainClass = "net.corda.node.Corda" + args = ['--log-to-console', '--no-local-shell', '--config-file=/config/node.conf'] + jvmFlags = ['-Xmx1g', '-javaagent:/app/libs/quasar-core-0.7.10.jar'] +} + // Use manual resource copying of log4j2.xml rather than source sets. // This prevents problems in IntelliJ with regard to duplicate source roots. processResources { @@ -73,6 +83,8 @@ dependencies { compile "net.corda.plugins:cordform-common:$gradle_plugins_version" + compile group: 'co.paralleluniverse', name: 'quasar-core', version: '0.7.10:jdk8@jar' + // Log4J: logging framework (with SLF4J bindings) compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}" compile "org.apache.logging.log4j:log4j-web:${log4j_version}" 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 3b8e30bd2d..e301a22d44 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -11,6 +11,7 @@ package net.corda.node.internal import net.corda.client.rpc.notUsed +import net.corda.core.CordaRuntimeException import net.corda.core.concurrent.CordaFuture import net.corda.core.context.InvocationContext import net.corda.core.context.InvocationOrigin @@ -59,6 +60,7 @@ import net.corda.nodeapi.exceptions.NonRpcFlowException import net.corda.nodeapi.exceptions.RejectedCommandException import rx.Observable import java.io.InputStream +import java.net.ConnectException import java.security.PublicKey import java.time.Instant @@ -247,6 +249,17 @@ internal class CordaRPCOpsImpl( services.networkMapCache.clearNetworkMapCache() } + override fun refreshNetworkMapCache() { + try { + services.networkMapUpdater.updateNetworkMapCache() + } catch (e: Exception) { + when (e) { + is ConnectException -> throw CordaRuntimeException("There is connection problem to network map. The possible causes are incorrect configuration or network map service being down") + else -> throw e + } + } + } + override fun vaultQuery(contractStateType: Class): Vault.Page { return vaultQueryBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType) } diff --git a/node/src/main/kotlin/net/corda/node/internal/rpc/proxies/ExceptionSerialisingRpcOpsProxy.kt b/node/src/main/kotlin/net/corda/node/internal/rpc/proxies/ExceptionSerialisingRpcOpsProxy.kt index 4a5d451e80..286759a803 100644 --- a/node/src/main/kotlin/net/corda/node/internal/rpc/proxies/ExceptionSerialisingRpcOpsProxy.kt +++ b/node/src/main/kotlin/net/corda/node/internal/rpc/proxies/ExceptionSerialisingRpcOpsProxy.kt @@ -46,8 +46,6 @@ internal class ExceptionSerialisingRpcOpsProxy(private val delegate: CordaRPCOps val result = super.invoke(proxy, method, arguments) return result?.let { ensureSerialisable(it) } } catch (exception: Exception) { - // In this special case logging and re-throwing is the right approach. - log(exception) throw ensureSerialisable(exception) } } @@ -90,7 +88,13 @@ internal class ExceptionSerialisingRpcOpsProxy(private val delegate: CordaRPCOps private fun ensureSerialisable(error: Throwable): Throwable { val serialisable = (superclasses(error::class.java) + error::class.java).any { it.isAnnotationPresent(CordaSerializable::class.java) || it.interfaces.any { it.isAnnotationPresent(CordaSerializable::class.java) } } - val result = if (serialisable) error else CordaRuntimeException(error.message, error) + val result = if (serialisable) { + error + } else { + log(error) + CordaRuntimeException(error.message, error) + } + if (result is CordaThrowable) { result.stackTrace = arrayOf() result.setCause(null) 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 6ed7d09c80..bc81c4fbed 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 @@ -11,6 +11,7 @@ package net.corda.node.services.network import com.google.common.util.concurrent.MoreExecutors +import net.corda.core.CordaRuntimeException import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SignedData import net.corda.core.internal.copyTo @@ -38,6 +39,7 @@ import java.nio.file.StandardCopyOption import java.time.Duration import java.util.* import java.util.concurrent.Executors +import java.util.concurrent.ScheduledThreadPoolExecutor import java.util.concurrent.TimeUnit import kotlin.system.exitProcess @@ -55,7 +57,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, } private val parametersUpdatesTrack: PublishSubject = PublishSubject.create() - private val executor = Executors.newSingleThreadScheduledExecutor(NamedThreadFactory("Network Map Updater Thread", Executors.defaultThreadFactory())) + private val executor = ScheduledThreadPoolExecutor(1, NamedThreadFactory("Network Map Updater Thread", Executors.defaultThreadFactory())) private var newNetworkParameters: Pair? = null private var fileWatcherSubscription: Subscription? = null @@ -91,10 +93,11 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, if (networkMapClient == null) return // Subscribe to remote network map if configured. + executor.executeExistingDelayedTasksAfterShutdownPolicy = false executor.submit(object : Runnable { override fun run() { val nextScheduleDelay = try { - updateNetworkMapCache(networkMapClient) + updateNetworkMapCache() } catch (t: Throwable) { logger.warn("Error encountered while updating network map, will retry in $defaultRetryInterval", t) defaultRetryInterval @@ -105,7 +108,8 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, }) // The check may be expensive, so always run it in the background even the first time. } - private fun updateNetworkMapCache(networkMapClient: NetworkMapClient): Duration { + fun updateNetworkMapCache(): Duration { + if (networkMapClient == null) throw CordaRuntimeException("Network map cache can be updated only if network map/compatibility zone URL is specified") val (globalNetworkMap, cacheTimeout) = networkMapClient.getNetworkMap() globalNetworkMap.parametersUpdate?.let { handleUpdateNetworkParameters(networkMapClient, it) } val additionalHashes = extraNetworkMapKeys.flatMap { diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/MigrationExporter.kt b/node/src/main/kotlin/net/corda/node/services/persistence/MigrationExporter.kt index 8d70b19a77..ff78d35356 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/MigrationExporter.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/MigrationExporter.kt @@ -11,7 +11,7 @@ package net.corda.node.services.persistence import net.corda.core.identity.AbstractParty -import net.corda.core.internal.MigrationHelpers.migrationResourceNameForSchema +import net.corda.nodeapi.internal.MigrationHelpers.migrationResourceNameForSchema import net.corda.core.internal.objectOrNewInstance import net.corda.core.schemas.MappedSchema import net.corda.nodeapi.internal.persistence.CordaPersistence diff --git a/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt b/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt index 9ef1398618..3c9edc3bd5 100644 --- a/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt @@ -39,8 +39,7 @@ import kotlin.test.assertFails class NetworkParametersTest { private val mockNet = InternalMockNetwork( - emptyList(), - MockNetworkParameters(networkSendManuallyPumped = true), + defaultParameters = MockNetworkParameters(networkSendManuallyPumped = true), notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME))) @After diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeUnloadHandlerTests.kt b/node/src/test/kotlin/net/corda/node/internal/NodeUnloadHandlerTests.kt similarity index 53% rename from node/src/integration-test/kotlin/net/corda/node/NodeUnloadHandlerTests.kt rename to node/src/test/kotlin/net/corda/node/internal/NodeUnloadHandlerTests.kt index d5f6da21ac..16aa8e423f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeUnloadHandlerTests.kt +++ b/node/src/test/kotlin/net/corda/node/internal/NodeUnloadHandlerTests.kt @@ -7,43 +7,39 @@ * * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. */ +package net.corda.node.internal -package net.corda.node - +import net.corda.core.internal.packageName import net.corda.core.node.ServiceHub import net.corda.core.node.services.CordaService import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.contextLogger -import net.corda.core.utilities.getOrThrow -import net.corda.testing.core.DUMMY_BANK_A_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.internal.IntegrationTest -import net.corda.testing.internal.IntegrationTestSchemas -import net.corda.testing.internal.toDatabaseSchemaName -import org.junit.Assert -import org.junit.ClassRule +import net.corda.testing.node.internal.InternalMockNetwork +import org.junit.After import org.junit.Assert.assertTrue import org.junit.Test import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit -class NodeUnloadHandlerTests : IntegrationTest() { +class NodeUnloadHandlerTests { companion object { - @ClassRule - @JvmField - val databaseSchemas = IntegrationTestSchemas(DUMMY_BANK_A_NAME.toDatabaseSchemaName(), DUMMY_NOTARY_NAME.toDatabaseSchemaName() ) - val latch = CountDownLatch(1) + val registerLatch = CountDownLatch(1) + val shutdownLatch = CountDownLatch(1) + } + + private val mockNet = InternalMockNetwork(cordappPackages = listOf(javaClass.packageName), notarySpecs = emptyList()) + + @After + fun cleanUp() { + mockNet.stopNodes() } @Test fun `should be able to register run on stop lambda`() { - driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.node"), notarySpecs = emptyList())) { - startNode(providedName = DUMMY_BANK_A_NAME).getOrThrow() - // just want to fall off the end of this for the mo... - } - assertTrue("Timed out waiting for AbstractNode to invoke the test service shutdown callback", latch.await(30, TimeUnit.SECONDS)) + val node = mockNet.createNode() + registerLatch.await() // Make sure the handler is registered on node start up + node.dispose() + assertTrue("Timed out waiting for AbstractNode to invoke the test service shutdown callback", shutdownLatch.await(30, TimeUnit.SECONDS)) } @Suppress("unused") @@ -55,11 +51,12 @@ class NodeUnloadHandlerTests : IntegrationTest() { init { serviceHub.registerUnloadHandler(this::shutdown) + registerLatch.countDown() } private fun shutdown() { log.info("shutting down") - latch.countDown() + shutdownLatch.countDown() } } } diff --git a/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt b/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt index 991e771357..aad4d3d00b 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt @@ -28,7 +28,7 @@ class InMemoryMessagingTests { @Before fun setUp() { - mockNet = InternalMockNetwork(emptyList()) + mockNet = InternalMockNetwork() } @After diff --git a/node/src/test/kotlin/net/corda/node/services/FinalityHandlerTest.kt b/node/src/test/kotlin/net/corda/node/services/FinalityHandlerTest.kt index 8f4dfc408e..fc07181b00 100644 --- a/node/src/test/kotlin/net/corda/node/services/FinalityHandlerTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/FinalityHandlerTest.kt @@ -35,7 +35,7 @@ class FinalityHandlerTest { fun `sent to flow hospital on error and attempted retry on node restart`() { // Setup a network where only Alice has the finance CorDapp and it sends a cash tx to Bob who doesn't have the // CorDapp. Bob's FinalityHandler will error when validating the tx. - mockNet = InternalMockNetwork(cordappPackages = emptyList()) + mockNet = InternalMockNetwork() val alice = mockNet.createNode(InternalMockNodeParameters( legalName = ALICE_NAME, diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt index dee2a22cd4..21c866ab17 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt @@ -29,7 +29,7 @@ import kotlin.test.assertNotNull import kotlin.test.assertNull class NetworkMapCacheTest { - private val mockNet = InternalMockNetwork(emptyList()) + private val mockNet = InternalMockNetwork() @After fun teardown() { diff --git a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt index 8ad7bf50ce..cfdef8319d 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt @@ -50,7 +50,7 @@ class NodeSchemaServiceTest { @Test fun `check node runs with minimal core schema set`() { - val mockNet = InternalMockNetwork(cordappPackages = emptyList()) + val mockNet = InternalMockNetwork() val mockNode = mockNet.createNode() val schemaService = mockNode.services.schemaService @@ -62,7 +62,7 @@ class NodeSchemaServiceTest { @Test fun `check node runs inclusive of notary node schema set`() { - val mockNet = InternalMockNetwork(cordappPackages = emptyList()) + val mockNet = InternalMockNetwork() val mockNotaryNode = mockNet.notaryNodes.first() val schemaService = mockNotaryNode.services.schemaService diff --git a/serialization-deterministic/build.gradle b/serialization-deterministic/build.gradle index 8d77449637..598841922a 100644 --- a/serialization-deterministic/build.gradle +++ b/serialization-deterministic/build.gradle @@ -26,13 +26,13 @@ configurations { dependencies { compileOnly project(':serialization') - compileOnly "$quasar_group:quasar-core:$quasar_version:jdk8" // Configure these by hand. It should be a minimal subset of dependencies, // and without any obviously non-deterministic ones such as Hibernate. runtimeLibraries project(path: ':core-deterministic', configuration: 'runtimeArtifacts') runtimeLibraries "org.apache.qpid:proton-j:$protonj_version" runtimeLibraries "org.iq80.snappy:snappy:$snappy_version" + runtimeLibraries "com.google.guava:guava:$guava_version" } jar { diff --git a/serialization/build.gradle b/serialization/build.gradle index 48fa7dc6c0..eafc8cd155 100644 --- a/serialization/build.gradle +++ b/serialization/build.gradle @@ -14,6 +14,8 @@ dependencies { compile "org.ow2.asm:asm:$asm_version" + compile "com.google.guava:guava:$guava_version" + // For AMQP serialisation. compile "org.apache.qpid:proton-j:$protonj_version" diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt index 5750281f22..630c0d5af1 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt @@ -534,7 +534,7 @@ private fun Throwable.setMessage(newMsg: String) { fun ClassWhitelist.requireWhitelisted(type: Type) { if (!this.isWhitelisted(type.asClass()!!)) { - throw NotSerializableException("Class $type is not on the whitelist or annotated with @CordaSerializable.") + throw NotSerializableException("Class \"$type\" is not on the whitelist or annotated with @CordaSerializable.") } } diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/InternalMockNetworkIntegrationTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/InternalMockNetworkIntegrationTests.kt index 8b02b1f97a..17d39ec493 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/InternalMockNetworkIntegrationTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/InternalMockNetworkIntegrationTests.kt @@ -20,7 +20,7 @@ class InternalMockNetworkIntegrationTests { companion object { @JvmStatic fun main(args: Array) { - InternalMockNetwork(emptyList()).run { + InternalMockNetwork().run { repeat(2) { createNode() } runNetwork() stopNodes() diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt index 6e5cb838c1..d41dfee363 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt @@ -106,7 +106,7 @@ data class InternalMockNodeParameters( ) } -open class InternalMockNetwork(private val cordappPackages: List, +open class InternalMockNetwork(private val cordappPackages: List = emptyList(), defaultParameters: MockNetworkParameters = MockNetworkParameters(), val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, val threadPerNode: Boolean = defaultParameters.threadPerNode, diff --git a/testing/node-driver/src/test/kotlin/net/corda/testing/node/internal/InternalMockNetworkTests.kt b/testing/node-driver/src/test/kotlin/net/corda/testing/node/internal/InternalMockNetworkTests.kt index 0fa8bbcaba..a81be3461d 100644 --- a/testing/node-driver/src/test/kotlin/net/corda/testing/node/internal/InternalMockNetworkTests.kt +++ b/testing/node-driver/src/test/kotlin/net/corda/testing/node/internal/InternalMockNetworkTests.kt @@ -19,7 +19,7 @@ class InternalMockNetworkTests { fun `does not leak serialization env if init fails`() { val e = Exception("didn't work") assertThatThrownBy { - object : InternalMockNetwork(emptyList()) { + object : InternalMockNetwork() { override fun createNotaries() = throw e } }.isSameAs(e) diff --git a/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt b/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt index 564eb2cb7f..b73e0817d0 100644 --- a/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt +++ b/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt @@ -230,6 +230,10 @@ class CordaRPCProxyClient(private val targetHostAndPort: NetworkHostAndPort) : C TODO("not implemented") } + override fun refreshNetworkMapCache() { + TODO("not implemented") + } + private inline fun doPost(hostAndPort: NetworkHostAndPort, path: String, payload: ByteArray) : T { val url = URL("http://$hostAndPort/rpc/$path") val connection = url.openHttpConnection().apply { diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt index c9af615234..1cc2c75c9f 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt @@ -50,5 +50,5 @@ class DummyContractV2 : UpgradedContractWithLegacyConstraint) { - val main = Main() + val main = BlobInspector() try { CommandLine.run(main, *args) } catch (e: ExecutionException) { @@ -41,18 +45,21 @@ fun main(args: Array) { } @Command( - name = "Blob Inspector", + name = "blob-inspector", versionProvider = CordaVersionProvider::class, - mixinStandardHelpOptions = true, // add --help and --version options, + mixinStandardHelpOptions = true, // add --help and --version options, showDefaultValues = true, - description = ["Inspect AMQP serialised binary blobs"] + description = ["Convert AMQP serialised binary blobs to text"] ) -class Main : Runnable { +class BlobInspector : Runnable { @Parameters(index = "0", paramLabel = "SOURCE", description = ["URL or file path to the blob"], converter = [SourceConverter::class]) - private var source: URL? = null + var source: URL? = null @Option(names = ["--format"], paramLabel = "type", description = ["Output format. Possible values: [YAML, JSON]"]) - private var formatType: FormatType = FormatType.YAML + private var formatType: OutputFormatType = OutputFormatType.YAML + + @Option(names = ["--input-format"], paramLabel = "type", description = ["Input format. If the file can't be decoded with the given value it's auto-detected, so you should never normally need to specify this. Possible values: [BINARY, HEX, BASE64]"]) + private var inputFormatType: InputFormatType = InputFormatType.BINARY @Option(names = ["--full-parties"], description = ["Display the owningKey and certPath properties of Party and PartyAndReference objects respectively"]) @@ -64,54 +71,89 @@ class Main : Runnable { @Option(names = ["--verbose"], description = ["Enable verbose output"]) var verbose: Boolean = false - override fun run() { + override fun run() = run(System.out) + + fun run(out: PrintStream) { if (verbose) { System.setProperty("logLevel", "trace") } - val bytes = source!!.readBytes().run { - require(size > amqpMagic.size) { "Insufficient bytes for AMQP blob" } - sequence() - } - - require(bytes.take(amqpMagic.size) == amqpMagic) { "Not an AMQP blob" } + val inputBytes = source!!.readBytes() + val bytes = parseToBinaryRelaxed(inputFormatType, inputBytes) + ?: throw IllegalArgumentException("Error: this input does not appear to be encoded in Corda's AMQP extended format, sorry.") if (schema) { - val envelope = DeserializationInput.getEnvelope(bytes) - println(envelope.schema) - println() - println(envelope.transformsSchema) - println() + val envelope = DeserializationInput.getEnvelope(bytes.sequence()) + out.println(envelope.schema) + out.println() + out.println(envelope.transformsSchema) + out.println() } - initialiseSerialization() - val factory = when (formatType) { - FormatType.YAML -> YAMLFactory() - FormatType.JSON -> JsonFactory() + OutputFormatType.YAML -> YAMLFactory() + OutputFormatType.JSON -> JsonFactory() } + val mapper = JacksonSupport.createNonRpcMapper(factory, fullParties) - // Deserialise with the lenient carpenter as we only care for the AMQP field getters - val deserialized = bytes.deserialize(context = SerializationFactory.defaultFactory.defaultContext.withLenientCarpenter()) - println(deserialized.javaClass.name) - mapper.writeValue(System.out, deserialized) + initialiseSerialization() + try { + val deserialized = bytes.deserialize(context = SerializationDefaults.STORAGE_CONTEXT) + out.println(deserialized.javaClass.name) + mapper.writeValue(out, deserialized) + } finally { + _contextSerializationEnv.set(null) + } + } + + private fun parseToBinaryRelaxed(format: InputFormatType, inputBytes: ByteArray): ByteArray? { + // Try the format the user gave us first, then try the others. + //@formatter:off + return parseToBinary(format, inputBytes) ?: + parseToBinary(InputFormatType.HEX, inputBytes) ?: + parseToBinary(InputFormatType.BASE64, inputBytes) ?: + parseToBinary(InputFormatType.BINARY, inputBytes) + //@formatter:on + } + + private fun parseToBinary(format: InputFormatType, inputBytes: ByteArray): ByteArray? { + try { + val bytes = when (format) { + InputFormatType.BINARY -> inputBytes + InputFormatType.HEX -> String(inputBytes).trim().hexToByteArray() + InputFormatType.BASE64 -> String(inputBytes).trim().base64ToByteArray() + } + require(bytes.size > amqpMagic.size) { "Insufficient bytes for AMQP blob" } + return if (bytes.copyOf(amqpMagic.size).contentEquals(amqpMagic.bytes)) { + if (verbose) + println("Parsing input as $format") + bytes + } else { + null // Not an AMQP blob. + } + } catch (t: Throwable) { + return null // Failed to parse in some other way. + } } private fun initialiseSerialization() { + // Deserialise with the lenient carpenter as we only care for the AMQP field getters _contextSerializationEnv.set(SerializationEnvironmentImpl( SerializationFactoryImpl().apply { registerScheme(AMQPInspectorSerializationScheme) }, - AMQP_P2P_CONTEXT + p2pContext = AMQP_P2P_CONTEXT.withLenientCarpenter(), + storageContext = AMQP_STORAGE_CONTEXT.withLenientCarpenter() )) } } private object AMQPInspectorSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) { override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean { - return magic == amqpMagic && target == SerializationContext.UseCase.P2P + return magic == amqpMagic } + override fun rpcClientSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException() override fun rpcServerSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException() } @@ -137,5 +179,6 @@ private class CordaVersionProvider : IVersionProvider { } } -private enum class FormatType { YAML, JSON } +private enum class OutputFormatType { YAML, JSON } +private enum class InputFormatType { BINARY, HEX, BASE64 } diff --git a/tools/blobinspector/src/test/kotlin/net/corda/blobinspector/BlobInspectorTest.kt b/tools/blobinspector/src/test/kotlin/net/corda/blobinspector/BlobInspectorTest.kt new file mode 100644 index 0000000000..65e1223c4f --- /dev/null +++ b/tools/blobinspector/src/test/kotlin/net/corda/blobinspector/BlobInspectorTest.kt @@ -0,0 +1,67 @@ +package net.corda.blobinspector + +import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.SignedDataWithCert +import net.corda.core.node.NetworkParameters +import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.WireTransaction +import net.corda.testing.common.internal.checkNotOnClasspath +import org.apache.commons.io.output.WriterOutputStream +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import java.io.PrintStream +import java.io.StringWriter +import java.nio.charset.StandardCharsets.UTF_8 + +class BlobInspectorTest { + private val blobInspector = BlobInspector() + + @Test + fun `network-parameters file`() { + val output = run("network-parameters") + assertThat(output) + .startsWith(SignedDataWithCert::class.java.name) + .contains(NetworkParameters::class.java.name) + .contains(CordaX500Name("Notary Service", "Zurich", "CH").toString()) // Name of the notary in the network parameters + } + + @Test + fun `node-info file`() { + checkNotOnClassPath("net.corda.nodeapi.internal.SignedNodeInfo") + val output = run("node-info") + assertThat(output) + .startsWith("net.corda.nodeapi.internal.SignedNodeInfo") + .contains(CordaX500Name("BankOfCorda", "New York", "US").toString()) + } + + @Test + fun `WireTransaction with Cash state`() { + checkNotOnClassPath("net.corda.finance.contracts.asset.Cash\$State") + val output = run("cash-wtx.blob") + assertThat(output) + .startsWith(WireTransaction::class.java.name) + .contains("net.corda.finance.contracts.asset.Cash\$State") + } + + @Test + fun `SignedTransaction with Cash state taken from node db`() { + checkNotOnClassPath("net.corda.finance.contracts.asset.Cash\$State") + val output = run("cash-stx-db.blob") + assertThat(output) + .startsWith(SignedTransaction::class.java.name) + .contains("net.corda.finance.contracts.asset.Cash\$State") + } + + private fun run(resourceName: String): String { + blobInspector.source = javaClass.getResource(resourceName) + val writer = StringWriter() + blobInspector.run(PrintStream(WriterOutputStream(writer, UTF_8))) + val output = writer.toString() + println(output) + return output + } + + private fun checkNotOnClassPath(className: String) { + checkNotOnClasspath(className) { "The Blob Inspector does not have this as a dependency." } + } +} diff --git a/tools/blobinspector/src/test/resources/net/corda/blobinspector/cash-stx-db.blob b/tools/blobinspector/src/test/resources/net/corda/blobinspector/cash-stx-db.blob new file mode 100644 index 0000000000..21f93d8367 Binary files /dev/null and b/tools/blobinspector/src/test/resources/net/corda/blobinspector/cash-stx-db.blob differ diff --git a/tools/blobinspector/src/test/resources/net/corda/blobinspector/cash-wtx.blob b/tools/blobinspector/src/test/resources/net/corda/blobinspector/cash-wtx.blob new file mode 100644 index 0000000000..f828f1c10c Binary files /dev/null and b/tools/blobinspector/src/test/resources/net/corda/blobinspector/cash-wtx.blob differ diff --git a/tools/blobinspector/src/test/resources/net/corda/blobinspector/network-parameters b/tools/blobinspector/src/test/resources/net/corda/blobinspector/network-parameters new file mode 100644 index 0000000000..d6494a03d9 Binary files /dev/null and b/tools/blobinspector/src/test/resources/net/corda/blobinspector/network-parameters differ diff --git a/tools/blobinspector/src/test/resources/net/corda/blobinspector/node-info b/tools/blobinspector/src/test/resources/net/corda/blobinspector/node-info new file mode 100644 index 0000000000..91570082b7 Binary files /dev/null and b/tools/blobinspector/src/test/resources/net/corda/blobinspector/node-info differ diff --git a/tools/dbmigration/src/main/kotlin/com/r3/corda/dbmigration/Launcher.kt b/tools/dbmigration/src/main/kotlin/com/r3/corda/dbmigration/Launcher.kt index 4812e6d9d3..3054d6970c 100644 --- a/tools/dbmigration/src/main/kotlin/com/r3/corda/dbmigration/Launcher.kt +++ b/tools/dbmigration/src/main/kotlin/com/r3/corda/dbmigration/Launcher.kt @@ -18,7 +18,7 @@ import joptsimple.OptionException import joptsimple.OptionParser import joptsimple.OptionSet import joptsimple.util.EnumConverter -import net.corda.core.internal.MigrationHelpers +import net.corda.nodeapi.internal.MigrationHelpers import net.corda.core.internal.div import net.corda.core.internal.exists import net.corda.core.schemas.MappedSchema