diff --git a/.idea/compiler.xml b/.idea/compiler.xml index ec47b96af0..94e345833e 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -56,6 +56,9 @@ + + + @@ -96,6 +99,8 @@ + + diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 21516259c2..5cdaa2e867 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -125,6 +125,7 @@ see changes to this list. * Micheal Hinstridge (Thoughtworks) * Michele Sollecito (R3) * Mike Hearn (R3) +* Mike Ward (R3) * Mike Reichelt (US Bank) * Mustafa Ozturk (Natixis) * Nick Skinner (Northern Trust) @@ -161,6 +162,7 @@ see changes to this list. * Sasmit Sahu * Scott James * Shams Asari (R3) +* Siddhartha Sengupta (Tradewind Markets) * Simon Taylor (Barclays) * Sofus Mortensen (Digital Asset Holdings) * stevenroose diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/AMQPClientSerializationScheme.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/AMQPClientSerializationScheme.kt index 472a43dd82..dfb5f7f1fb 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/AMQPClientSerializationScheme.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/AMQPClientSerializationScheme.kt @@ -7,11 +7,11 @@ import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.internal.SerializationEnvironment import net.corda.core.serialization.internal.SerializationEnvironmentImpl import net.corda.core.serialization.internal.nodeSerializationEnv -import net.corda.nodeapi.internal.serialization.amqp.custom.RxNotificationSerializer import net.corda.serialization.internal.* import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme import net.corda.serialization.internal.amqp.SerializerFactory import net.corda.serialization.internal.amqp.amqpMagic +import net.corda.serialization.internal.amqp.custom.RxNotificationSerializer import java.util.concurrent.ConcurrentHashMap /** diff --git a/config/dev/log4j2.xml b/config/dev/log4j2.xml index f7943e6775..d71b403691 100644 --- a/config/dev/log4j2.xml +++ b/config/dev/log4j2.xml @@ -23,7 +23,7 @@ - + diff --git a/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt b/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt index 0b3bd0932e..e33a52e7ab 100644 --- a/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt +++ b/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt @@ -13,9 +13,9 @@ package net.corda.core.utilities import com.esotericsoftware.kryo.KryoException import net.corda.core.crypto.random63BitValue import net.corda.core.serialization.* -import net.corda.serialization.internal.KRYO_CHECKPOINT_CONTEXT +import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT +import net.corda.node.serialization.kryo.kryoMagic import net.corda.serialization.internal.SerializationContextImpl -import net.corda.serialization.internal.kryo.kryoMagic import net.corda.testing.core.SerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat import org.junit.Rule diff --git a/docs/source/_static/corda-introductory-whitepaper.pdf b/docs/source/_static/corda-introductory-whitepaper.pdf index a43f57e18b..334bb93530 100644 Binary files a/docs/source/_static/corda-introductory-whitepaper.pdf and b/docs/source/_static/corda-introductory-whitepaper.pdf differ diff --git a/docs/source/whitepaper/corda-platform-whitepaper.pdf b/docs/source/_static/corda-platform-whitepaper.pdf similarity index 100% rename from docs/source/whitepaper/corda-platform-whitepaper.pdf rename to docs/source/_static/corda-platform-whitepaper.pdf diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 290aac5560..f142982119 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -7,6 +7,10 @@ release, see :doc:`upgrade-notes`. Unreleased ========== +* ``NodeStartup`` will now only print node's configuration if ``devMode`` is ``true``, avoiding the risk of printing passwords in a production setup. + +* SLF4J's MDC will now only be printed to the console if not empty. No more log lines ending with "{}". + * ``WireTransaction.Companion.createComponentGroups`` has been marked as ``@CordaInternal``. It was never intended to be public and was already internal for Kotlin code. diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst index e1b5396534..3bfff458e4 100644 --- a/docs/source/contributing.rst +++ b/docs/source/contributing.rst @@ -11,20 +11,16 @@ Identifying an area to contribute --------------------------------- There are several ways to identify an area where you can contribute to Corda: +* Browse issues labelled as ``good first issue`` in the + `Corda GitHub Issues `_ + + * Any issue with a ``good first issue`` label is considered ideal for open-source contributions + * If there is a feature you would like to add and there isn't a corresponding issue labelled as ``good first issue``, + that doesn't mean your contribution isn't welcome. Please reach out on the ``#design`` channel to clarify (see + below) + * Ask in the ``#design`` channel of the `Corda Slack `_ -* Browse the `Corda GitHub issues `_ - - * It's always worth checking in the ``#design`` channel whether a given issue is a good target for your - contribution. Someone else may already be working on it, or it may be blocked by an on-going piece of work - -* Browse issues labelled as ``HelpWanted`` on the - `Corda JIRA board `_ - - * Any issue with a ``HelpWanted`` label is considered ideal for open-source contributions - * If there is a feature you would like to add and there isn't a corresponding issue labelled as ``HelpWanted``, that - doesn't mean your contribution isn't welcome. Please reach out on the ``#design`` channel to clarify - Making the required changes --------------------------- @@ -32,44 +28,117 @@ Making the required changes 2. Clone the fork to your local machine 3. Make the changes, in accordance with the :doc:`code style guide ` +Things to check +^^^^^^^^^^^^^^^ + +Is your error handling up to scratch? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Errors should not leak to the UI. When writing tools intended for end users, like the node or command line tools, +remember to add ``try``/``catch`` blocks. Throw meaningful errors. For example, instead of throwing an +``OutOfMemoryError``, use the error message to indicate that a file is missing, a network socket was unreachable, etc. +Tools should not dump stack traces to the end user. + +Look for API breaks +~~~~~~~~~~~~~~~~~~~ + +We have an automated checker tool that runs as part of our continuous integration pipeline and helps a lot, but it +can't catch semantic changes where the behavior of an API changes in ways that might violate app developer expectations. + +Suppress inevitable compiler warnings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Compiler warnings should have a ``@Suppress`` annotation on them if they're expected and can't be avoided. + +Remove deprecated functionality +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When deprecating functionality, make sure you remove the deprecated uses in the codebase. + +Avoid making formatting changes as you work +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In Kotlin 1.2.20, new style guide rules were implemented. The new Kotlin style guide is significantly more detailed +than before and IntelliJ knows how to implement those rules. Re-formatting the codebase creates a lot of diffs that +make merging more complicated. + +Things to consider when writing CLI apps +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Set exit codes using ``exitProcess``. Zero means success. Other numbers mean errors. Setting a unique error code + (starting from 1) for each thing that can conceivably break makes your tool shell-scripting friendly + +* Do a bit of work to figure out reasonable defaults. Nobody likes having to set a dozen flags before the tool will + cooperate + +* Your ``--help`` text or other docs should ideally include examples. Writing examples is also a good way to find out + that your program requires a dozen flags to do anything + +* Flags should have sensible defaults + +* Don’t print logging output to the console unless the user requested it via a ``–verbose`` flag (conventionally + shortened to ``-v``) or a ``–log-to-console`` flag. Logs should be either suppressed or saved to a text file during + normal usage, except for errors, which are always OK to print + Testing the changes ------------------- +Adding tests +^^^^^^^^^^^^ +Unit tests and integration tests for external API changes must cover Java and Kotlin. For internal API changes these +tests can be scaled back to kotlin only. + Running the tests ^^^^^^^^^^^^^^^^^ Your changes must pass the tests described :doc:`here `. +Manual testing +^^^^^^^^^^^^^^ +Before sending that code for review, spend time poking and prodding the tool and thinking, “Would the experience of +using this feature make my mum proud of me?”. Automated tests are not a substitute for dogfooding. + Building against the master branch ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -You can test your changes against CorDapps defined in other repos by following the instructions :doc:`here `. +You can test your changes against CorDapps defined in other repos by following the instructions +:doc:`here `. Updating the docs ----------------- Any changes to Corda's public API must be documented as follows: -1. Update the relevant `.rst file(s) `_ -2. Include the change in the :doc:`changelog ` and :doc:`release notes ` where applicable -3. :doc:`Build the docs locally ` -4. Open the built .html files for the modified pages to ensure they render correctly +1. Add comments and javadocs/kdocs. API functions must have javadoc/kdoc comments and sentences must be terminated + with a full stop. We also start comments with capital letters, even for inline comments. Where Java APIs have + synonyms (e.g. ``%d`` and ``%date``), we prefer the longer form for legibility reasons. You can configure your IDE + to highlight these in bright yellow +2. Update the relevant `.rst file(s) `_ +3. Include the change in the :doc:`changelog ` if the change is external and therefore visible to CorDapp + developers and/or node operators +4. :doc:`Build the docs locally ` +5. Check the built .html files (under ``docs/build/html``) for the modified pages to ensure they render correctly +6. If relevant, add a sample. Samples are one of the key ways in which users learn about what the platform can do. + If you add a new API or feature and don't update the samples, your work will be much less impactful Merging the changes back into Corda ----------------------------------- -1. Create a pull request from your fork to the master branch of the Corda repo -2. Complete the pull-request checklist in the comments box: +1. Create a pull request from your fork to the ``master`` branch of the Corda repo - * State that you have run the tests - * State that you have included JavaDocs for any new public APIs - * State that you have included the change in the :doc:`changelog ` and - :doc:`release notes ` where applicable - * State that you are in agreement with the terms of - `CONTRIBUTING.md `_ +2. In the PR comments box, complete the pull-request checklist: -3. Request a review from a member of the Corda platform team via the `#design channel `_ -4. Wait for your PR to pass all four types of continuous integration tests (integration, API stability, build and unit) + * [ ] Have you run the unit, integration and smoke tests as described here? https://docs.corda.net/head/testing.html + * [ ] If you added/changed public APIs, did you write/update the JavaDocs? + * [ ] If the changes are of interest to application developers, have you added them to the changelog, and potentially + release notes? + * [ ] If you are contributing for the first time, please read the agreement in CONTRIBUTING.md now and add to this + Pull Request that you agree to it. - * Currently, external contributors cannot see the output of these tests. If your PR fails a test that passed - locally, ask the reviewer for further details +3. In the PR comments box, also add a clear description of the purpose for the PR -5. Once a reviewer has approved the PR and the tests have passed, squash-and-merge the PR as a single commit +4. Request a review from a member of the Corda platform team via the `#design channel `_ + +5. The reviewer will either: + + * Accept and merge your PR + * Request that you make further changes. Do this by committing and pushing the changes onto the branch you are PRing + into Corda. The PR will be updated automatically diff --git a/docs/source/key-concepts-notaries.rst b/docs/source/key-concepts-notaries.rst index e92af20ed4..6866382ae2 100644 --- a/docs/source/key-concepts-notaries.rst +++ b/docs/source/key-concepts-notaries.rst @@ -66,6 +66,34 @@ created the "denial of state" transaction, allowing the attack to be resolved of In the case of the validating model, the use of anonymous, freshly-generated public keys instead of legal identities to identify parties in a transaction limit the information the notary cluster sees. +Data visibility +^^^^^^^^^^^^^^^ + +Below is a summary of what specific transaction components have to be revealed to each type of notary: + ++-----------------------------------+---------------+-----------------------+ +| Transaction components | Validating | Non-validating | ++===================================+===============+=======================+ +| Input states | Fully visible | References only [1]_ | ++-----------------------------------+---------------+-----------------------+ +| Output states | Fully visible | Hidden | ++-----------------------------------+---------------+-----------------------+ +| Commands (with signer identities) | Fully visible | Hidden | ++-----------------------------------+---------------+-----------------------+ +| Attachments | Fully visible | Hidden | ++-----------------------------------+---------------+-----------------------+ +| Time window | Fully visible | Fully visible | ++-----------------------------------+---------------+-----------------------+ +| Notary identity | Fully visible | Fully visible | ++-----------------------------------+---------------+-----------------------+ +| Signatures | Fully visible | Hidden | ++-----------------------------------+---------------+-----------------------+ + +Both types of notaries record the calling party's identity: the public key and the X.500 Distinguished Name. + +.. [1] A state reference is composed of the issuing transaction's id and the state's position in the outputs. It does not + reveal what kind of state it is or its contents. + Multiple notaries ----------------- Each Corda network can have multiple notary clusters, each potentially running a different consensus algorithm. This diff --git a/docs/source/node-database.rst b/docs/source/node-database.rst index 6d2cadce46..8f3c32ba4f 100644 --- a/docs/source/node-database.rst +++ b/docs/source/node-database.rst @@ -6,7 +6,7 @@ Default in-memory database By default, nodes store their data in an H2 database. You can connect directly to a running node's database to see its stored states, transactions and attachments as follows: -* Download the `h2 platform-independent zip `_, unzip the zip, and +* Download the **last stable** `h2 platform-independent zip `_, unzip the zip, and navigate in a terminal window to the unzipped folder * Change directories to the bin folder: ``cd h2/bin`` diff --git a/docs/source/tutorial-attachments.rst b/docs/source/tutorial-attachments.rst index df5e4b5a5e..297951f1d2 100644 --- a/docs/source/tutorial-attachments.rst +++ b/docs/source/tutorial-attachments.rst @@ -7,7 +7,7 @@ Attachments are ZIP/JAR files referenced from transaction by hash, but not inclu itself. These files are automatically requested from the node sending the transaction when needed and cached locally so they are not re-requested if encountered again. Attachments typically contain: -* Contract executable code +* Contract code * Metadata about a transaction, such as PDF version of an invoice being settled * Shared information to be permanently recorded on the ledger diff --git a/docs/source/whitepaper/corda-introductory-whitepaper-jp.pdf b/docs/source/whitepaper/corda-introductory-whitepaper-jp.pdf deleted file mode 100644 index 495ced5e2e..0000000000 Binary files a/docs/source/whitepaper/corda-introductory-whitepaper-jp.pdf and /dev/null differ diff --git a/docs/source/whitepaper/corda-introductory-whitepaper.pdf b/docs/source/whitepaper/corda-introductory-whitepaper.pdf deleted file mode 100644 index 334bb93530..0000000000 Binary files a/docs/source/whitepaper/corda-introductory-whitepaper.pdf and /dev/null differ diff --git a/docs/source/whitepaper/corda-introductory-whitepaper_zhs.pdf b/docs/source/whitepaper/corda-introductory-whitepaper_zhs.pdf deleted file mode 100644 index 2c02bec5c5..0000000000 Binary files a/docs/source/whitepaper/corda-introductory-whitepaper_zhs.pdf and /dev/null differ diff --git a/docs/source/whitepaper/corda-introductory-whitepaper_zht.pdf b/docs/source/whitepaper/corda-introductory-whitepaper_zht.pdf deleted file mode 100644 index 1038b6b9b4..0000000000 Binary files a/docs/source/whitepaper/corda-introductory-whitepaper_zht.pdf and /dev/null differ diff --git a/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobInspector.kt b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobInspector.kt index cc4f35aed4..84de454358 100644 --- a/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobInspector.kt +++ b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobInspector.kt @@ -5,7 +5,11 @@ import net.corda.core.serialization.EncodingWhitelist import net.corda.core.serialization.SerializationEncoding import net.corda.core.utilities.ByteSequence import net.corda.serialization.internal.SerializationFactoryImpl -import net.corda.serialization.internal.amqp.* +import net.corda.serialization.internal.amqp.CompositeType +import net.corda.serialization.internal.amqp.DeserializationInput +import net.corda.serialization.internal.amqp.RestrictedType +import net.corda.serialization.internal.amqp.TypeNotation +import net.corda.serialization.internal.amqp.amqpMagic import org.apache.qpid.proton.amqp.Binary import org.apache.qpid.proton.amqp.DescribedType import org.apache.qpid.proton.amqp.Symbol 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 ca3d883c3f..f3094ed808 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 @@ -37,19 +37,16 @@ import net.corda.serialization.internal.CordaSerializationMagic import net.corda.serialization.internal.SerializationFactoryImpl import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme import net.corda.serialization.internal.amqp.amqpMagic -import net.corda.serialization.internal.kryo.AbstractKryoSerializationScheme -import net.corda.serialization.internal.kryo.kryoMagic import java.nio.file.Path import java.nio.file.Paths import java.nio.file.StandardCopyOption.REPLACE_EXISTING import java.time.Instant import java.util.concurrent.Executors import java.util.concurrent.TimeoutException -import kotlin.streams.toList -import kotlin.collections.HashSet import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.set +import kotlin.streams.toList /** * Class to bootstrap a local network of Corda nodes on the same filesystem. @@ -348,22 +345,12 @@ class NetworkBootstrapper { private fun initialiseSerialization() { _contextSerializationEnv.set(SerializationEnvironmentImpl( SerializationFactoryImpl().apply { - registerScheme(KryoParametersSerializationScheme) registerScheme(AMQPParametersSerializationScheme) }, AMQP_P2P_CONTEXT) ) } - private object KryoParametersSerializationScheme : AbstractKryoSerializationScheme() { - override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean { - return magic == kryoMagic && target == SerializationContext.UseCase.P2P - } - - override fun rpcClientKryoPool(context: SerializationContext) = throw UnsupportedOperationException() - override fun rpcServerKryoPool(context: SerializationContext) = throw UnsupportedOperationException() - } - private object AMQPParametersSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) { override fun rpcClientSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException() override fun rpcServerSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException() diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt index dae59d458e..41eb2a17cf 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt @@ -21,10 +21,10 @@ import net.corda.core.serialization.serialize import net.corda.node.serialization.amqp.AMQPServerSerializationScheme import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.createDevKeyStores +import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.SerializationContextImpl import net.corda.serialization.internal.SerializationFactoryImpl import net.corda.serialization.internal.amqp.amqpMagic -import net.corda.serialization.internal.AllWhitelist import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.TestIdentity @@ -362,13 +362,15 @@ class X509UtilitiesTest { @Test fun `serialize - deserialize X509CertPath`() { val factory = SerializationFactoryImpl().apply { registerScheme(AMQPServerSerializationScheme()) } - val context = SerializationContextImpl(amqpMagic, + val context = SerializationContextImpl( + amqpMagic, javaClass.classLoader, AllWhitelist, emptyMap(), true, SerializationContext.UseCase.P2P, - null) + null + ) val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey) val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey) diff --git a/node/build.gradle b/node/build.gradle index a51b525344..02e9ad88c6 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -84,6 +84,10 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + // Kryo: object graph serialization. + compile "com.esotericsoftware:kryo:4.0.0" + compile "de.javakaffee:kryo-serializers:0.41" + compile "com.google.guava:guava:$guava_version" // For caches rather than guava diff --git a/node/src/main/kotlin/net/corda/node/NodeArgsParser.kt b/node/src/main/kotlin/net/corda/node/NodeArgsParser.kt index babd44a57e..1d5338ed6e 100644 --- a/node/src/main/kotlin/net/corda/node/NodeArgsParser.kt +++ b/node/src/main/kotlin/net/corda/node/NodeArgsParser.kt @@ -10,12 +10,14 @@ package net.corda.node +import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import joptsimple.OptionSet import joptsimple.util.EnumConverter import joptsimple.util.PathConverter import net.corda.core.internal.div import net.corda.core.internal.exists +import net.corda.core.utilities.Try import net.corda.node.services.config.ConfigHelper import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.parseAsNodeConfiguration @@ -125,19 +127,22 @@ data class CmdLineOptions(val baseDirectory: Path, val bootstrapRaftCluster: Boolean, val unknownConfigKeysPolicy: UnknownConfigKeysPolicy, val devMode: Boolean) { - fun loadConfig(): NodeConfiguration { - val config = ConfigHelper.loadConfig( + fun loadConfig(): Pair> { + val rawConfig = ConfigHelper.loadConfig( baseDirectory, configFile, configOverrides = ConfigFactory.parseMap(mapOf("noLocalShell" to this.noLocalShell) + if (devMode) mapOf("devMode" to this.devMode) else emptyMap()) - ).parseAsNodeConfiguration(unknownConfigKeysPolicy::handle) - if (nodeRegistrationOption != null) { - require(!config.devMode) { "registration cannot occur in devMode" } - requireNotNull(config.compatibilityZoneURL) { - "compatibilityZoneURL must be present in node configuration file in registration mode." + ) + return rawConfig to Try.on { + rawConfig.parseAsNodeConfiguration(unknownConfigKeysPolicy::handle).also { + if (nodeRegistrationOption != null) { + require(!it.devMode) { "registration cannot occur in devMode" } + requireNotNull(it.compatibilityZoneURL) { + "compatibilityZoneURL must be present in node configuration file in registration mode." + } + } } } - return config } } 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 fcca092dc6..23e7782bfe 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -382,6 +382,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, NodeInfoWatcher(configuration.baseDirectory, getRxIoScheduler(), Duration.ofMillis(configuration.additionalNodeInfoPollingFrequencyMsec)), networkMapClient, networkParameters.serialize().hash, + services.myInfo.serialize().hash, configuration.baseDirectory, configuration.extraNetworkMapKeys) runOnStop += networkMapUpdater::close @@ -862,7 +863,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, private fun makeIdentityService(identityCert: X509Certificate): PersistentIdentityService { val trustRoot = configuration.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA) val nodeCa = configuration.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA) - return PersistentIdentityService(trustRoot, identityCert, nodeCa) + return PersistentIdentityService(trustRoot, listOf(identityCert, nodeCa)) } protected abstract fun makeTransactionVerifierService(): TransactionVerifierService 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 9089686d14..bf60b130c5 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -38,6 +38,7 @@ import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser import net.corda.node.serialization.amqp.AMQPServerSerializationScheme +import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT import net.corda.node.serialization.kryo.KryoServerSerializationScheme import net.corda.node.services.Permissions import net.corda.node.services.api.NodePropertiesStore 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 dab26e0ed4..070ecfe164 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -11,6 +11,8 @@ package net.corda.node.internal import com.jcabi.manifests.Manifests +import com.typesafe.config.Config +import com.typesafe.config.ConfigRenderOptions import io.netty.channel.unix.Errors import net.corda.core.crypto.Crypto import net.corda.core.cordapp.Cordapp @@ -19,6 +21,7 @@ import net.corda.core.internal.concurrent.thenMatch import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.randomOrNull +import net.corda.core.utilities.Try import net.corda.core.utilities.loggerFor import net.corda.node.CmdLineOptions import net.corda.node.NodeArgsParser @@ -91,7 +94,11 @@ open class NodeStartup(val args: Array) { drawBanner(versionInfo) Node.printBasicNodeInfo(LOGS_CAN_BE_FOUND_IN_STRING, System.getProperty("log-path")) val conf = try { - val conf0 = loadConfigFile(cmdlineOptions) + val (rawConfig, conf0Result) = loadConfigFile(cmdlineOptions) + if (cmdlineOptions.devMode) { + println("Config:\n${rawConfig.root().render(ConfigRenderOptions.defaults())}") + } + val conf0 = conf0Result.getOrThrow() if (cmdlineOptions.bootstrapRaftCluster) { if (conf0 is NodeConfigurationImpl) { println("Bootstrapping raft cluster (starting up as seed node).") @@ -223,7 +230,7 @@ open class NodeStartup(val args: Array) { NodeRegistrationHelper(conf, HTTPNetworkRegistrationService(compatibilityZoneURL), nodeRegistrationConfig).buildKeystore() } - protected open fun loadConfigFile(cmdlineOptions: CmdLineOptions): NodeConfiguration = cmdlineOptions.loadConfig() + protected open fun loadConfigFile(cmdlineOptions: CmdLineOptions): Pair> = cmdlineOptions.loadConfig() protected open fun banJavaSerialisation(conf: NodeConfiguration) { val isOracleDbDriver = conf.dataSourceProperties.getProperty("dataSource.url", "").startsWith("jdbc:oracle:") diff --git a/node/src/main/kotlin/net/corda/node/serialization/amqp/AMQPServerSerializationScheme.kt b/node/src/main/kotlin/net/corda/node/serialization/amqp/AMQPServerSerializationScheme.kt index 7f74b7b190..70bcaaf0da 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/amqp/AMQPServerSerializationScheme.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/amqp/AMQPServerSerializationScheme.kt @@ -4,10 +4,10 @@ import net.corda.core.cordapp.Cordapp import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationCustomSerializer -import net.corda.nodeapi.internal.serialization.amqp.custom.RxNotificationSerializer import net.corda.serialization.internal.CordaSerializationMagic import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme import net.corda.serialization.internal.amqp.SerializerFactory +import net.corda.serialization.internal.amqp.custom.RxNotificationSerializer import java.util.concurrent.ConcurrentHashMap /** diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/CordaClassResolver.kt b/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClassResolver.kt similarity index 85% rename from serialization/src/main/kotlin/net/corda/serialization/internal/kryo/CordaClassResolver.kt rename to node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClassResolver.kt index 2db579ad35..17692a6a47 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/CordaClassResolver.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClassResolver.kt @@ -8,7 +8,7 @@ * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. */ -package net.corda.serialization.internal.kryo +package net.corda.node.serialization.kryo import com.esotericsoftware.kryo.* import com.esotericsoftware.kryo.io.Input @@ -23,6 +23,7 @@ import net.corda.core.serialization.SerializationContext import net.corda.core.utilities.contextLogger import net.corda.serialization.internal.AttachmentsClassLoader import net.corda.serialization.internal.MutableClassWhitelist +import net.corda.serialization.internal.TransientClassWhiteList import net.corda.serialization.internal.amqp.hasAnnotationInHierarchy import java.io.PrintWriter import java.lang.reflect.Modifier @@ -156,43 +157,6 @@ class CordaClassResolver(serializationContext: SerializationContext) : DefaultCl } } -class BuiltInExceptionsWhitelist : ClassWhitelist { - companion object { - private val packageName = "^(?:java|kotlin)(?:[.]|$)".toRegex() - } - - override fun hasListed(type: Class<*>) = Throwable::class.java.isAssignableFrom(type) && packageName.containsMatchIn(type.`package`.name) -} - -sealed class AbstractMutableClassWhitelist(private val whitelist: MutableSet, private val delegate: ClassWhitelist) : MutableClassWhitelist { - - override fun hasListed(type: Class<*>): Boolean { - /** - * There are certain delegates like [net.corda.serialization.internal.AllButBlacklisted] - * which may throw when asked whether the type is listed. - * In such situations - it may be a good idea to ask [delegate] first before making a check against own [whitelist]. - */ - return delegate.hasListed(type) || (type.name in whitelist) - } - - override fun add(entry: Class<*>) { - whitelist += entry.name - } -} - -// TODO: Need some concept of from which class loader -class GlobalTransientClassWhiteList(delegate: ClassWhitelist) : AbstractMutableClassWhitelist(whitelist, delegate) { - companion object { - private val whitelist: MutableSet = Collections.synchronizedSet(mutableSetOf()) - } -} - -/** - * A whitelist that can be customised via the [net.corda.core.serialization.SerializationWhitelist], - * since it implements [MutableClassWhitelist]. - */ -class TransientClassWhiteList(delegate: ClassWhitelist) : AbstractMutableClassWhitelist(Collections.synchronizedSet(mutableSetOf()), delegate) - /** * This class is not currently used, but can be installed to log a large number of missing entries from the whitelist * and was used to track down the initial set. diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/CordaClosureSerializer.kt b/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClosureSerializer.kt similarity index 96% rename from serialization/src/main/kotlin/net/corda/serialization/internal/kryo/CordaClosureSerializer.kt rename to node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClosureSerializer.kt index ed1dcd4261..683f1c91fd 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/CordaClosureSerializer.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClosureSerializer.kt @@ -8,7 +8,7 @@ * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. */ -package net.corda.serialization.internal.kryo +package net.corda.node.serialization.kryo import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.io.Output diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/DefaultKryoCustomizer.kt b/node/src/main/kotlin/net/corda/node/serialization/kryo/DefaultKryoCustomizer.kt similarity index 99% rename from serialization/src/main/kotlin/net/corda/serialization/internal/kryo/DefaultKryoCustomizer.kt rename to node/src/main/kotlin/net/corda/node/serialization/kryo/DefaultKryoCustomizer.kt index b3d63f2a2e..627108a354 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/DefaultKryoCustomizer.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/kryo/DefaultKryoCustomizer.kt @@ -8,7 +8,7 @@ * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. */ -package net.corda.serialization.internal.kryo +package net.corda.node.serialization.kryo import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.Serializer diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/Kryo.kt b/node/src/main/kotlin/net/corda/node/serialization/kryo/Kryo.kt similarity index 99% rename from serialization/src/main/kotlin/net/corda/serialization/internal/kryo/Kryo.kt rename to node/src/main/kotlin/net/corda/node/serialization/kryo/Kryo.kt index e9f8df23ad..3914fd22e7 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/Kryo.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/kryo/Kryo.kt @@ -8,7 +8,7 @@ * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. */ -package net.corda.serialization.internal.kryo +package net.corda.node.serialization.kryo import com.esotericsoftware.kryo.* import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/KryoSerializationScheme.kt b/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoSerializationScheme.kt similarity index 94% rename from serialization/src/main/kotlin/net/corda/serialization/internal/kryo/KryoSerializationScheme.kt rename to node/src/main/kotlin/net/corda/node/serialization/kryo/KryoSerializationScheme.kt index b5bd85a266..734331eebc 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/KryoSerializationScheme.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoSerializationScheme.kt @@ -8,7 +8,7 @@ * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. */ -package net.corda.serialization.internal.kryo +package net.corda.node.serialization.kryo import co.paralleluniverse.fibers.Fiber import co.paralleluniverse.io.serialization.kryo.KryoSerializer @@ -22,10 +22,10 @@ import com.esotericsoftware.kryo.serializers.ClosureSerializer import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.SerializedBytes import net.corda.core.utilities.ByteSequence import net.corda.serialization.internal.* -import net.corda.serialization.internal.SectionId import java.security.PublicKey import java.util.concurrent.ConcurrentHashMap @@ -140,3 +140,14 @@ abstract class AbstractKryoSerializationScheme : SerializationScheme { } } } + +val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl( + kryoMagic, + SerializationDefaults.javaClass.classLoader, + QuasarWhitelist, + emptyMap(), + true, + SerializationContext.UseCase.Checkpoint, + null, + AlwaysAcceptEncodingWhitelist +) diff --git a/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoServerSerializationScheme.kt b/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoServerSerializationScheme.kt index 5d8fa132fe..088ff88cde 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoServerSerializationScheme.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoServerSerializationScheme.kt @@ -13,8 +13,6 @@ package net.corda.node.serialization.kryo import com.esotericsoftware.kryo.pool.KryoPool import net.corda.core.serialization.SerializationContext import net.corda.serialization.internal.CordaSerializationMagic -import net.corda.serialization.internal.kryo.AbstractKryoSerializationScheme -import net.corda.serialization.internal.kryo.kryoMagic class KryoServerSerializationScheme : AbstractKryoSerializationScheme() { override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean { @@ -22,7 +20,5 @@ class KryoServerSerializationScheme : AbstractKryoSerializationScheme() { } override fun rpcClientKryoPool(context: SerializationContext): KryoPool = throw UnsupportedOperationException() - override fun rpcServerKryoPool(context: SerializationContext): KryoPool = throw UnsupportedOperationException() - -} \ No newline at end of file +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/KryoStreams.kt b/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoStreams.kt similarity index 97% rename from serialization/src/main/kotlin/net/corda/serialization/internal/kryo/KryoStreams.kt rename to node/src/main/kotlin/net/corda/node/serialization/kryo/KryoStreams.kt index d1d28d56ff..b10bffbeac 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/KryoStreams.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoStreams.kt @@ -10,7 +10,7 @@ @file:JvmName("KryoStreams") -package net.corda.serialization.internal.kryo +package net.corda.node.serialization.kryo import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/SerializeAsTokenSerializer.kt b/node/src/main/kotlin/net/corda/node/serialization/kryo/SerializeAsTokenSerializer.kt similarity index 97% rename from serialization/src/main/kotlin/net/corda/serialization/internal/kryo/SerializeAsTokenSerializer.kt rename to node/src/main/kotlin/net/corda/node/serialization/kryo/SerializeAsTokenSerializer.kt index 6858c8b021..5918cc3e65 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/kryo/SerializeAsTokenSerializer.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/kryo/SerializeAsTokenSerializer.kt @@ -8,7 +8,7 @@ * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. */ -package net.corda.serialization.internal.kryo +package net.corda.node.serialization.kryo import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.KryoException diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index f9803a2820..11b8d8e213 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -15,7 +15,6 @@ import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory.systemEnvironment import com.typesafe.config.ConfigFactory.systemProperties import com.typesafe.config.ConfigParseOptions -import com.typesafe.config.ConfigRenderOptions import net.corda.core.identity.CordaX500Name import net.corda.core.internal.createDirectories import net.corda.core.internal.div @@ -64,8 +63,6 @@ object ConfigHelper { .withFallback(defaultConfig) .resolve() - log.info("Config:\n${finalConfig.root().render(ConfigRenderOptions.defaults())}") - val entrySet = finalConfig.entrySet().filter { entry -> entry.key.contains("\"") } for ((key) in entrySet) { log.error("Config files should not contain \" in property names. Please fix: $key") diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index a9e16f4da2..942638dc40 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -34,7 +34,7 @@ import javax.annotation.concurrent.ThreadSafe */ // TODO There is duplicated logic between this and PersistentIdentityService @ThreadSafe -class InMemoryIdentityService(identities: Array, +class InMemoryIdentityService(identities: List = emptyList(), override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityService { companion object { private val log = contextLogger() 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 bf7abd3608..acae901c5f 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 @@ -38,10 +38,17 @@ import javax.persistence.Entity import javax.persistence.Id import javax.persistence.Lob +/** + * An identity service that stores parties and their identities to a key value tables in the database. The entries are + * cached for efficient lookup. + * + * @param trustRoot certificate from the zone operator for identity on the network. + * @param caCertificates list of additional certificates. + */ // TODO There is duplicated logic between this and InMemoryIdentityService @ThreadSafe class PersistentIdentityService(override val trustRoot: X509Certificate, - vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal { + caCertificates: List = emptyList()) : SingletonSerializeAsToken(), IdentityServiceInternal { companion object { private val log = contextLogger() 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 d60edf0051..90314a7555 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 @@ -45,6 +45,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, private val fileWatcher: NodeInfoWatcher, private val networkMapClient: NetworkMapClient?, private val currentParametersHash: SecureHash, + private val ourNodeInfoHash: SecureHash?, private val baseDirectory: Path, private val extraNetworkMapKeys: List ) : AutoCloseable { @@ -79,8 +80,10 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, networkMapCache.addNode(it.nodeInfo) } is NodeInfoUpdate.Remove -> { - val nodeInfo = networkMapCache.getNodeByHash(it.hash) - nodeInfo?.let { networkMapCache.removeNode(it) } + if (it.hash != ourNodeInfoHash) { + val nodeInfo = networkMapCache.getNodeByHash(it.hash) + nodeInfo?.let { networkMapCache.removeNode(it) } + } } } } @@ -137,8 +140,11 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, // Remove node info from network map. (currentNodeHashes - allHashesFromNetworkMap - fileWatcher.processedNodeInfoHashes) - .mapNotNull(networkMapCache::getNodeByHash) - .forEach(networkMapCache::removeNode) + .mapNotNull { + if (it != ourNodeInfoHash) { + networkMapCache.getNodeByHash(it) + } else null + }.forEach(networkMapCache::removeNode) return cacheTimeout } diff --git a/node/src/test/kotlin/net/corda/node/internal/serialization/testutils/TestSerializationContext.kt b/node/src/test/kotlin/net/corda/node/internal/serialization/testutils/TestSerializationContext.kt index 5dbf0fa138..c80e176e56 100644 --- a/node/src/test/kotlin/net/corda/node/internal/serialization/testutils/TestSerializationContext.kt +++ b/node/src/test/kotlin/net/corda/node/internal/serialization/testutils/TestSerializationContext.kt @@ -1,9 +1,9 @@ package net.corda.node.internal.serialization.testutils import net.corda.core.serialization.SerializationContext +import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.SerializationContextImpl import net.corda.serialization.internal.amqp.amqpMagic -import net.corda.serialization.internal.AllWhitelist val serializationProperties: MutableMap = mutableMapOf() @@ -14,4 +14,5 @@ val serializationContext = SerializationContextImpl( properties = serializationProperties, objectReferencesEnabled = false, useCase = SerializationContext.UseCase.Testing, - encoding = null) \ No newline at end of file + encoding = null +) diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/kryo/KryoStreamsTest.kt b/node/src/test/kotlin/net/corda/node/serialization/kryo/KryoStreamsTest.kt similarity index 95% rename from serialization/src/test/kotlin/net/corda/serialization/internal/kryo/KryoStreamsTest.kt rename to node/src/test/kotlin/net/corda/node/serialization/kryo/KryoStreamsTest.kt index 1e4fe9a742..48b0702cee 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/kryo/KryoStreamsTest.kt +++ b/node/src/test/kotlin/net/corda/node/serialization/kryo/KryoStreamsTest.kt @@ -8,7 +8,7 @@ * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. */ -package net.corda.serialization.internal.kryo +package net.corda.node.serialization.kryo import net.corda.core.internal.declaredField import net.corda.serialization.internal.ByteBufferOutputStream @@ -38,7 +38,7 @@ class KryoStreamsTest { fun `substitute output works`() { assertArrayEquals(byteArrayOf(100, -101), kryoOutput { write(100) - substitute(::NegOutputStream) + substitute(KryoStreamsTest::NegOutputStream) write(101) }) } @@ -47,7 +47,7 @@ class KryoStreamsTest { fun `substitute input works`() { kryoInput(byteArrayOf(100, 101).inputStream()) { assertEquals(100, read()) - substitute(::NegInputStream) + substitute(KryoStreamsTest::NegInputStream) assertEquals(-101, read().toByte()) assertEquals(-1, read()) } diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/kryo/KryoTests.kt b/node/src/test/kotlin/net/corda/node/serialization/kryo/KryoTests.kt similarity index 97% rename from serialization/src/test/kotlin/net/corda/serialization/internal/kryo/KryoTests.kt rename to node/src/test/kotlin/net/corda/node/serialization/kryo/KryoTests.kt index a69dc79492..91c23c15e1 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/kryo/KryoTests.kt +++ b/node/src/test/kotlin/net/corda/node/serialization/kryo/KryoTests.kt @@ -8,7 +8,7 @@ * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. */ -package net.corda.serialization.internal.kryo +package net.corda.node.serialization.kryo import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.KryoException @@ -43,6 +43,7 @@ import org.slf4j.LoggerFactory import java.io.InputStream import java.time.Instant import java.util.* +import kotlin.collections.ArrayList import kotlin.test.* class TestScheme : AbstractKryoSerializationScheme() { @@ -103,7 +104,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) { fun `serialised form is stable when the same object instance is added to the deserialised object graph`() { val noReferencesContext = context.withoutReferences() val obj : ByteSequence = Ints.toByteArray(0x01234567).sequence() - val originalList : ArrayList = arrayListOf(obj) + val originalList : ArrayList = ArrayList().apply { this += obj } val deserialisedList = originalList.serialize(factory, noReferencesContext).deserialize(factory, noReferencesContext) originalList += obj deserialisedList += obj @@ -116,8 +117,14 @@ class KryoTests(private val compression: CordaSerializationEncoding?) { val instant = Instant.ofEpochMilli(123) val instantCopy = Instant.ofEpochMilli(123) assertThat(instant).isNotSameAs(instantCopy) - val listWithCopies = arrayListOf(instant, instantCopy) - val listWithSameInstances = arrayListOf(instant, instant) + val listWithCopies = ArrayList().apply { + this += instant + this += instantCopy + } + val listWithSameInstances = ArrayList().apply { + this += instant + this += instant + } assertThat(listWithSameInstances.serialize(factory, noReferencesContext)).isEqualTo(listWithCopies.serialize(factory, noReferencesContext)) } diff --git a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt index 2da3b18313..aea0ce9a7a 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt @@ -42,7 +42,7 @@ class InMemoryIdentityServiceTests { val BOB get() = bob.party val BOB_IDENTITY get() = bob.identity val BOB_PUBKEY get() = bob.publicKey - fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate) + fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities.toList(), DEV_ROOT_CA.certificate) } @Rule diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt index 56684fd94b..78c7d4b780 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt @@ -15,6 +15,7 @@ import com.google.common.jimfs.Jimfs import com.nhaarman.mockito_kotlin.* import net.corda.cordform.CordformNode.NODE_INFO_DIRECTORY import net.corda.core.crypto.Crypto +import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sign import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -71,7 +72,6 @@ class NetworkMapUpdaterTest { server = NetworkMapServer(cacheExpiryMs.millis, PortAllocation.Incremental(10000).nextHostAndPort()) val hostAndPort = server.start() networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_ROOT_CA.certificate) - updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient, server.networkParameters.serialize().hash, baseDir, listOf(privateNetUUID)) } @After @@ -81,8 +81,13 @@ class NetworkMapUpdaterTest { server.close() } + private fun setUpdater(ourNodeHash: SecureHash? = null, extraNetworkMapKeys: List = emptyList()) { + updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient, server.networkParameters.serialize().hash, ourNodeHash, baseDir, extraNetworkMapKeys) + } + @Test fun `process add node updates from network map, with additional node infos from dir`() { + setUpdater() val (nodeInfo1, signedNodeInfo1) = createNodeInfoAndSigned("Info 1") val (nodeInfo2, signedNodeInfo2) = createNodeInfoAndSigned("Info 2") val (nodeInfo3, signedNodeInfo3) = createNodeInfoAndSigned("Info 3") @@ -118,6 +123,7 @@ class NetworkMapUpdaterTest { @Test fun `process remove node updates from network map, with additional node infos from dir`() { + setUpdater() val (nodeInfo1, signedNodeInfo1) = createNodeInfoAndSigned("Info 1") val (nodeInfo2, signedNodeInfo2) = createNodeInfoAndSigned("Info 2") val (nodeInfo3, signedNodeInfo3) = createNodeInfoAndSigned("Info 3") @@ -158,6 +164,7 @@ class NetworkMapUpdaterTest { @Test fun `receive node infos from directory, without a network map`() { + setUpdater() val fileNodeInfoAndSigned = createNodeInfoAndSigned("Info from file") // Not subscribed yet. @@ -176,6 +183,7 @@ class NetworkMapUpdaterTest { @Test fun `emit new parameters update info on parameters update from network map`() { + setUpdater() val paramsFeed = updater.trackParametersUpdate() val snapshot = paramsFeed.snapshot val updates = paramsFeed.updates.bufferUntilSubscribed() @@ -198,6 +206,7 @@ class NetworkMapUpdaterTest { @Test fun `ack network parameters update`() { + setUpdater() val newParameters = testNetworkParameters(epoch = 314) server.scheduleParametersUpdate(newParameters, "Test update", Instant.MIN) updater.subscribeToNetworkMap() @@ -214,6 +223,7 @@ class NetworkMapUpdaterTest { @Test fun `fetch nodes from private network`() { + setUpdater(extraNetworkMapKeys = listOf(privateNetUUID)) server.addNodesToPrivateNetwork(privateNetUUID, listOf(ALICE_NAME)) Assertions.assertThatThrownBy { networkMapClient.getNetworkMap(privateNetUUID).payload.nodeInfoHashes } .isInstanceOf(IOException::class.java) @@ -230,6 +240,7 @@ class NetworkMapUpdaterTest { @Test fun `remove node from filesystem deletes it from network map cache`() { + setUpdater() val fileNodeInfoAndSigned1 = createNodeInfoAndSigned("Info from file 1") val fileNodeInfoAndSigned2 = createNodeInfoAndSigned("Info from file 2") updater.subscribeToNetworkMap() @@ -252,6 +263,7 @@ class NetworkMapUpdaterTest { @Test fun `remove node info file, but node in network map server`() { + setUpdater() val nodeInfoBuilder = TestNodeInfoBuilder() val (_, key) = nodeInfoBuilder.addLegalIdentity(CordaX500Name("Info", "London", "GB")) val (serverNodeInfo, serverSignedNodeInfo) = nodeInfoBuilder.buildWithSigned(1, 1) @@ -280,6 +292,23 @@ class NetworkMapUpdaterTest { assertThat(networkMapCache.allNodeHashes).containsOnly(serverSignedNodeInfo.raw.hash) } + // Test fix for ENT-1882 + // This scenario can happen when signing of network map server is performed much longer after the node joined the network. + // Network map will advertise hashes without that node. + @Test + fun `not remove own node info when it is not in network map yet`() { + val (myInfo, signedMyInfo) = createNodeInfoAndSigned("My node info") + val (_, signedOtherInfo) = createNodeInfoAndSigned("Other info") + setUpdater(ourNodeHash = signedMyInfo.raw.hash) + networkMapCache.addNode(myInfo) // Simulate behaviour on node startup when our node info is added to cache + networkMapClient.publish(signedOtherInfo) + updater.subscribeToNetworkMap() + Thread.sleep(2L * cacheExpiryMs) + verify(networkMapCache, never()).removeNode(myInfo) + assertThat(server.networkMapHashes()).containsOnly(signedOtherInfo.raw.hash) + assertThat(networkMapCache.allNodeHashes).containsExactlyInAnyOrder(signedMyInfo.raw.hash, signedOtherInfo.raw.hash) + } + private fun createMockNetworkMapCache(): NetworkMapCacheInternal { return mock { val data = ConcurrentHashMap() diff --git a/serialization/build.gradle b/serialization/build.gradle index 6edf7fc75e..48fa7dc6c0 100644 --- a/serialization/build.gradle +++ b/serialization/build.gradle @@ -12,9 +12,6 @@ dependencies { compile "org.apache.activemq:artemis-commons:${artemis_version}" - // Kryo: object graph serialization. - compile "com.esotericsoftware:kryo:4.0.0" - compile "de.javakaffee:kryo-serializers:0.41" compile "org.ow2.asm:asm:$asm_version" // For AMQP serialisation. diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/ByteBufferStreams.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/ByteBufferStreams.kt index 4dddcf8c52..a37ced49e2 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/ByteBufferStreams.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/ByteBufferStreams.kt @@ -15,7 +15,7 @@ internal val serializeOutputStreamPool = LazyPool( shouldReturnToPool = { it.size() < 256 * 1024 }, // Discard if it grew too large newInstance = { ByteBufferOutputStream(64 * 1024) }) -internal fun byteArrayOutput(task: (ByteBufferOutputStream) -> T): ByteArray { +fun byteArrayOutput(task: (ByteBufferOutputStream) -> T): ByteArray { return serializeOutputStreamPool.run { underlying -> task(underlying) underlying.toByteArray() // Must happen after close, to allow ZIP footer to be written for example. diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/ClassWhitelists.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/ClassWhitelists.kt index fd89209380..5d647d4f17 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/ClassWhitelists.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/ClassWhitelists.kt @@ -1,6 +1,7 @@ package net.corda.serialization.internal import net.corda.core.serialization.ClassWhitelist +import java.util.* interface MutableClassWhitelist : ClassWhitelist { fun add(entry: Class<*>) @@ -9,3 +10,41 @@ interface MutableClassWhitelist : ClassWhitelist { object AllWhitelist : ClassWhitelist { override fun hasListed(type: Class<*>): Boolean = true } + +class BuiltInExceptionsWhitelist : ClassWhitelist { + companion object { + private val packageName = "^(?:java|kotlin)(?:[.]|$)".toRegex() + } + + override fun hasListed(type: Class<*>): Boolean { + return Throwable::class.java.isAssignableFrom(type) && packageName.containsMatchIn(type.`package`.name) + } +} + +sealed class AbstractMutableClassWhitelist(private val whitelist: MutableSet, private val delegate: ClassWhitelist) : MutableClassWhitelist { + override fun hasListed(type: Class<*>): Boolean { + /** + * There are certain delegates like [net.corda.serialization.internal.AllButBlacklisted] + * which may throw when asked whether the type is listed. + * In such situations - it may be a good idea to ask [delegate] first before making a check against own [whitelist]. + */ + return delegate.hasListed(type) || (type.name in whitelist) + } + + override fun add(entry: Class<*>) { + whitelist += entry.name + } +} + +/** + * A whitelist that can be customised via the [net.corda.core.serialization.SerializationWhitelist], + * since it implements [MutableClassWhitelist]. + */ +class TransientClassWhiteList(delegate: ClassWhitelist) : AbstractMutableClassWhitelist(Collections.synchronizedSet(mutableSetOf()), delegate) + +// TODO: Need some concept of from which class loader +class GlobalTransientClassWhiteList(delegate: ClassWhitelist) : AbstractMutableClassWhitelist(whitelist, delegate) { + companion object { + private val whitelist: MutableSet = Collections.synchronizedSet(mutableSetOf()) + } +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/ClientContexts.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/ClientContexts.kt index 64f1d1c341..1fafa7ad29 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/ClientContexts.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/ClientContexts.kt @@ -15,9 +15,6 @@ package net.corda.serialization.internal import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationDefaults import net.corda.serialization.internal.amqp.amqpMagic -import net.corda.serialization.internal.kryo.BuiltInExceptionsWhitelist -import net.corda.serialization.internal.kryo.GlobalTransientClassWhiteList -import net.corda.serialization.internal.kryo.kryoMagic /* * Serialisation contexts for the client. @@ -26,10 +23,12 @@ import net.corda.serialization.internal.kryo.kryoMagic */ -val AMQP_RPC_CLIENT_CONTEXT = SerializationContextImpl(amqpMagic, +val AMQP_RPC_CLIENT_CONTEXT = SerializationContextImpl( + amqpMagic, SerializationDefaults.javaClass.classLoader, GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), emptyMap(), true, SerializationContext.UseCase.RPCClient, - null) + null +) 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 a3a005c440..7e9007e88d 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt @@ -10,7 +10,6 @@ package net.corda.serialization.internal -import com.esotericsoftware.kryo.KryoException import net.corda.core.serialization.SerializationWhitelist import net.corda.core.utilities.NetworkHostAndPort import org.apache.activemq.artemis.api.core.SimpleString @@ -40,7 +39,6 @@ object DefaultWhitelist : SerializationWhitelist { mapOf(Unit to Unit).javaClass, // SingletonMap NetworkHostAndPort::class.java, SimpleString::class.java, - KryoException::class.java, // TODO: Will be removed when we migrate away from Kryo StringBuffer::class.java, Unit::class.java, java.io.ByteArrayInputStream::class.java, diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationFormat.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationFormat.kt index b7dc2035d7..bb9de04eaf 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationFormat.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationFormat.kt @@ -10,7 +10,6 @@ package net.corda.serialization.internal -import net.corda.core.internal.VisibleForTesting import net.corda.core.serialization.SerializationEncoding import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.OpaqueBytes @@ -64,5 +63,4 @@ enum class CordaSerializationEncoding : SerializationEncoding, OrdinalWriter { abstract fun wrap(stream: InputStream): InputStream } -@VisibleForTesting -internal val encodingNotPermittedFormat = "Encoding not permitted: %s" +const val encodingNotPermittedFormat = "Encoding not permitted: %s" 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 19a360b388..fe9482296d 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt @@ -18,7 +18,6 @@ import net.corda.core.internal.copyBytes import net.corda.core.serialization.* import net.corda.core.utilities.ByteSequence import net.corda.serialization.internal.amqp.amqpMagic -import net.corda.serialization.internal.kryo.kryoMagic import org.slf4j.LoggerFactory import java.io.NotSerializableException import java.util.* @@ -108,7 +107,7 @@ open class SerializationFactoryImpl( constructor() : this(ConcurrentHashMap()) companion object { - val magicSize = sequenceOf(kryoMagic, amqpMagic).map { it.size }.distinct().single() + val magicSize = amqpMagic.size } private val creator: List = Exception().stackTrace.asList() @@ -124,7 +123,7 @@ open class SerializationFactoryImpl( return schemes.computeIfAbsent(lookupKey) { registeredSchemes.filter { it.canDeserializeVersion(magic, target) }.forEach { return@computeIfAbsent it } // XXX: Not single? logger.warn("Cannot find serialization scheme for: [$lookupKey, " + - "${if (magic == amqpMagic) "AMQP" else if (magic == kryoMagic) "Kryo" else "UNKNOWN MAGIC"}] registeredSchemes are: $registeredSchemes") + "${if (magic == amqpMagic) "AMQP" else "UNKNOWN MAGIC"}] registeredSchemes are: $registeredSchemes") throw UnsupportedOperationException("Serialization scheme $lookupKey not supported.") } to magic } @@ -154,15 +153,12 @@ open class SerializationFactoryImpl( registeredSchemes += scheme } - val alreadyRegisteredSchemes: Collection get() = Collections.unmodifiableCollection(registeredSchemes) - override fun toString(): String { return "${this.javaClass.name} registeredSchemes=$registeredSchemes ${creator.joinToString("\n")}" } override fun equals(other: Any?): Boolean { - return other is SerializationFactoryImpl && - other.registeredSchemes == this.registeredSchemes + return other is SerializationFactoryImpl && other.registeredSchemes == this.registeredSchemes } override fun hashCode(): Int = registeredSchemes.hashCode() diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/ServerContexts.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/ServerContexts.kt index c6cb65f40e..1e0e056ddb 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/ServerContexts.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/ServerContexts.kt @@ -15,9 +15,6 @@ package net.corda.serialization.internal import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationDefaults import net.corda.serialization.internal.amqp.amqpMagic -import net.corda.serialization.internal.kryo.BuiltInExceptionsWhitelist -import net.corda.serialization.internal.kryo.GlobalTransientClassWhiteList -import net.corda.serialization.internal.kryo.kryoMagic /* * Serialisation contexts for the server. @@ -30,19 +27,23 @@ import net.corda.serialization.internal.kryo.kryoMagic */ -val AMQP_STORAGE_CONTEXT = SerializationContextImpl(amqpMagic, +val AMQP_STORAGE_CONTEXT = SerializationContextImpl( + amqpMagic, SerializationDefaults.javaClass.classLoader, AllButBlacklisted, emptyMap(), true, SerializationContext.UseCase.Storage, null, - AlwaysAcceptEncodingWhitelist) + AlwaysAcceptEncodingWhitelist +) -val AMQP_RPC_SERVER_CONTEXT = SerializationContextImpl(amqpMagic, +val AMQP_RPC_SERVER_CONTEXT = SerializationContextImpl( + amqpMagic, SerializationDefaults.javaClass.classLoader, GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), emptyMap(), true, SerializationContext.UseCase.RPCServer, - null) + null +) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/SharedContexts.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/SharedContexts.kt index 75b0f05261..87fde07b73 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/SharedContexts.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/SharedContexts.kt @@ -15,35 +15,18 @@ package net.corda.serialization.internal import net.corda.core.serialization.* import net.corda.serialization.internal.CordaSerializationEncoding.SNAPPY import net.corda.serialization.internal.amqp.amqpMagic -import net.corda.serialization.internal.kryo.BuiltInExceptionsWhitelist -import net.corda.serialization.internal.kryo.GlobalTransientClassWhiteList -import net.corda.serialization.internal.kryo.kryoMagic -/* - * Serialisation contexts shared by the server and client. - * - * NOTE: The [KRYO_STORAGE_CONTEXT] and [AMQP_STORAGE_CONTEXT] - * CANNOT always be instantiated outside of the server and so - * MUST be kept separate from these ones! - */ -val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(kryoMagic, - SerializationDefaults.javaClass.classLoader, - QuasarWhitelist, - emptyMap(), - true, - SerializationContext.UseCase.Checkpoint, - SNAPPY, - AlwaysAcceptEncodingWhitelist) - -val AMQP_P2P_CONTEXT = SerializationContextImpl(amqpMagic, +val AMQP_P2P_CONTEXT = SerializationContextImpl( + amqpMagic, SerializationDefaults.javaClass.classLoader, GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), emptyMap(), true, SerializationContext.UseCase.P2P, - null) + null +) -internal object AlwaysAcceptEncodingWhitelist : EncodingWhitelist { +object AlwaysAcceptEncodingWhitelist : EncodingWhitelist { override fun acceptEncoding(encoding: SerializationEncoding) = true } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/UseCaseAwareness.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/UseCaseAwareness.kt index bc7d408c75..43f64d3823 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/UseCaseAwareness.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/UseCaseAwareness.kt @@ -14,10 +14,10 @@ import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationFactory import java.util.* -internal fun checkUseCase(allowedUseCases: EnumSet) { +fun checkUseCase(allowedUseCases: EnumSet) { val currentContext: SerializationContext = SerializationFactory.currentFactory?.currentContext ?: throw IllegalStateException("Current context is not set") if (!allowedUseCases.contains(currentContext.useCase)) { throw IllegalStateException("UseCase '${currentContext.useCase}' is not within '$allowedUseCases'") } -} \ No newline at end of file +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt index 8debf5936d..21c6cba0f4 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt @@ -18,10 +18,7 @@ import net.corda.core.internal.objectOrNewInstance import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.* import net.corda.core.utilities.ByteSequence -import net.corda.serialization.internal.CordaSerializationMagic -import net.corda.serialization.internal.DefaultWhitelist -import net.corda.serialization.internal.MutableClassWhitelist -import net.corda.serialization.internal.SerializationScheme +import net.corda.serialization.internal.* import java.lang.reflect.Modifier import java.util.* import java.util.concurrent.ConcurrentHashMap diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/MapSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/MapSerializer.kt index 70f84cebb9..c5236e2382 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/MapSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/MapSerializer.kt @@ -124,14 +124,33 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial } internal fun Class<*>.checkSupportedMapType() { + checkHashMap() + checkWeakHashMap() + checkDictionary() +} + +private fun Class<*>.checkHashMap() { if (HashMap::class.java.isAssignableFrom(this) && !LinkedHashMap::class.java.isAssignableFrom(this)) { throw IllegalArgumentException( "Map type $this is unstable under iteration. Suggested fix: use java.util.LinkedHashMap instead.") - } else if (WeakHashMap::class.java.isAssignableFrom(this)) { + } +} + +/** + * The [WeakHashMap] class does not exist within the DJVM, and so we need + * to isolate this reference. + */ +private fun Class<*>.checkWeakHashMap() { + if (WeakHashMap::class.java.isAssignableFrom(this)) { throw IllegalArgumentException("Weak references with map types not supported. Suggested fix: " + "use java.util.LinkedHashMap instead.") - } else if (Dictionary::class.java.isAssignableFrom(this)) { + } +} + +private fun Class<*>.checkDictionary() { + if (Dictionary::class.java.isAssignableFrom(this)) { throw IllegalArgumentException( "Unable to serialise deprecated type $this. Suggested fix: prefer java.util.map implementations") } -} \ No newline at end of file +} + diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/RxNotificationSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/RxNotificationSerializer.kt similarity index 93% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/RxNotificationSerializer.kt rename to serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/RxNotificationSerializer.kt index 36250827f3..a5e346f029 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/RxNotificationSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/RxNotificationSerializer.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal.serialization.amqp.custom +package net.corda.serialization.internal.amqp.custom import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory diff --git a/serialization/src/test/java/net/corda/serialization/internal/LambdaCheckpointSerializationTest.java b/serialization/src/test/java/net/corda/serialization/internal/LambdaCheckpointSerializationTest.java index 49429942d2..7374e917ac 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/LambdaCheckpointSerializationTest.java +++ b/serialization/src/test/java/net/corda/serialization/internal/LambdaCheckpointSerializationTest.java @@ -10,18 +10,18 @@ package net.corda.serialization.internal; -import com.google.common.collect.Maps; import net.corda.core.serialization.SerializationContext; import net.corda.core.serialization.SerializationFactory; import net.corda.core.serialization.SerializedBytes; +import net.corda.node.serialization.kryo.CordaClosureSerializer; +import net.corda.node.serialization.kryo.KryoSerializationSchemeKt; import net.corda.testing.core.SerializationEnvironmentRule; -import net.corda.serialization.internal.kryo.CordaClosureSerializer; -import net.corda.serialization.internal.kryo.KryoSerializationSchemeKt; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import java.io.Serializable; +import java.util.Collections; import java.util.concurrent.Callable; import static org.assertj.core.api.Assertions.assertThat; @@ -36,7 +36,15 @@ public final class LambdaCheckpointSerializationTest { @Before public void setup() { factory = testSerialization.getSerializationFactory(); - context = new SerializationContextImpl(KryoSerializationSchemeKt.getKryoMagic(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, SerializationContext.UseCase.Checkpoint, null); + context = new SerializationContextImpl( + KryoSerializationSchemeKt.getKryoMagic(), + getClass().getClassLoader(), + AllWhitelist.INSTANCE, + Collections.emptyMap(), + true, + SerializationContext.UseCase.Checkpoint, + null + ); } @Test diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt index 65f810a3a6..06e36c5c57 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt @@ -24,9 +24,9 @@ import net.corda.core.node.services.AttachmentStorage import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationContext -import net.corda.serialization.internal.kryo.CordaClassResolver -import net.corda.serialization.internal.kryo.CordaKryo -import net.corda.serialization.internal.kryo.kryoMagic +import net.corda.node.serialization.kryo.CordaClassResolver +import net.corda.node.serialization.kryo.CordaKryo +import net.corda.node.serialization.kryo.kryoMagic import net.corda.testing.internal.rigorousMock import net.corda.testing.services.MockAttachmentStorage import org.junit.Rule diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/ListsSerializationTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/ListsSerializationTest.kt index 4fefc4fe17..a57604f88b 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/ListsSerializationTest.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/ListsSerializationTest.kt @@ -13,11 +13,11 @@ package net.corda.serialization.internal import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.util.DefaultClassResolver import net.corda.core.serialization.* +import net.corda.node.serialization.kryo.kryoMagic import net.corda.node.services.statemachine.DataSessionMessage import net.corda.serialization.internal.amqp.DeserializationInput import net.corda.serialization.internal.amqp.Envelope import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.kryo.kryoMagic import net.corda.testing.internal.amqpSpecific import net.corda.testing.internal.kryoSpecific import net.corda.testing.core.SerializationEnvironmentRule diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/MapsSerializationTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/MapsSerializationTest.kt index 31239b2ff2..536f84252b 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/MapsSerializationTest.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/MapsSerializationTest.kt @@ -16,8 +16,8 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize +import net.corda.node.serialization.kryo.kryoMagic import net.corda.node.services.statemachine.DataSessionMessage -import net.corda.serialization.internal.kryo.kryoMagic import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.amqpSpecific import net.corda.testing.internal.kryoSpecific diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt index 1bbc19663f..729fb74209 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt @@ -15,10 +15,10 @@ import com.esotericsoftware.kryo.KryoException import com.esotericsoftware.kryo.io.Output import net.corda.core.serialization.* import net.corda.core.utilities.OpaqueBytes -import net.corda.serialization.internal.kryo.CordaClassResolver -import net.corda.serialization.internal.kryo.CordaKryo -import net.corda.serialization.internal.kryo.DefaultKryoCustomizer -import net.corda.serialization.internal.kryo.kryoMagic +import net.corda.node.serialization.kryo.CordaClassResolver +import net.corda.node.serialization.kryo.CordaKryo +import net.corda.node.serialization.kryo.DefaultKryoCustomizer +import net.corda.node.serialization.kryo.kryoMagic import net.corda.testing.internal.rigorousMock import net.corda.testing.core.SerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt index 59b85939ab..1a82ab8276 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt @@ -14,8 +14,8 @@ import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.util.DefaultClassResolver import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize +import net.corda.node.serialization.kryo.kryoMagic import net.corda.node.services.statemachine.DataSessionMessage -import net.corda.serialization.internal.kryo.kryoMagic import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.kryoSpecific import org.junit.Assert.assertArrayEquals diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationSchemaTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationSchemaTests.kt index 6d3e5398a5..7f71b60ae6 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationSchemaTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationSchemaTests.kt @@ -3,8 +3,8 @@ package net.corda.serialization.internal.amqp import net.corda.core.serialization.* import net.corda.core.utilities.ByteSequence import net.corda.serialization.internal.* -import net.corda.serialization.internal.kryo.BuiltInExceptionsWhitelist -import net.corda.serialization.internal.kryo.GlobalTransientClassWhiteList +import net.corda.serialization.internal.BuiltInExceptionsWhitelist +import net.corda.serialization.internal.GlobalTransientClassWhiteList import org.junit.Test import java.util.concurrent.ConcurrentHashMap import kotlin.test.assertEquals 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 d04f337729..8b0e4cf35c 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 @@ -59,7 +59,7 @@ import java.util.* /** * Returns a simple [InMemoryIdentityService] containing the supplied [identities]. */ -fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate) +fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities.toList(), DEV_ROOT_CA.certificate) /** * An implementation of [ServiceHub] that is designed for in-memory unit tests of contract validation logic. It has diff --git a/testing/test-common/src/main/resources/log4j2-test.xml b/testing/test-common/src/main/resources/log4j2-test.xml index 6e8070e750..910ad4e00c 100644 --- a/testing/test-common/src/main/resources/log4j2-test.xml +++ b/testing/test-common/src/main/resources/log4j2-test.xml @@ -15,11 +15,11 @@ - + - + @@ -32,8 +32,8 @@ - 
 - 
 + + diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalSerializationTestHelpers.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalSerializationTestHelpers.kt index e3d9027ad8..9762e2a8c1 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalSerializationTestHelpers.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalSerializationTestHelpers.kt @@ -16,6 +16,7 @@ import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationS import net.corda.core.DoNotImplement import net.corda.core.serialization.internal.* import net.corda.node.serialization.amqp.AMQPServerSerializationScheme +import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT import net.corda.node.serialization.kryo.KryoServerSerializationScheme import net.corda.serialization.internal.* import net.corda.testing.core.SerializationEnvironmentRule diff --git a/tools/explorer/src/main/resources/log4j2.xml b/tools/explorer/src/main/resources/log4j2.xml index 636180175a..2511e86540 100644 --- a/tools/explorer/src/main/resources/log4j2.xml +++ b/tools/explorer/src/main/resources/log4j2.xml @@ -18,7 +18,7 @@ - + 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 4c21a08243..8daf7ad837 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 @@ -46,7 +46,7 @@ class InteractiveShellTest { override fun call() = a } - private val ids = InMemoryIdentityService(arrayOf(megaCorp.identity), DEV_ROOT_CA.certificate) + private val ids = InMemoryIdentityService(listOf(megaCorp.identity), DEV_ROOT_CA.certificate) @Suppress("DEPRECATION") private val om = JacksonSupport.createInMemoryMapper(ids, YAMLFactory())