diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 56123e9a97..79812632d4 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -2523,6 +2523,8 @@ public class net.corda.core.flows.DataVendingFlow extends net.corda.core.flows.F @Nullable public Void call() @NotNull + public final java.util.Set getOtherSessions() + @NotNull public final net.corda.core.flows.FlowSession getOtherSideSession() @NotNull public final Object getPayload() @@ -8048,6 +8050,8 @@ public static final class net.corda.testing.core.TestIdentity$Companion extends public final net.corda.testing.core.TestIdentity fresh(String, net.corda.core.crypto.SignatureScheme) ## public final class net.corda.testing.core.TestUtils extends java.lang.Object + @NotNull + public static final java.security.cert.X509CRL createCRL(net.corda.nodeapi.internal.crypto.CertificateAndKeyPair, java.util.List, java.net.URI, java.time.Instant, java.time.Instant, boolean, java.time.Instant, int, String) public static final T executeTest(java.time.Duration, kotlin.jvm.functions.Function0, java.time.Duration, kotlin.jvm.functions.Function0) @NotNull public static final net.corda.core.utilities.NetworkHostAndPort freeLocalHostAndPort() @@ -8380,10 +8384,12 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, int, kotlin.jvm.internal.DefaultConstructorMarker) public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection) public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, int, kotlin.jvm.internal.DefaultConstructorMarker) - public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.nio.file.Path, java.util.List, java.util.Map, boolean) - public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.nio.file.Path, java.util.List, java.util.Map, boolean, int, kotlin.jvm.internal.DefaultConstructorMarker) - public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.nio.file.Path, java.util.List, java.util.Map, boolean, boolean) - public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.nio.file.Path, java.util.List, java.util.Map, boolean, boolean, int, kotlin.jvm.internal.DefaultConstructorMarker) + public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.util.Map, boolean) + public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.util.Map, boolean, int, kotlin.jvm.internal.DefaultConstructorMarker) + public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.util.Map, boolean, boolean) + public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.util.Map, boolean, boolean, int, kotlin.jvm.internal.DefaultConstructorMarker) + public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.util.Map, boolean, boolean, java.time.Duration) + public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.util.Map, boolean, boolean, java.time.Duration, int, kotlin.jvm.internal.DefaultConstructorMarker) public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean) public final boolean component1() @NotNull @@ -8397,16 +8403,14 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O public final boolean component14() @Nullable public final java.util.Collection component15() - @Nullable - public final java.nio.file.Path component16() @NotNull - public final java.util.List component17() + public final java.util.Map component16() + public final boolean component17() + public final boolean component18() @NotNull - public final java.util.Map component18() - public final boolean component19() + public final java.time.Duration component19() @NotNull public final java.nio.file.Path component2() - public final boolean component20() @NotNull public final net.corda.testing.driver.PortAllocation component3() @NotNull @@ -8423,9 +8427,11 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O @NotNull public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection) @NotNull - public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.nio.file.Path, java.util.List, java.util.Map, boolean) + public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.util.Map, boolean) @NotNull - public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.nio.file.Path, java.util.List, java.util.Map, boolean, boolean) + public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.util.Map, boolean, boolean) + @NotNull + public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, java.util.Collection, java.util.Map, boolean, boolean, java.time.Duration) @NotNull public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Set) public boolean equals(Object) @@ -8434,10 +8440,6 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O public final java.util.Collection getCordappsForAllNodes() @NotNull public final net.corda.testing.driver.PortAllocation getDebugPortAllocation() - @Nullable - public final java.nio.file.Path getDjvmBootstrapSource() - @NotNull - public final java.util.List getDjvmCordaSource() @NotNull public final java.nio.file.Path getDriverDirectory() @NotNull @@ -8452,6 +8454,8 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O @NotNull public final java.util.Map getNotaryCustomOverrides() @NotNull + public final java.time.Duration getNotaryHandleTimeout() + @NotNull public final java.util.List getNotarySpecs() @NotNull public final net.corda.testing.driver.PortAllocation getPortAllocation() @@ -8472,10 +8476,6 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O @NotNull public final net.corda.testing.driver.DriverParameters withDebugPortAllocation(net.corda.testing.driver.PortAllocation) @NotNull - public final net.corda.testing.driver.DriverParameters withDjvmBootstrapSource(java.nio.file.Path) - @NotNull - public final net.corda.testing.driver.DriverParameters withDjvmCordaSource(java.util.List) - @NotNull public final net.corda.testing.driver.DriverParameters withDriverDirectory(java.nio.file.Path) @NotNull public final net.corda.testing.driver.DriverParameters withEnvironmentVariables(java.util.Map) @@ -8492,6 +8492,8 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O @NotNull public final net.corda.testing.driver.DriverParameters withNotaryCustomOverrides(java.util.Map) @NotNull + public final net.corda.testing.driver.DriverParameters withNotaryHandleTimeout(java.time.Duration) + @NotNull public final net.corda.testing.driver.DriverParameters withNotarySpecs(java.util.List) @NotNull public final net.corda.testing.driver.DriverParameters withPortAllocation(net.corda.testing.driver.PortAllocation) diff --git a/.ci/dev/forward-merge/Jenkinsfile b/.ci/dev/forward-merge/Jenkinsfile index d62431bb31..f66ba5aaed 100644 --- a/.ci/dev/forward-merge/Jenkinsfile +++ b/.ci/dev/forward-merge/Jenkinsfile @@ -13,13 +13,13 @@ * the branch name of origin branch, it should match the current branch * and it acts as a fail-safe inside {@code forwardMerger} pipeline */ -String originBranch = 'release/os/4.10' +String originBranch = 'release/os/4.11' /** * the branch name of target branch, it should be the branch with the next version * after the one in current branch. */ -String targetBranch = 'release/os/4.11' +String targetBranch = 'release/os/4.12' /** * Forward merge any changes between #originBranch and #targetBranch diff --git a/.ci/dev/pr-code-checks/Jenkinsfile b/.ci/dev/pr-code-checks/Jenkinsfile index 456dc80994..f2996ce6df 100644 --- a/.ci/dev/pr-code-checks/Jenkinsfile +++ b/.ci/dev/pr-code-checks/Jenkinsfile @@ -57,6 +57,7 @@ pipeline { steps { authenticateGradleWrapper() sh 'mkdir -p ${GRADLE_USER_HOME}' + authenticateGradleWrapper() snykDeltaScan(env.SNYK_API_TOKEN, env.C4_OS_SNYK_ORG_ID) } } diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 22525a4375..1f11809533 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,14 +7,6 @@ node-api @rick-r3 node/src/main/kotlin/net/corda/node/internal @rick-r3 node/src/main/kotlin/net/corda/node/services @rick-r3 -# Determinstic components -core-deterministic @rick-r3 -jdk8u-deterministic @rick-r3 -node/djvm @rick-r3 -serialization-deterministic @rick-r3 -serialization-djvm @rick-r3 -serialization-tests @rick-r3 - # Demobench defaults to Chris, but Viktor for the main code tools/demobench @rick-r3 diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml index d578fb899b..3da232efe8 100644 --- a/.github/workflows/check-pr-title.yml +++ b/.github/workflows/check-pr-title.yml @@ -9,6 +9,6 @@ jobs: steps: - uses: morrisoncole/pr-lint-action@v1.7.1 with: - title-regex: '^((CORDA|AG|EG|ENT|INFRA|ES)-\d+|NOTICK)(.*)' + title-regex: '^((CORDA|AG|EG|ENT|INFRA|ES)-\d+)(.*)' on-failed-regex-comment: "PR title failed to match regex -> `%regex%`" repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/jira_assign_issue.yml b/.github/workflows/jira_assign_issue.yml index 7d8b4fba96..9efd0f87a0 100644 --- a/.github/workflows/jira_assign_issue.yml +++ b/.github/workflows/jira_assign_issue.yml @@ -8,12 +8,18 @@ jobs: sync_assigned: runs-on: ubuntu-latest steps: + - name: Generate a token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.AUTH_APP_ID }} + private-key: ${{ secrets.AUTH_APP_PK }} - name: Assign uses: corda/jira-sync-assigned-action@master with: jiraBaseUrl: ${{ secrets.JIRA_BASE_URL }} jiraEmail: ${{ secrets.JIRA_USER_EMAIL }} jiraToken: ${{ secrets.JIRA_API_TOKEN }} - token: ${{ secrets.GH_TOKEN }} + token: ${{ steps.generate_token.outputs.token }} owner: corda repository: corda diff --git a/.github/workflows/jira_close_issue.yml b/.github/workflows/jira_close_issue.yml index dfe3d2443a..00f5b06363 100644 --- a/.github/workflows/jira_close_issue.yml +++ b/.github/workflows/jira_close_issue.yml @@ -8,12 +8,18 @@ jobs: sync_closed: runs-on: ubuntu-latest steps: + - name: Generate a token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.AUTH_APP_ID }} + private-key: ${{ secrets.AUTH_APP_PK }} - name: Close uses: corda/jira-sync-closed-action@master with: jiraBaseUrl: https://r3-cev.atlassian.net jiraEmail: ${{ secrets.JIRA_USER_EMAIL }} jiraToken: ${{ secrets.JIRA_API_TOKEN }} - token: ${{ secrets.GH_TOKEN }} + token: ${{ steps.generate_token.outputs.token }} owner: corda repository: corda diff --git a/.github/workflows/jira_create_issue.yml b/.github/workflows/jira_create_issue.yml index d992fc917e..2932292249 100644 --- a/.github/workflows/jira_create_issue.yml +++ b/.github/workflows/jira_create_issue.yml @@ -10,6 +10,13 @@ jobs: steps: - uses: actions/checkout@v4.1.1 + - name: Generate a token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.AUTH_APP_ID }} + private-key: ${{ secrets.AUTH_APP_PK }} + - name: Jira Create issue id: create uses: corda/jira-create-issue-action@master @@ -30,7 +37,7 @@ jobs: - name: Create comment uses: peter-evans/create-or-update-comment@v4.0.0 with: - token: ${{ secrets.GH_TOKEN }} + token: ${{ steps.generate_token.outputs.token }} issue-number: ${{ github.event.issue.number }} body: | Automatically created Jira issue: ${{ steps.create.outputs.issue }} diff --git a/build.gradle b/build.gradle index d3f1123b70..e3f7a0f271 100644 --- a/build.gradle +++ b/build.gradle @@ -75,8 +75,6 @@ buildscript { ext.disruptor_version = constants.getProperty("disruptorVersion") ext.metrics_version = constants.getProperty("metricsVersion") ext.metrics_new_relic_version = constants.getProperty("metricsNewRelicVersion") - ext.djvm_version = constants.getProperty("djvmVersion") - ext.deterministic_rt_version = constants.getProperty('deterministicRtVersion') ext.okhttp_version = constants.getProperty("okhttpVersion") ext.netty_version = constants.getProperty("nettyVersion") ext.tcnative_version = constants.getProperty("tcnativeVersion") @@ -126,6 +124,8 @@ buildscript { ext.commons_configuration2_version = constants.getProperty("commonsConfiguration2Version") ext.commons_text_version = constants.getProperty("commonsTextVersion") ext.snake_yaml_version = constants.getProperty("snakeYamlVersion") + ext.javaassist_version = constants.getProperty("javaassistVersion") + if (JavaVersion.current().isJava8()) { ext.fontawesomefx_commons_version = constants.getProperty("fontawesomefxCommonsJava8Version") ext.fontawesomefx_fontawesome_version = constants.getProperty("fontawesomefxFontawesomeJava8Version") @@ -134,9 +134,6 @@ buildscript { ext.fontawesomefx_fontawesome_version = constants.getProperty("fontawesomefxFontawesomeVersion") } - // Name of the IntelliJ SDK created for the deterministic Java rt.jar. - // ext.deterministic_idea_sdk = '1.8 (Deterministic)' - // Update 121 is required for ObjectInputFilter. // Updates [131, 161] also have zip compression bugs on MacOS (High Sierra). // when the java version in NodeStartup.hasMinimumJavaVersion() changes, so must this check @@ -615,28 +612,6 @@ task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { it.exists() }) } - afterEvaluate { - classDirectories = files(classDirectories.files.collect { - fileTree(dir: it, - // these exclusions are necessary because jacoco gets confused by same class names - // which occur due to deterministic versions of non deterministic classes - exclude: ['**/net/corda/core/crypto/DigestSupplier**', - '**/net/corda/core/crypto/DelegatingSecureRandomService', - '**/net/corda/core/internal/ThreadLocalToggleField**', - '**/net/corda/core/internal/InheritableThreadLocalToggleField**', - '**/net/corda/core/internal/ToggleField**', - 'net/corda/core/internal/rules/StateContractValidationEnforcementRule**', - 'net/corda/core/internal/SimpleToggleField**', - 'net/corda/core/serialization/SerializationFactory**', - 'net/corda/serialization/internal/amqp/AMQPStreams**', - 'net/corda/serialization/internal/amqp/AMQPSerializerFactories**', - 'net/corda/serialization/internal/amqp/AMQPSerializationThreadContext**', - 'net/corda/serialization/internal/ByteBufferStreams**', - 'net/corda/serialization/internal/model/DefaultCacheProvider**', - 'net/corda/serialization/internal/DefaultWhitelist**' - ]) - }) - } } tasks.register('detekt', JavaExec) { @@ -689,15 +664,11 @@ bintrayConfig { 'corda-mock', 'corda-rpc', 'corda-core', - 'corda-core-deterministic', - 'corda-deterministic-verifier', - 'corda-deserializers-djvm', 'corda', 'corda-finance-workflows', 'corda-finance-contracts', 'corda-node', 'corda-node-api', - 'corda-node-djvm', 'corda-test-common', 'corda-core-test-utils', 'corda-test-utils', @@ -710,8 +681,6 @@ bintrayConfig { 'corda-shell', 'corda-tools-shell-cli', 'corda-serialization', - 'corda-serialization-deterministic', - 'corda-serialization-djvm', 'corda-tools-blob-inspector', 'corda-tools-explorer', 'corda-tools-network-bootstrapper', @@ -837,8 +806,4 @@ distributedTesting { distribution DistributeTestsBy.METHOD } } - - ignoredTests = [ - ':core-deterministic:testing:data:test' - ] } diff --git a/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt b/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt index d3c6b7ee22..cc86ed31ab 100644 --- a/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt +++ b/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt @@ -9,4 +9,4 @@ package net.corda.common.logging * (originally added to source control for ease of use) */ -internal const val CURRENT_MAJOR_RELEASE = "4.10-SNAPSHOT" \ No newline at end of file +internal const val CURRENT_MAJOR_RELEASE = "4.11-SNAPSHOT" \ No newline at end of file diff --git a/constants.properties b/constants.properties index 2874bcc1df..309968d183 100644 --- a/constants.properties +++ b/constants.properties @@ -3,7 +3,7 @@ # their own projects. So don't get fancy with syntax! # Fancy syntax - multi pass ${whatever} replacement -cordaVersion=4.10 +cordaVersion=4.11 versionSuffix=SNAPSHOT gradlePluginsVersion=5.0.12 kotlinVersion=1.2.71 @@ -12,17 +12,18 @@ java8MinUpdateVersion=171 # When incrementing platformVersion make sure to update # # net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. # # ***************************************************************# -platformVersion=12 +platformVersion=13 openTelemetryVersion=1.20.1 openTelemetrySemConvVersion=1.20.1-alpha guavaVersion=28.0-jre # Quasar version to use with Java 8: -quasarVersion=0.7.15_r3 +quasarVersion=0.7.16_r3 # Quasar version to use with Java 11: quasarVersion11=0.8.1_r3 jdkClassifier11=jdk11 dockerJavaVersion=3.2.5 proguardVersion=6.1.1 +// bouncy castle version must not be changed on a patch release. Needs a full release test cycle to flush out any issues. bouncycastleVersion=1.78.1 classgraphVersion=4.8.135 disruptorVersion=3.4.2 @@ -33,8 +34,6 @@ snakeYamlVersion=1.33 caffeineVersion=2.9.3 metricsVersion=4.1.0 metricsNewRelicVersion=1.1.1 -djvmVersion=1.1.1 -deterministicRtVersion=1.0-RC02 openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4 openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4 jolokiaAgentVersion=1.6.1 @@ -77,9 +76,9 @@ mockitoKotlinVersion=1.6.0 hamkrestVersion=1.7.0.0 joptSimpleVersion=5.0.2 jansiVersion=1.18 -hibernateVersion=5.4.32.Final +hibernateVersion=5.6.14.Final # h2Version - Update docs if renamed or removed. -h2Version=1.4.199 +h2Version=2.2.224 rxjavaVersion=1.3.8 dokkaVersion=0.10.1 eddsaVersion=0.3.0 @@ -88,7 +87,7 @@ commonsCollectionsVersion=4.3 beanutilsVersion=1.9.4 shiroVersion=1.10.0 hikariVersion=3.3.1 -liquibaseVersion=3.6.3 +liquibaseVersion=4.20.0 dockerComposeRuleVersion=1.5.0 seleniumVersion=3.141.59 ghostdriverVersion=2.1.0 @@ -107,3 +106,4 @@ fontawesomefxFontawesomeJava8Version=4.7.0-5 # FontAwesomeFX for a more recent version of the Java Runtime (class file version 55.0) fontawesomefxCommonsVersion=11.0 fontawesomefxFontawesomeVersion=4.7.0-11 +javaassistVersion=3.27.0-GA diff --git a/core-deterministic/README.md b/core-deterministic/README.md deleted file mode 100644 index 766d178882..0000000000 --- a/core-deterministic/README.md +++ /dev/null @@ -1,2 +0,0 @@ -## corda-core-deterministic. -This artifact is a deterministic subset of the binary contents of `corda-core`. diff --git a/core-deterministic/build.gradle b/core-deterministic/build.gradle deleted file mode 100644 index ef9bf28633..0000000000 --- a/core-deterministic/build.gradle +++ /dev/null @@ -1,248 +0,0 @@ -import net.corda.gradle.jarfilter.JarFilterTask -import net.corda.gradle.jarfilter.MetaFixerTask -import proguard.gradle.ProGuardTask - -import static org.gradle.api.JavaVersion.VERSION_1_8 - -plugins { - id 'org.jetbrains.kotlin.jvm' - id 'net.corda.plugins.publish-utils' - id 'com.jfrog.artifactory' - id 'java-library' - id 'idea' -} -apply from: "${rootProject.projectDir}/deterministic.gradle" - -description 'Corda core (deterministic)' - -evaluationDependsOn(":core") - -// required by DJVM and Avian JVM (for running inside the SGX enclave) which only supports Java 8. -targetCompatibility = VERSION_1_8 - -def javaHome = System.getProperty('java.home') -def jarBaseName = "corda-${project.name}".toString() - -configurations { - deterministicLibraries { - canBeConsumed = false - extendsFrom api - } - deterministicArtifacts.extendsFrom deterministicLibraries -} - -dependencies { - compileOnly project(':core') - - // Configure these by hand. It should be a minimal subset of core's dependencies, - // and without any obviously non-deterministic ones such as Hibernate. - - // These "api" dependencies will become "compile" scoped in our published POM. - api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - api "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - api "javax.persistence:javax.persistence-api:2.2" - api "com.google.code.findbugs:jsr305:$jsr305_version" - api "org.slf4j:slf4j-api:$slf4j_version" - - compileOnly "io.opentelemetry:opentelemetry-api:${open_telemetry_version}" - compileOnly project(':opentelemetry') - - // These dependencies will become "runtime" scoped in our published POM. - // See publish.dependenciesFrom.defaultScope. - deterministicLibraries "org.bouncycastle:bcprov-jdk18on:$bouncycastle_version" - deterministicLibraries "org.bouncycastle:bcpkix-jdk18on:$bouncycastle_version" - deterministicLibraries "net.i2p.crypto:eddsa:$eddsa_version" -} - -tasks.named('jar', Jar) { - archiveBaseName = 'DOES-NOT-EXIST' - // Don't build a jar here because it would be the wrong one. - // The jar we really want will be built by the metafix task. - enabled = false -} - -def coreJarTask = project(':core').tasks.named('jar', Jar) -def originalJar = coreJarTask.map { it.outputs.files.singleFile } - -def patchCore = tasks.register('patchCore', Zip) { - dependsOn coreJarTask - destinationDirectory = layout.buildDirectory.dir('source-libs') - metadataCharset 'UTF-8' - archiveClassifier = 'transient' - archiveExtension = 'jar' - - from(compileKotlin) - from(processResources) - from(zipTree(originalJar)) { - exclude 'net/corda/core/crypto/DelegatingSecureRandomService*.class' - exclude 'net/corda/core/crypto/DigestSupplier.class' - exclude 'net/corda/core/internal/*ToggleField*.class' - exclude 'net/corda/core/serialization/*SerializationFactory*.class' - exclude 'net/corda/core/serialization/internal/AttachmentsHolderImpl.class' - exclude 'net/corda/core/serialization/internal/CheckpointSerializationFactory*.class' - exclude 'net/corda/core/internal/rules/*.class' - exclude 'net/corda/core/contracts/CordaRotatedKeys.class' - exclude 'net/corda/core/contracts/RotatedKeysKt.class' - exclude 'net/corda/core/contracts/RotatedKeys.class' - exclude 'net/corda/core/internal/utilities/PrivateInterner*.class' - } - - reproducibleFileOrder = true - includeEmptyDirs = false -} - -def predeterminise = tasks.register('predeterminise', ProGuardTask) { - injars patchCore - outjars file("$buildDir/proguard/pre-deterministic-${project.version}.jar") - - if (JavaVersion.current().isJava9Compatible()) { - libraryjars "$javaHome/jmods" - } else { - libraryjars "$javaHome/lib/rt.jar" - libraryjars "$javaHome/lib/jce.jar" - } - configurations.compileClasspath.forEach { - if (originalJar != it) { - libraryjars it, filter: '!META-INF/versions/**' - } - } - - keepattributes '*' - keepdirectories - dontwarn '**$1$1,org.hibernate.annotations.*' - dontpreverify - dontobfuscate - dontoptimize - dontnote - printseeds - verbose - - keep '@interface net.corda.core.* { *; }' - keep '@interface net.corda.core.contracts.** { *; }' - keep '@interface net.corda.core.serialization.** { *; }' - keep '@net.corda.core.KeepForDJVM class * { *; }', includedescriptorclasses:true - keepclassmembers 'class net.corda.core.** { public synthetic ; }' -} - -def jarFilter = tasks.register('jarFilter', JarFilterTask) { - jars predeterminise - annotations { - forDelete = [ - "net.corda.core.DeleteForDJVM" - ] - forStub = [ - "net.corda.core.StubOutForDJVM" - ] - forRemove = [ - "co.paralleluniverse.fibers.Suspendable", - "org.hibernate.annotations.Immutable" - ] - forSanitise = [ - "net.corda.core.DeleteForDJVM" - ] - } -} - -def determinise = tasks.register('determinise', ProGuardTask) { - injars jarFilter - outjars file("$buildDir/proguard/$jarBaseName-${project.version}.jar") - - if (JavaVersion.current().isJava9Compatible()) { - libraryjars "$javaHome/jmods" - } else { - libraryjars "$javaHome/lib/rt.jar" - libraryjars "$javaHome/lib/jce.jar" - } - configurations.deterministicLibraries.forEach { - libraryjars it, filter: '!META-INF/versions/**' - } - - // Analyse the JAR for dead code, and remove (some of) it. - optimizations 'code/removal/simple,code/removal/advanced' - printconfiguration - - keepattributes '*' - keepdirectories - dontobfuscate - dontnote - printseeds - verbose - - keep '@interface net.corda.core.CordaInternal { *; }' - keep '@interface net.corda.core.DoNotImplement { *; }' - keep '@interface net.corda.core.KeepForDJVM { *; }' - keep '@interface net.corda.core.contracts.** { *; }' - keep '@interface net.corda.core.serialization.** { *; }' - keep '@net.corda.core.KeepForDJVM class * { *; }', includedescriptorclasses:true - keepclassmembers 'class net.corda.core.** { public synthetic ; }' -} - -def checkDeterminism = tasks.register('checkDeterminism', ProGuardTask) - -def metafix = tasks.register('metafix', MetaFixerTask) { - outputDir = layout.buildDirectory.dir('libs') - jars determinise - suffix "" - - // Strip timestamps from the JAR to make it reproducible. - preserveTimestamps = false - finalizedBy checkDeterminism -} - -// DOCSTART 01 -checkDeterminism.configure { - dependsOn jdkTask - injars metafix - - libraryjars deterministic_rt_jar - - configurations.deterministicLibraries.forEach { - libraryjars it, filter: '!META-INF/versions/**' - } - - keepattributes '*' - dontpreverify - dontobfuscate - dontoptimize - verbose - - keep 'class *' -} -// DOCEND 01 - -defaultTasks "determinise" -determinise.configure { - finalizedBy metafix -} -tasks.named('assemble') { - dependsOn checkDeterminism -} - -def deterministicJar = metafix.map { it.outputs.files.singleFile } -artifacts { - deterministicArtifacts deterministicJar - publish deterministicJar -} - -tasks.named('sourceJar', Jar) { - from 'README.md' - include 'README.md' -} - -tasks.named('javadocJar', Jar) { - from 'README.md' - include 'README.md' -} - -publish { - dependenciesFrom configurations.deterministicArtifacts - name jarBaseName -} - -idea { - module { - if (project.hasProperty("deterministic_idea_sdk")) { - jdkName project.property("deterministic_idea_sdk") as String - } - } -} diff --git a/core-deterministic/src/main/kotlin/net/corda/core/crypto/DelegatingSecureRandomService.kt b/core-deterministic/src/main/kotlin/net/corda/core/crypto/DelegatingSecureRandomService.kt deleted file mode 100644 index 34355c4191..0000000000 --- a/core-deterministic/src/main/kotlin/net/corda/core/crypto/DelegatingSecureRandomService.kt +++ /dev/null @@ -1,19 +0,0 @@ -package net.corda.core.crypto - -import java.security.Provider -import java.security.SecureRandomSpi - -@Suppress("unused") -class DelegatingSecureRandomService(provider: CordaSecurityProvider) - : Provider.Service(provider, "SecureRandom", "dummy-algorithm", UnsupportedSecureRandomSpi::javaClass.name, null, null) { - private val instance: SecureRandomSpi = UnsupportedSecureRandomSpi(algorithm) - override fun newInstance(param: Any?) = instance - - private class UnsupportedSecureRandomSpi(private val algorithm: String) : SecureRandomSpi() { - override fun engineSetSeed(seed: ByteArray) = unsupported() - override fun engineNextBytes(bytes: ByteArray) = unsupported() - override fun engineGenerateSeed(numBytes: Int) = unsupported() - - private fun unsupported(): Nothing = throw UnsupportedOperationException("$algorithm not supported") - } -} diff --git a/core-deterministic/src/main/kotlin/net/corda/core/crypto/DigestSupplier.kt b/core-deterministic/src/main/kotlin/net/corda/core/crypto/DigestSupplier.kt deleted file mode 100644 index 18d60effa4..0000000000 --- a/core-deterministic/src/main/kotlin/net/corda/core/crypto/DigestSupplier.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.core.crypto - -import net.corda.core.crypto.internal.DigestAlgorithmFactory -import java.util.function.Supplier - -@Suppress("unused") -private class DigestSupplier(private val algorithm: String) : Supplier { - override fun get(): DigestAlgorithm = DigestAlgorithmFactory.create(algorithm) - val digestLength: Int by lazy { get().digestLength } -} diff --git a/core-deterministic/src/main/kotlin/net/corda/core/internal/ToggleField.kt b/core-deterministic/src/main/kotlin/net/corda/core/internal/ToggleField.kt deleted file mode 100644 index 1912bd895e..0000000000 --- a/core-deterministic/src/main/kotlin/net/corda/core/internal/ToggleField.kt +++ /dev/null @@ -1,62 +0,0 @@ -package net.corda.core.internal - -import net.corda.core.KeepForDJVM -import net.corda.core.utilities.contextLogger -import org.slf4j.Logger -import kotlin.reflect.KProperty - -/** May go from null to non-null and vice-versa, and that's it. */ -abstract class ToggleField(val name: String) { - abstract fun get(): T? - fun set(value: T?) { - if (value != null) { - check(get() == null) { "$name already has a value." } - setImpl(value) - } else { - check(get() != null) { "$name is already null." } - clear() - } - } - - protected abstract fun setImpl(value: T) - protected abstract fun clear() - operator fun getValue(thisRef: Any?, property: KProperty<*>) = get() - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) = set(value) -} - -@KeepForDJVM -class SimpleToggleField(name: String, private val once: Boolean = false) : ToggleField(name) { - private var holder: T? = null // Force T? in API for safety. - override fun get() = holder - override fun setImpl(value: T) { holder = value } - override fun clear() { - check(!once) { "Value of $name cannot be changed." } - holder = null - } -} - -@KeepForDJVM -class ThreadLocalToggleField(name: String) : ToggleField(name) { - private var holder: T? = null // Force T? in API for safety. - override fun get() = holder - override fun setImpl(value: T) { holder = value } - override fun clear() { - holder = null - } -} - -@Suppress("UNUSED") -@KeepForDJVM -class InheritableThreadLocalToggleField(name: String, - private val log: Logger = staticLog, - private val isAGlobalThreadBeingCreated: (Array) -> Boolean) : ToggleField(name) { - private companion object { - private val staticLog = contextLogger() - } - private var holder: T? = null // Force T? in API for safety. - override fun get() = holder - override fun setImpl(value: T) { holder = value } - override fun clear() { - holder = null - } -} \ No newline at end of file diff --git a/core-deterministic/src/main/kotlin/net/corda/core/internal/rules/TargetVersionDependentRules.kt b/core-deterministic/src/main/kotlin/net/corda/core/internal/rules/TargetVersionDependentRules.kt deleted file mode 100644 index 528d059e2c..0000000000 --- a/core-deterministic/src/main/kotlin/net/corda/core/internal/rules/TargetVersionDependentRules.kt +++ /dev/null @@ -1,12 +0,0 @@ -package net.corda.core.internal.rules - -import net.corda.core.contracts.ContractState - -// This file provides rules that depend on the targetVersion of the current Contract or Flow. -// In core, this is determined by means which are unavailable in the DJVM, -// so we must provide deterministic alternatives here. - -@Suppress("unused") -object StateContractValidationEnforcementRule { - fun shouldEnforce(@Suppress("UNUSED_PARAMETER") state: ContractState): Boolean = true -} \ No newline at end of file diff --git a/core-deterministic/src/main/kotlin/net/corda/core/internal/utilities/PrivateInterner.kt b/core-deterministic/src/main/kotlin/net/corda/core/internal/utilities/PrivateInterner.kt deleted file mode 100644 index 24aa506c5c..0000000000 --- a/core-deterministic/src/main/kotlin/net/corda/core/internal/utilities/PrivateInterner.kt +++ /dev/null @@ -1,16 +0,0 @@ -package net.corda.core.internal.utilities - -import net.corda.core.KeepForDJVM - -@KeepForDJVM -class PrivateInterner(val verifier: IternabilityVerifier = AlwaysInternableVerifier()) { - // DJVM implementation does not intern and does not use Guava - fun intern(sample: S): S = sample - - @KeepForDJVM - companion object { - @Suppress("UNUSED_PARAMETER") - fun findFor(clazz: Class<*>?): PrivateInterner? = null - } -} - diff --git a/core-deterministic/src/main/kotlin/net/corda/core/serialization/SerializationFactory.kt b/core-deterministic/src/main/kotlin/net/corda/core/serialization/SerializationFactory.kt deleted file mode 100644 index 2d6d8d3b09..0000000000 --- a/core-deterministic/src/main/kotlin/net/corda/core/serialization/SerializationFactory.kt +++ /dev/null @@ -1,99 +0,0 @@ -package net.corda.core.serialization - -import net.corda.core.KeepForDJVM -import net.corda.core.serialization.internal.effectiveSerializationEnv -import net.corda.core.utilities.ByteSequence - -/** - * An abstraction for serializing and deserializing objects, with support for versioning of the wire format via - * a header / prefix in the bytes. - */ -@KeepForDJVM -abstract class SerializationFactory { - /** - * Deserialize the bytes in to an object, using the prefixed bytes to determine the format. - * - * @param byteSequence The bytes to deserialize, including a format header prefix. - * @param clazz The class or superclass or the object to be deserialized, or [Any] or [Object] if unknown. - * @param context A context that configures various parameters to deserialization. - */ - abstract fun deserialize(byteSequence: ByteSequence, clazz: Class, context: SerializationContext): T - - /** - * Deserialize the bytes in to an object, using the prefixed bytes to determine the format. - * - * @param byteSequence The bytes to deserialize, including a format header prefix. - * @param clazz The class or superclass or the object to be deserialized, or [Any] or [Object] if unknown. - * @param context A context that configures various parameters to deserialization. - * @return deserialized object along with [SerializationContext] to identify encoding used. - */ - abstract fun deserializeWithCompatibleContext(byteSequence: ByteSequence, clazz: Class, context: SerializationContext): ObjectWithCompatibleContext - - /** - * Serialize an object to bytes using the preferred serialization format version from the context. - * - * @param obj The object to be serialized. - * @param context A context that configures various parameters to serialization, including the serialization format version. - */ - abstract fun serialize(obj: T, context: SerializationContext): SerializedBytes - - /** - * If there is a need to nest serialization/deserialization with a modified context during serialization or deserialization, - * this will return the current context used to start serialization/deserialization. - */ - val currentContext: SerializationContext? get() = _currentContext - - /** - * A context to use as a default if you do not require a specially configured context. It will be the current context - * if the use is somehow nested (see [currentContext]). - */ - val defaultContext: SerializationContext get() = currentContext ?: effectiveSerializationEnv.p2pContext - - private var _currentContext: SerializationContext? = null - - /** - * Change the current context inside the block to that supplied. - */ - fun withCurrentContext(context: SerializationContext?, block: () -> T): T { - return if (context == null) { - block() - } else { - val priorContext = _currentContext - _currentContext = context - try { - block() - } finally { - _currentContext = priorContext - } - } - } - - /** - * Allow subclasses to temporarily mark themselves as the current factory for the current thread during serialization/deserialization. - * Will restore the prior context on exiting the block. - */ - fun asCurrent(block: SerializationFactory.() -> T): T { - val priorContext = _currentFactory - _currentFactory= this - try { - return this.block() - } finally { - _currentFactory = priorContext - } - } - - companion object { - private var _currentFactory: SerializationFactory? = null - - /** - * A default factory for serialization/deserialization, taking into account the [currentFactory] if set. - */ - val defaultFactory: SerializationFactory get() = currentFactory ?: effectiveSerializationEnv.serializationFactory - - /** - * If there is a need to nest serialization/deserialization with a modified context during serialization or deserialization, - * this will return the current factory used to start serialization/deserialization. - */ - val currentFactory: SerializationFactory? get() = _currentFactory - } -} diff --git a/core-deterministic/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsHolderImpl.kt b/core-deterministic/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsHolderImpl.kt deleted file mode 100644 index a2f7b8ab30..0000000000 --- a/core-deterministic/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsHolderImpl.kt +++ /dev/null @@ -1,23 +0,0 @@ -package net.corda.core.serialization.internal - -import net.corda.core.contracts.Attachment -import java.net.URL - -@Suppress("unused") -private class AttachmentsHolderImpl : AttachmentsHolder { - private val attachments = LinkedHashMap>() - - override val size: Int get() = attachments.size - - override fun getKey(key: URL): URL? { - return attachments[key]?.first - } - - override fun get(key: URL): Attachment? { - return attachments[key]?.second - } - - override fun set(key: URL, value: Attachment) { - attachments[key] = key to value - } -} diff --git a/core-deterministic/src/main/resources/META-INF/DJVM-preload b/core-deterministic/src/main/resources/META-INF/DJVM-preload deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/core-deterministic/testing/build.gradle b/core-deterministic/testing/build.gradle deleted file mode 100644 index 61abf977af..0000000000 --- a/core-deterministic/testing/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' -} - -dependencies { - testImplementation project(path: ':core-deterministic', configuration: 'deterministicArtifacts') - testImplementation project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts') - testImplementation project(path: ':core-deterministic:testing:verifier', configuration: 'deterministicArtifacts') - testImplementation project(path: ':core-deterministic:testing:data', configuration: 'testData') - testImplementation(project(':finance:contracts')) { - transitive = false - } - testImplementation(project(':finance:workflows')) { - transitive = false - } - - testImplementation "org.slf4j:slf4j-api:$slf4j_version" - testRuntimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" - testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - testImplementation "org.assertj:assertj-core:$assertj_version" - testImplementation "junit:junit:$junit_version" - testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}" - testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" -} - -// This module has no artifact and only contains tests. -tasks.named('jar', Jar) { - enabled = false -} diff --git a/core-deterministic/testing/data/build.gradle b/core-deterministic/testing/data/build.gradle deleted file mode 100644 index 0141dc3c61..0000000000 --- a/core-deterministic/testing/data/build.gradle +++ /dev/null @@ -1,44 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' -} - -configurations { - testData { - canBeResolved = false - } -} - -dependencies { - testImplementation project(':core') - testImplementation project(':finance:workflows') - testImplementation project(':node-driver') - testImplementation project(path: ':core-deterministic:testing:verifier', configuration: 'runtimeArtifacts') - - testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - testImplementation "org.jetbrains.kotlin:kotlin-reflect" - - testImplementation "junit:junit:$junit_version" - - testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}" - testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" -} - -tasks.named('jar', Jar) { - enabled = false -} - -def test = tasks.named('test', Test) { - filter { - // Running this class is the whole point, so include it explicitly. - includeTestsMatching "net.corda.deterministic.data.GenerateData" - } - // force execution of these tests to generate artifacts required by other module (eg. VerifyTransactionTest) - // note: required by Gradle Build Cache. - outputs.upToDateWhen { false } -} - -def testDataJar = file("$buildDir/test-data.jar") -artifacts { - archives file: testDataJar, type: 'jar', builtBy: test - testData file: testDataJar, type: 'jar', builtBy: test -} diff --git a/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/GenerateData.kt b/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/GenerateData.kt deleted file mode 100644 index 4612f19c7a..0000000000 --- a/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/GenerateData.kt +++ /dev/null @@ -1,92 +0,0 @@ -package net.corda.deterministic.data - -import net.corda.core.serialization.deserialize -import net.corda.deterministic.verifier.LocalSerializationRule -import net.corda.deterministic.verifier.TransactionVerificationRequest -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import java.io.FileNotFoundException -import java.net.URLClassLoader -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import java.nio.file.attribute.FileTime -import java.util.* -import java.util.Calendar.* -import java.util.jar.JarOutputStream -import java.util.zip.Deflater.NO_COMPRESSION -import java.util.zip.ZipEntry -import java.util.zip.ZipEntry.* -import kotlin.reflect.jvm.jvmName - -/** - * Use the JUnit framework to generate a JAR of test data. - */ -class GenerateData { - companion object { - private val CONSTANT_TIME: FileTime = FileTime.fromMillis( - GregorianCalendar(1980, FEBRUARY, 1).apply { timeZone = TimeZone.getTimeZone("UTC") }.timeInMillis - ) - private const val KEYSTORE_ALIAS = "tx" - private val KEYSTORE_PASSWORD = "deterministic".toCharArray() - private val TEST_DATA: Path = Paths.get("build", "test-data.jar") - - private fun compressed(name: String) = ZipEntry(name).apply { - lastModifiedTime = CONSTANT_TIME - method = DEFLATED - } - - private fun directory(name: String) = ZipEntry(name).apply { - lastModifiedTime = CONSTANT_TIME - method = STORED - compressedSize = 0 - size = 0 - crc = 0 - } - } - - @Rule - @JvmField - val testSerialization = LocalSerializationRule(GenerateData::class.jvmName) - - @Before - fun createTransactions() { - JarOutputStream(Files.newOutputStream(TEST_DATA)).use { jar -> - jar.setComment("Test data for Deterministic Corda") - jar.setLevel(NO_COMPRESSION) - - // Serialised transactions for the Enclavelet - jar.putNextEntry(directory("txverify")) - jar.putNextEntry(compressed("txverify/tx-success.bin")) - TransactionGenerator.writeSuccess(jar) - jar.putNextEntry(compressed("txverify/tx-failure.bin")) - TransactionGenerator.writeFailure(jar) - - // KeyStore containing an EC private key. - jar.putNextEntry(directory("keystore")) - jar.putNextEntry(compressed("keystore/txsignature.pfx")) - KeyStoreGenerator.writeKeyStore(jar, KEYSTORE_ALIAS, KEYSTORE_PASSWORD) - } - testSerialization.reset() - } - - @Test(timeout = 300_000) - fun verifyTransactions() { - URLClassLoader(arrayOf(TEST_DATA.toUri().toURL())).use { cl -> - cl.loadResource("txverify/tx-success.bin") - .deserialize() - .toLedgerTransaction() - .verify() - - cl.loadResource("txverify/tx-failure.bin") - .deserialize() - .toLedgerTransaction() - } - } - - private fun ClassLoader.loadResource(resourceName: String): ByteArray { - return getResourceAsStream(resourceName)?.use { it.readBytes() } - ?: throw FileNotFoundException(resourceName) - } -} diff --git a/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/KeyStoreGenerator.kt b/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/KeyStoreGenerator.kt deleted file mode 100644 index a1511fbb42..0000000000 --- a/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/KeyStoreGenerator.kt +++ /dev/null @@ -1,50 +0,0 @@ -package net.corda.deterministic.data - -import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder -import java.io.OutputStream -import java.math.BigInteger.TEN -import java.security.KeyPairGenerator -import java.security.KeyStore -import java.security.spec.ECGenParameterSpec -import java.util.* -import java.util.Calendar.* - -object KeyStoreGenerator { - private val keyPairGenerator: KeyPairGenerator = KeyPairGenerator.getInstance("EC").apply { - initialize(ECGenParameterSpec("secp256k1")) - } - - fun writeKeyStore(output: OutputStream, alias: String, password: CharArray) { - val keyPair = keyPairGenerator.generateKeyPair() - val signer = JcaContentSignerBuilder("SHA256WithECDSA").build(keyPair.private) - val dname = X500Name("CN=Enclavelet") - val startDate = Calendar.getInstance().let { cal -> - cal.time = Date() - cal.add(HOUR, -1) - cal.time - } - val endDate = Calendar.getInstance().let { cal -> - cal.time = startDate - cal.add(YEAR, 1) - cal.time - } - val certificate = JcaX509v3CertificateBuilder( - dname, - TEN, - startDate, - endDate, - dname, - keyPair.public - ).build(signer) - val x509 = JcaX509CertificateConverter().getCertificate(certificate) - - KeyStore.getInstance("PKCS12").apply { - load(null, password) - setKeyEntry(alias, keyPair.private, password, arrayOf(x509)) - store(output, password) - } - } -} \ No newline at end of file diff --git a/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/TransactionGenerator.kt b/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/TransactionGenerator.kt deleted file mode 100644 index 87b7dbb019..0000000000 --- a/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/TransactionGenerator.kt +++ /dev/null @@ -1,115 +0,0 @@ -package net.corda.deterministic.data - -import com.nhaarman.mockito_kotlin.doReturn -import com.nhaarman.mockito_kotlin.mock -import com.nhaarman.mockito_kotlin.whenever -import net.corda.core.crypto.entropyToKeyPair -import net.corda.core.identity.AnonymousParty -import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.Party -import net.corda.core.node.services.IdentityService -import net.corda.core.serialization.serialize -import net.corda.deterministic.verifier.MockContractAttachment -import net.corda.deterministic.verifier.SampleCommandData -import net.corda.deterministic.verifier.TransactionVerificationRequest -import net.corda.finance.POUNDS -import net.corda.finance.`issued by` -import net.corda.finance.contracts.asset.Cash.Commands.Issue -import net.corda.finance.contracts.asset.Cash.Commands.Move -import net.corda.finance.contracts.asset.Cash.Companion.PROGRAM_ID -import net.corda.finance.contracts.asset.Cash.State -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.core.TestIdentity -import net.corda.testing.core.getTestPartyAndCertificate -import net.corda.testing.node.MockServices -import net.corda.testing.node.ledger -import java.io.OutputStream -import java.math.BigInteger -import java.security.KeyPair -import java.security.PublicKey - -object TransactionGenerator { - private val DUMMY_NOTARY: Party = TestIdentity(DUMMY_NOTARY_NAME, 20).party - - private val DUMMY_CASH_ISSUER_KEY: KeyPair = entropyToKeyPair(BigInteger.valueOf(10)) - private val DUMMY_CASH_ISSUER_IDENTITY = getTestPartyAndCertificate(Party(CordaX500Name("Snake Oil Issuer", "London", "GB"), DUMMY_CASH_ISSUER_KEY.public)) - private val DUMMY_CASH_ISSUER = DUMMY_CASH_ISSUER_IDENTITY.party.ref(1) - - private val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) - private val MEGA_CORP: Party = megaCorp.party - private val MEGA_CORP_PUBKEY: PublicKey = megaCorp.keyPair.public - private val MINI_CORP_PUBKEY: PublicKey = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")).keyPair.public - - private val ledgerServices = MockServices(emptyList(), MEGA_CORP.name, mock().also { - doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY) - doReturn(DUMMY_CASH_ISSUER.party).whenever(it).partyFromKey(DUMMY_CASH_ISSUER_KEY.public) - }) - - fun writeSuccess(output: OutputStream) { - ledgerServices.ledger(DUMMY_NOTARY) { - // Issue a couple of cash states and spend them. - val wtx1 = transaction { - attachments(PROGRAM_ID) - output(PROGRAM_ID, "c1", State(1000.POUNDS `issued by` DUMMY_CASH_ISSUER, AnonymousParty(MEGA_CORP_PUBKEY))) - command(DUMMY_CASH_ISSUER.party.owningKey, Issue()) - verifies() - } - val wtx2 = transaction { - attachments(PROGRAM_ID) - output(PROGRAM_ID, "c2", State(2000.POUNDS `issued by` DUMMY_CASH_ISSUER, AnonymousParty(MEGA_CORP_PUBKEY))) - command(DUMMY_CASH_ISSUER.party.owningKey, Issue()) - verifies() - } - val wtx3 = transaction { - attachments(PROGRAM_ID) - input("c1") - input("c2") - output(PROGRAM_ID, "c3", State(3000.POUNDS `issued by` DUMMY_CASH_ISSUER, AnonymousParty(MINI_CORP_PUBKEY))) - command(MEGA_CORP_PUBKEY, Move()) - verifies() - } - val contractAttachment = MockContractAttachment(interpreter.services.cordappProvider.getContractAttachmentID(PROGRAM_ID)!!, PROGRAM_ID) - TransactionVerificationRequest( - wtx3.serialize(), - arrayOf(wtx1.serialize(), wtx2.serialize()), - arrayOf(contractAttachment.serialize().bytes), - ledgerServices.networkParameters.serialize()) - .serialize() - .writeTo(output) - } - } - - fun writeFailure(output: OutputStream) { - ledgerServices.ledger(DUMMY_NOTARY) { - // Issue a couple of cash states and spend them. - val wtx1 = transaction { - attachments(PROGRAM_ID) - output(PROGRAM_ID, "c1", State(1000.POUNDS `issued by` DUMMY_CASH_ISSUER, AnonymousParty(MEGA_CORP_PUBKEY))) - command(DUMMY_CASH_ISSUER.party.owningKey, Issue()) - verifies() - } - val wtx2 = transaction { - attachments(PROGRAM_ID) - output(PROGRAM_ID, "c2", State(2000.POUNDS `issued by` DUMMY_CASH_ISSUER, AnonymousParty(MEGA_CORP_PUBKEY))) - command(DUMMY_CASH_ISSUER.party.owningKey, Issue()) - verifies() - } - val wtx3 = transaction { - attachments(PROGRAM_ID) - input("c1") - input("c2") - command(DUMMY_CASH_ISSUER.party.owningKey, SampleCommandData) - output(PROGRAM_ID, "c3", State(3000.POUNDS `issued by` DUMMY_CASH_ISSUER, AnonymousParty(MINI_CORP_PUBKEY))) - failsWith("Required ${Move::class.java.canonicalName} command") - } - val contractAttachment = MockContractAttachment(interpreter.services.cordappProvider.getContractAttachmentID(PROGRAM_ID)!!, PROGRAM_ID) - TransactionVerificationRequest( - wtx3.serialize(), - arrayOf(wtx1.serialize(), wtx2.serialize()), - arrayOf(contractAttachment.serialize().bytes), - ledgerServices.networkParameters.serialize()) - .serialize() - .writeTo(output) - } - } -} \ No newline at end of file diff --git a/core-deterministic/testing/src/test/java/net/corda/deterministic/CheatingSecurityProvider.java b/core-deterministic/testing/src/test/java/net/corda/deterministic/CheatingSecurityProvider.java deleted file mode 100644 index 6d4c54a7b4..0000000000 --- a/core-deterministic/testing/src/test/java/net/corda/deterministic/CheatingSecurityProvider.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.corda.deterministic; - -import java.security.Provider; -import java.security.SecureRandom; -import java.security.SecureRandomSpi; -import java.security.Security; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.Assert.assertEquals; - -/** - * Temporarily restore Sun's [SecureRandom] provider. - * This is ONLY for allowing us to generate test data, e.g. signatures. - * - * JDK11 upgrade: rewritten in Java to gain access to private internal JDK classes via module directives (not available to Kotlin compiler): - * sun.security.provider.SecureRandom() - */ -public class CheatingSecurityProvider extends Provider implements AutoCloseable { - - private static AtomicInteger counter = new AtomicInteger(); - - @SuppressWarnings("deprecation") // JDK11: should replace with Provider(String name, double version, String info) (since 9) - public CheatingSecurityProvider() { - super("Cheat-" + counter.getAndIncrement(), 1.8, "Cheat security provider"); - putService(new CheatingSecureRandomService(this)); - assertEquals(1, Security.insertProviderAt(this, 1)); - } - - public void close() { - Security.removeProvider(getName()); - } - - private class SunSecureRandom extends SecureRandom { - public SunSecureRandom() { - // JDK11 upgrade: rewritten in Java to gain access to private internal JDK classes via open module directive - super(new sun.security.provider.SecureRandom(), null); - } - } - - private class CheatingSecureRandomService extends Provider.Service { - - public CheatingSecureRandomService(Provider provider) { - super(provider, "SecureRandom", "CheatingPRNG", CheatingSecureRandomSpi.class.getName(), null, null); - } - - private SecureRandomSpi instance = new CheatingSecureRandomSpi(); - - public Object newInstance(Object constructorParameter){ - return instance; - } - } - - private class CheatingSecureRandomSpi extends SecureRandomSpi { - - private SecureRandom secureRandom = new SunSecureRandom(); - - public void engineSetSeed(byte[] seed) { secureRandom.setSeed(seed); } - public void engineNextBytes(byte[] bytes) { secureRandom.nextBytes(bytes); } - public byte[] engineGenerateSeed(int numBytes) { return secureRandom.generateSeed(numBytes); } - } -} \ No newline at end of file diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/core/internal/ClassLoadingUtils.kt b/core-deterministic/testing/src/test/kotlin/net/corda/core/internal/ClassLoadingUtils.kt deleted file mode 100644 index e2f9075b8c..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/core/internal/ClassLoadingUtils.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.corda.core.internal - -/** - * Stubbing out non-deterministic method. - */ -fun createInstancesOfClassesImplementing(@Suppress("UNUSED_PARAMETER") classloader: ClassLoader, @Suppress("UNUSED_PARAMETER") clazz: Class, - @Suppress("UNUSED_PARAMETER") classVersionRange: IntRange? = null): Set { - return emptySet() -} \ No newline at end of file diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/CordaExceptionTest.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/CordaExceptionTest.kt deleted file mode 100644 index 3b95b952d2..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/CordaExceptionTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -package net.corda.deterministic - -import net.corda.core.CordaException -import net.corda.core.contracts.AttachmentResolutionException -import net.corda.core.contracts.TransactionResolutionException -import net.corda.core.contracts.TransactionVerificationException.* -import net.corda.core.crypto.SecureHash -import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.Party -import org.junit.Assert.* -import org.junit.Test -import java.security.PublicKey -import kotlin.test.assertFailsWith - -class CordaExceptionTest { - companion object { - const val CONTRACT_CLASS = "com.r3.corda.contracts.TestContract" - val TEST_HASH = SecureHash.zeroHash - val TX_ID = SecureHash.allOnesHash - - val ALICE_NAME = CordaX500Name("Alice Corp", "Madrid", "ES") - val ALICE_KEY: PublicKey = object : PublicKey { - override fun getAlgorithm(): String = "TEST-256" - override fun getFormat(): String = "" - override fun getEncoded() = byteArrayOf() - } - val ALICE = Party(ALICE_NAME, ALICE_KEY) - - val BOB_NAME = CordaX500Name("Bob Plc", "Rome", "IT") - val BOB_KEY: PublicKey = object : PublicKey { - override fun getAlgorithm(): String = "TEST-512" - override fun getFormat(): String = "" - override fun getEncoded() = byteArrayOf() - } - val BOB = Party(BOB_NAME, BOB_KEY) - } - - @Test(timeout=300_000) - fun testCordaException() { - val ex = assertFailsWith { throw CordaException("BAD THING") } - assertEquals("BAD THING", ex.message) - } - - @Test(timeout=300_000) - fun testAttachmentResolutionException() { - val ex = assertFailsWith { throw AttachmentResolutionException(TEST_HASH) } - assertEquals(TEST_HASH, ex.hash) - } - - @Test(timeout=300_000) - fun testTransactionResolutionException() { - val ex = assertFailsWith { throw TransactionResolutionException(TEST_HASH) } - assertEquals(TEST_HASH, ex.hash) - } - - @Test(timeout=300_000) - fun testConflictingAttachmentsRejection() { - val ex = assertFailsWith { throw ConflictingAttachmentsRejection(TX_ID, CONTRACT_CLASS) } - assertEquals(TX_ID, ex.txId) - assertEquals(CONTRACT_CLASS, ex.contractClass) - } - - @Test(timeout=300_000) - fun testNotaryChangeInWrongTransactionType() { - val ex = assertFailsWith { throw NotaryChangeInWrongTransactionType(TX_ID, ALICE, BOB) } - assertEquals(TX_ID, ex.txId) - assertEquals(ALICE, ex.txNotary) - assertEquals(BOB, ex.outputNotary) - } -} \ No newline at end of file diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/KeyStoreProvider.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/KeyStoreProvider.kt deleted file mode 100644 index 20929a557a..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/KeyStoreProvider.kt +++ /dev/null @@ -1,44 +0,0 @@ -package net.corda.deterministic - -import org.junit.AssumptionViolatedException -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement -import java.security.KeyPair -import java.security.KeyStore -import java.security.PrivateKey -import java.security.cert.TrustAnchor -import java.security.cert.X509Certificate - -class KeyStoreProvider(private val storeName: String, private val storePassword: String) : TestRule { - private lateinit var keyStore: KeyStore - - private fun loadKeyStoreResource(resourceName: String, password: CharArray, type: String = "PKCS12"): KeyStore { - return KeyStore.getInstance(type).apply { - // Skip these tests if we cannot load the keystore. - val keyStream = KeyStoreProvider::class.java.classLoader.getResourceAsStream(resourceName) - ?: throw AssumptionViolatedException("KeyStore $resourceName not found") - keyStream.use { input -> - load(input, password) - } - } - } - - override fun apply(statement: Statement, description: Description?): Statement { - return object : Statement() { - override fun evaluate() { - keyStore = loadKeyStoreResource(storeName, storePassword.toCharArray()) - statement.evaluate() - } - } - } - - fun getKeyPair(alias: String): KeyPair { - val privateKey = keyStore.getKey(alias, storePassword.toCharArray()) as PrivateKey - return KeyPair(keyStore.getCertificate(alias).publicKey, privateKey) - } - - @Suppress("UNUSED") - fun trustAnchorsFor(vararg aliases: String): Set - = aliases.map { alias -> TrustAnchor(keyStore.getCertificate(alias) as X509Certificate, null) }.toSet() -} diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/Utilities.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/Utilities.kt deleted file mode 100644 index bb7290206e..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/Utilities.kt +++ /dev/null @@ -1,14 +0,0 @@ -package net.corda.deterministic - -import java.io.ByteArrayOutputStream -import java.io.IOException - -private val classLoader: ClassLoader = object {}.javaClass.classLoader - -@Throws(IOException::class) -fun bytesOfResource(resourceName: String): ByteArray { - return ByteArrayOutputStream().let { baos -> - classLoader.getResourceAsStream(resourceName).copyTo(baos) - baos.toByteArray() - } -} diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/contracts/AttachmentTest.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/contracts/AttachmentTest.kt deleted file mode 100644 index 491aa33d9c..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/contracts/AttachmentTest.kt +++ /dev/null @@ -1,79 +0,0 @@ -package net.corda.deterministic.contracts - -import net.corda.core.contracts.Attachment -import net.corda.core.crypto.SecureHash -import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.Party -import org.junit.Assert.* -import org.junit.Before -import org.junit.Test -import java.io.ByteArrayOutputStream -import java.io.InputStream -import java.security.PublicKey -import java.util.jar.JarOutputStream -import java.util.zip.Deflater.* -import java.util.zip.ZipEntry - -class AttachmentTest { - private companion object { - private val data = byteArrayOf(0x73, 0x71, 0x18, 0x5F, 0x3A, 0x47, -0x22, 0x38) - private val jarData: ByteArray = ByteArrayOutputStream().let { baos -> - JarOutputStream(baos).use { jar -> - jar.setLevel(BEST_COMPRESSION) - jar.putNextEntry(ZipEntry("data.bin").apply { method = DEFLATED }) - data.inputStream().copyTo(jar) - } - baos.toByteArray() - } - - private val ALICE_NAME = CordaX500Name("Alice Corp", "Madrid", "ES") - private val ALICE_KEY: PublicKey = object : PublicKey { - override fun getAlgorithm(): String = "TEST-256" - override fun getFormat(): String = "" - override fun getEncoded() = byteArrayOf() - } - private val ALICE = Party(ALICE_NAME, ALICE_KEY) - } - - private lateinit var attachment: Attachment - - @Before - fun setup() { - attachment = object : Attachment { - override val signerKeys: List - get() = listOf(ALICE_KEY) - override val id: SecureHash - get() = SecureHash.allOnesHash - override val signers: List - get() = listOf(ALICE) - override val size: Int - get() = jarData.size - - override fun open(): InputStream { - return jarData.inputStream() - } - } - } - - @Test(timeout=300_000) - fun testAttachmentJar() { - attachment.openAsJAR().use { jar -> - val entry = jar.nextJarEntry ?: return@use - assertEquals("data.bin", entry.name) - val entryData = ByteArrayOutputStream().use { - jar.copyTo(it) - it.toByteArray() - } - assertArrayEquals(data, entryData) - } - } - - @Test(timeout=300_000) - fun testExtractFromAttachment() { - val resultData = ByteArrayOutputStream().use { - attachment.extractFile("data.bin", it) - it.toByteArray() - } - assertArrayEquals(data, resultData) - } -} \ No newline at end of file diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/contracts/PrivacySaltTest.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/contracts/PrivacySaltTest.kt deleted file mode 100644 index 61fb7071a6..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/contracts/PrivacySaltTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -package net.corda.deterministic.contracts - -import net.corda.core.contracts.PrivacySalt -import org.junit.Test -import kotlin.test.* - -class PrivacySaltTest { - private companion object { - private const val SALT_SIZE = 32 - } - - @Test(timeout=300_000) - fun testValidSalt() { - PrivacySalt(ByteArray(SALT_SIZE) { 0x14 }) - } - - @Test(timeout=300_000) - fun testInvalidSaltWithAllZeros() { - val ex = assertFailsWith { PrivacySalt(ByteArray(SALT_SIZE)) } - assertEquals("Privacy salt should not be all zeros.", ex.message) - } - - @Test(timeout=300_000) - fun testTooShortPrivacySaltForSHA256() { - val ex = assertFailsWith { PrivacySalt(ByteArray(SALT_SIZE - 1) { 0x7f }) } - assertEquals("Privacy salt should be at least 32 bytes.", ex.message) - } -} diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/contracts/UniqueIdentifierTest.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/contracts/UniqueIdentifierTest.kt deleted file mode 100644 index 1aed8eed0f..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/contracts/UniqueIdentifierTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -package net.corda.deterministic.contracts - -import net.corda.core.contracts.UniqueIdentifier -import org.assertj.core.api.Assertions.assertThat -import org.junit.Assert.* -import org.junit.Test -import java.util.* -import kotlin.reflect.full.primaryConstructor -import kotlin.test.assertFailsWith - -class UniqueIdentifierTest { - private companion object { - private const val NAME = "MyName" - private val TEST_UUID: UUID = UUID.fromString("00000000-1111-2222-3333-444444444444") - } - - @Test(timeout=300_000) - fun testNewInstance() { - val id = UniqueIdentifier(NAME, TEST_UUID) - assertEquals("${NAME}_$TEST_UUID", id.toString()) - assertEquals(NAME, id.externalId) - assertEquals(TEST_UUID, id.id) - } - - @Test(timeout=300_000) - fun testPrimaryConstructor() { - val primary = UniqueIdentifier::class.primaryConstructor ?: throw AssertionError("primary constructor missing") - assertThat(primary.call(NAME, TEST_UUID)).isEqualTo(UniqueIdentifier(NAME, TEST_UUID)) - } - - @Test(timeout=300_000) - fun testConstructors() { - assertEquals(1, UniqueIdentifier::class.constructors.size) - val ex = assertFailsWith { UniqueIdentifier::class.constructors.first().call() } - assertThat(ex).hasMessage("Callable expects 2 arguments, but 0 were provided.") - } -} \ No newline at end of file diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/CryptoSignUtils.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/CryptoSignUtils.kt deleted file mode 100644 index 9351d06bc3..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/CryptoSignUtils.kt +++ /dev/null @@ -1,68 +0,0 @@ -@file:JvmName("CryptoSignUtils") - -package net.corda.deterministic.crypto - -import net.corda.core.crypto.* -import net.corda.core.crypto.Crypto.findSignatureScheme -import net.corda.core.crypto.Crypto.isSupportedSignatureScheme -import net.corda.core.serialization.serialize -import java.security.* - -/** - * This is a slightly modified copy of signing utils from net.corda.core.crypto.Crypto, which are normally removed from DJVM. - * However, we need those for TransactionSignatureTest. - */ -object CryptoSignUtils { - @JvmStatic - @Throws(InvalidKeyException::class, SignatureException::class) - fun doSign(schemeCodeName: String, privateKey: PrivateKey, clearData: ByteArray): ByteArray { - return doSign(findSignatureScheme(schemeCodeName), privateKey, clearData) - } - - /** - * Generic way to sign [ByteArray] data with a [PrivateKey] and a known [Signature]. - * @param signatureScheme a [SignatureScheme] object, retrieved from supported signature schemes, see [Crypto]. - * @param privateKey the signer's [PrivateKey]. - * @param clearData the data/message to be signed in [ByteArray] form (usually the Merkle root). - * @return the digital signature (in [ByteArray]) on the input message. - * @throws IllegalArgumentException if the signature scheme is not supported for this private key. - * @throws InvalidKeyException if the private key is invalid. - * @throws SignatureException if signing is not possible due to malformed data or private key. - */ - @JvmStatic - @Throws(InvalidKeyException::class, SignatureException::class) - fun doSign(signatureScheme: SignatureScheme, privateKey: PrivateKey, clearData: ByteArray): ByteArray { - require(isSupportedSignatureScheme(signatureScheme)) { - "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" - } - require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" } - val signature = Signature.getInstance(signatureScheme.signatureName, signatureScheme.providerName) - signature.initSign(privateKey) - signature.update(clearData) - return signature.sign() - } - - /** - * Generic way to sign [SignableData] objects with a [PrivateKey]. - * [SignableData] is a wrapper over the transaction's id (Merkle root) in order to attach extra information, such as - * a timestamp or partial and blind signature indicators. - * @param keyPair the signer's [KeyPair]. - * @param signableData a [SignableData] object that adds extra information to a transaction. - * @return a [TransactionSignature] object than contains the output of a successful signing, signer's public key and - * the signature metadata. - * @throws IllegalArgumentException if the signature scheme is not supported for this private key. - * @throws InvalidKeyException if the private key is invalid. - * @throws SignatureException if signing is not possible due to malformed data or private key. - */ - @JvmStatic - @Throws(InvalidKeyException::class, SignatureException::class) - fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature { - val sigKey: SignatureScheme = findSignatureScheme(keyPair.private) - val sigMetaData: SignatureScheme = findSignatureScheme(keyPair.public) - require(sigKey == sigMetaData) { - "Metadata schemeCodeName: ${sigMetaData.schemeCodeName} is not aligned with the key type: ${sigKey.schemeCodeName}." - } - val signatureBytes = doSign(sigKey.schemeCodeName, keyPair.private, signableData.serialize().bytes) - return TransactionSignature(signatureBytes, keyPair.public, signableData.signatureMetadata) - } -} diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/MerkleTreeTest.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/MerkleTreeTest.kt deleted file mode 100644 index e2fcf99860..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/MerkleTreeTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -package net.corda.deterministic.crypto - -import net.corda.core.crypto.DigestService -import net.corda.core.crypto.MerkleTree -import net.corda.core.crypto.SecureHash -import org.junit.Assert.assertEquals -import org.junit.Test - -class MerkleTreeTest { - private fun leafs(algorithm : String) : List = - listOf(SecureHash.allOnesHashFor(algorithm), SecureHash.zeroHashFor(algorithm)) - - @Test(timeout=300_000) - fun testCreate() { - val merkle = MerkleTree.getMerkleTree(leafs(SecureHash.SHA2_256), DigestService.sha2_256) - assertEquals(SecureHash.create("A5DE9B714ACCD8AFAAABF1CBD6E1014C9D07FF95C2AE154D91EC68485B31E7B5"), merkle.hash) - } - - @Test(timeout=300_000) - fun `test create SHA2-384`() { - val merkle = MerkleTree.getMerkleTree(leafs(SecureHash.SHA2_384), DigestService.sha2_384) - assertEquals(SecureHash.create("SHA-384:2B83D37859E3665D7C239964D769CF950EE6478C13E4CA2D6643C23B6C4EAE035C88F654D22E0D65E7CA40BAE4F3718F"), merkle.hash) - } - - @Test(timeout=300_000) - fun `test create SHA2-256 to SHA2-384`() { - val merkle = MerkleTree.getMerkleTree(leafs(SecureHash.SHA2_256), DigestService.sha2_384) - assertEquals(SecureHash.create("SHA-384:02A4E8EA5AA4BBAFE80C0E7127B15994B84030BE8616EA2A0127D85203CF34221403635C08084A6BDDB1DB06333F0A49"), merkle.hash) - } - -// @Test(timeout=300_000) -// fun testCreateSHA3256() { -// val merkle = MerkleTree.getMerkleTree(listOf(SecureHash.allOnesHashFor(SecureHash.SHA3_256), -// SecureHash.zeroHashFor(SecureHash.SHA3_256)), DigestService.sha3_256) -// assertEquals(SecureHash.create("SHA3-256:80673DBEEC8F6761ACBB121E7E45F61D4279CCD8B8E2231741ECD0716F4C9EDC"), merkle.hash) -// } -// -// @Test(timeout=300_000) -// fun testCreateSHA2256toSHA3256() { -// val merkle = MerkleTree.getMerkleTree(listOf(SecureHash.allOnesHash, SecureHash.zeroHash), DigestService.sha3_256) -// assertEquals(SecureHash.create("SHA3-256:80673DBEEC8F6761ACBB121E7E45F61D4279CCD8B8E2231741ECD0716F4C9EDC"), merkle.hash) -// } -// -// @Test(timeout=300_000) -// fun testCreateSHA3256toSHA2256() { -// val merkle = MerkleTree.getMerkleTree(listOf(SecureHash.allOnesHashFor(SecureHash.SHA3_256), -// SecureHash.zeroHashFor(SecureHash.SHA3_256)), DigestService.sha2_256) -// assertEquals(SecureHash.create("A5DE9B714ACCD8AFAAABF1CBD6E1014C9D07FF95C2AE154D91EC68485B31E7B5"), merkle.hash) -// } -} \ No newline at end of file diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/SecureHashTest.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/SecureHashTest.kt deleted file mode 100644 index 162b7cb488..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/SecureHashTest.kt +++ /dev/null @@ -1,42 +0,0 @@ -package net.corda.deterministic.crypto - -import net.corda.core.crypto.SecureHash -import org.bouncycastle.util.encoders.Hex -import org.junit.Assert.* -import org.junit.Test -import java.security.MessageDigest - -class SecureHashTest { - @Test(timeout=300_000) - fun testSHA256() { - val hash = SecureHash.sha256(byteArrayOf(0x64, -0x13, 0x42, 0x3a)) - assertEquals(SecureHash.create("6D1687C143DF792A011A1E80670A4E4E0C25D0D87A39514409B1ABFC2043581F"), hash) - assertEquals("6D1687C143DF792A011A1E80670A4E4E0C25D0D87A39514409B1ABFC2043581F", hash.toString()) - } - - @Test(timeout=300_000) - fun testPrefix() { - val data = byteArrayOf(0x7d, 0x03, -0x21, 0x32, 0x56, 0x47) - val digest = data.digestFor("SHA-256") - val prefix = SecureHash.sha256(data).prefixChars(8) - assertEquals(Hex.toHexString(digest).substring(0, 8).toUpperCase(), prefix) - } - - @Test(timeout=300_000) - fun testConcat() { - val hash1 = SecureHash.sha256(byteArrayOf(0x7d, 0x03, -0x21, 0x32, 0x56, 0x47)) - val hash2 = SecureHash.sha256(byteArrayOf(0x63, 0x01, 0x7f, -0x29, 0x1e, 0x3c)) - val combined = hash1.hashConcat(hash2) - assertArrayEquals((hash1.bytes + hash2.bytes).digestFor("SHA-256"), combined.bytes) - } - - @Test(timeout=300_000) - fun testConstants() { - assertArrayEquals(SecureHash.zeroHash.bytes, ByteArray(32)) - assertArrayEquals(SecureHash.allOnesHash.bytes, ByteArray(32) { 0xFF.toByte() }) - } -} - -private fun ByteArray.digestFor(algorithm: String): ByteArray { - return MessageDigest.getInstance(algorithm).digest(this) -} \ No newline at end of file diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/SecureRandomTest.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/SecureRandomTest.kt deleted file mode 100644 index d0c45cef39..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/SecureRandomTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package net.corda.deterministic.crypto - -import net.corda.core.crypto.CordaSecurityProvider -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test -import java.security.NoSuchAlgorithmException -import java.security.SecureRandom -import kotlin.test.assertFailsWith - -class SecureRandomTest { - private companion object { - init { - CordaSecurityProvider() - } - } - - @Test(timeout=300_000) - fun testNoCordaPRNG() { - val error = assertFailsWith { SecureRandom.getInstance("CordaPRNG") } - assertThat(error).hasMessage("CordaPRNG SecureRandom not available") - } -} \ No newline at end of file diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/TransactionSignatureTest.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/TransactionSignatureTest.kt deleted file mode 100644 index a775acf8e2..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/crypto/TransactionSignatureTest.kt +++ /dev/null @@ -1,143 +0,0 @@ -package net.corda.deterministic.crypto - -import net.corda.core.crypto.* -import net.corda.deterministic.KeyStoreProvider -import net.corda.deterministic.CheatingSecurityProvider -import net.corda.deterministic.verifier.LocalSerializationRule -import org.junit.* -import org.junit.rules.RuleChain -import java.security.* -import kotlin.test.* - -class TransactionSignatureTest { - companion object { - private const val KEYSTORE_PASSWORD = "deterministic" - private val testBytes = "12345678901234567890123456789012".toByteArray() - - private val keyStoreProvider = KeyStoreProvider("keystore/txsignature.pfx", KEYSTORE_PASSWORD) - private lateinit var keyPair: KeyPair - - @ClassRule - @JvmField - val rules: RuleChain = RuleChain.outerRule(LocalSerializationRule(TransactionSignatureTest::class)) - .around(keyStoreProvider) - - @BeforeClass - @JvmStatic - fun setupClass() { - keyPair = keyStoreProvider.getKeyPair("tx") - } - } - - /** Valid sign and verify. */ - @Test(timeout=300_000) - fun `Signature metadata full sign and verify`() { - // Create a SignableData object. - val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID)) - - // Sign the meta object. - val transactionSignature: TransactionSignature = CheatingSecurityProvider().use { - CryptoSignUtils.doSign(keyPair, signableData) - } - - // Check auto-verification. - assertTrue(transactionSignature.verify(testBytes.sha256())) - - // Check manual verification. - assertTrue(Crypto.doVerify(testBytes.sha256(), transactionSignature)) - } - - /** Verification should fail; corrupted metadata - clearData (Merkle root) has changed. */ - @Test(expected = SignatureException::class) - fun `Signature metadata full failure clearData has changed`() { - val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID)) - val transactionSignature = CheatingSecurityProvider().use { - CryptoSignUtils.doSign(keyPair, signableData) - } - Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature) - } - - @Test(timeout=300_000) - fun `Verify multi-tx signature`() { - // Deterministically create 5 txIds. - val txIds: List = IntRange(0, 4).map { byteArrayOf(it.toByte()).sha256() } - // Multi-tx signature. - val txSignature = signMultipleTx(txIds, keyPair) - - // The hash of all txIds are used as leaves. - val merkleTree = MerkleTree.getMerkleTree(txIds.map { it.sha256() }, DigestService.default) - - // We haven't added the partial tree yet. - assertNull(txSignature.partialMerkleTree) - // Because partial tree is still null, but we signed over a block of txs, verifying a single tx will fail. - assertFailsWith { Crypto.doVerify(txIds[3], txSignature) } - - // Create a partial tree for one tx. - val pmt = PartialMerkleTree.build(merkleTree, listOf(txIds[0].sha256())) - // Add the partial Merkle tree to the tx signature. - val txSignatureWithTree = TransactionSignature(txSignature.bytes, txSignature.by, txSignature.signatureMetadata, pmt) - - // Verify the corresponding txId with every possible way. - assertTrue(Crypto.doVerify(txIds[0], txSignatureWithTree)) - assertTrue(txSignatureWithTree.verify(txIds[0])) - assertTrue(Crypto.isValid(txIds[0], txSignatureWithTree)) - assertTrue(txSignatureWithTree.isValid(txIds[0])) - - // Verify the rest txs in the block, which are not included in the partial Merkle tree. - txIds.subList(1, txIds.size).forEach { - assertFailsWith { Crypto.doVerify(it, txSignatureWithTree) } - } - - // Test that the Merkle tree consists of hash(txId), not txId. - assertFailsWith { PartialMerkleTree.build(merkleTree, listOf(txIds[0])) } - - // What if we send the Full tree. This could be used if notaries didn't want to create a per tx partial tree. - // Create a partial tree for all txs, thus all leaves are included. - val pmtFull = PartialMerkleTree.build(merkleTree, txIds.map { it.sha256() }) - // Add the partial Merkle tree to the tx. - val txSignatureWithFullTree = TransactionSignature(txSignature.bytes, txSignature.by, txSignature.signatureMetadata, pmtFull) - - // All txs can be verified, as they are all included in the provided partial tree. - txIds.forEach { - assertTrue(Crypto.doVerify(it, txSignatureWithFullTree)) - } - } - - @Test(timeout=300_000) - fun `Verify one-tx signature`() { - val txId = "aTransaction".toByteArray().sha256() - // One-tx signature. - val txSignature = try { - signOneTx(txId, keyPair) - } catch (e: Throwable) { - e.cause?.printStackTrace() - throw e - } - - // partialMerkleTree should be null. - assertNull(txSignature.partialMerkleTree) - // Verify the corresponding txId with every possible way. - assertTrue(Crypto.doVerify(txId, txSignature)) - assertTrue(txSignature.verify(txId)) - assertTrue(Crypto.isValid(txId, txSignature)) - assertTrue(txSignature.isValid(txId)) - - // We signed the txId itself, not its hash (because it was a signature over one tx only and no partial tree has been received). - assertFailsWith { Crypto.doVerify(txId.sha256(), txSignature) } - } - - // Returns a TransactionSignature over the Merkle root, but the partial tree is null. - private fun signMultipleTx(txIds: List, keyPair: KeyPair): TransactionSignature { - val merkleTreeRoot = MerkleTree.getMerkleTree(txIds.map { it.sha256() }, DigestService.default).hash - return signOneTx(merkleTreeRoot, keyPair) - } - - // Returns a TransactionSignature over one SecureHash. - // Note that if one tx is to be signed, we don't create a Merkle tree and we directly sign over the txId. - private fun signOneTx(txId: SecureHash, keyPair: KeyPair): TransactionSignature { - val signableData = SignableData(txId, SignatureMetadata(3, Crypto.findSignatureScheme(keyPair.public).schemeNumberID)) - return CheatingSecurityProvider().use { - CryptoSignUtils.doSign(keyPair, signableData) - } - } -} diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/transactions/TransactionWithSignaturesTest.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/transactions/TransactionWithSignaturesTest.kt deleted file mode 100644 index e092ee371d..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/transactions/TransactionWithSignaturesTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.deterministic.transactions - -import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.TransactionSignature -import net.corda.core.transactions.TransactionWithSignatures -import org.junit.Test -import java.security.PublicKey - -class TransactionWithSignaturesTest { - @Test(timeout=300_000) - fun txWithSigs() { - val tx = object : TransactionWithSignatures { - override val id: SecureHash - get() = SecureHash.zeroHash - override val requiredSigningKeys: Set - get() = emptySet() - override val sigs: List - get() = emptyList() - - override fun getKeyDescriptions(keys: Set): List { - return emptyList() - } - } - tx.verifyRequiredSignatures() - tx.checkSignaturesAreValid() - tx.getMissingSigners() - tx.verifySignaturesExcept() - tx.verifySignaturesExcept(emptySet()) - } -} \ No newline at end of file diff --git a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/txverify/VerifyTransactionTest.kt b/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/txverify/VerifyTransactionTest.kt deleted file mode 100644 index f5012485f2..0000000000 --- a/core-deterministic/testing/src/test/kotlin/net/corda/deterministic/txverify/VerifyTransactionTest.kt +++ /dev/null @@ -1,29 +0,0 @@ -package net.corda.deterministic.txverify - -import net.corda.deterministic.bytesOfResource -import net.corda.deterministic.verifier.LocalSerializationRule -import net.corda.deterministic.verifier.verifyTransaction -import net.corda.finance.contracts.asset.Cash.Commands.* -import org.assertj.core.api.Assertions.assertThat -import org.junit.ClassRule -import org.junit.Test -import kotlin.test.assertFailsWith - -class VerifyTransactionTest { - companion object { - @ClassRule - @JvmField - val serialization = LocalSerializationRule(VerifyTransactionTest::class) - } - - @Test(timeout=300_000) - fun success() { - verifyTransaction(bytesOfResource("txverify/tx-success.bin")) - } - - @Test(timeout=300_000) - fun failure() { - val e = assertFailsWith { verifyTransaction(bytesOfResource("txverify/tx-failure.bin")) } - assertThat(e).hasMessageContaining("Required ${Move::class.java.canonicalName} command") - } -} diff --git a/core-deterministic/testing/src/test/resources/log4j2-test.xml b/core-deterministic/testing/src/test/resources/log4j2-test.xml deleted file mode 100644 index 4e309bf567..0000000000 --- a/core-deterministic/testing/src/test/resources/log4j2-test.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/core-deterministic/testing/verifier/build.gradle b/core-deterministic/testing/verifier/build.gradle deleted file mode 100644 index 334191cb9f..0000000000 --- a/core-deterministic/testing/verifier/build.gradle +++ /dev/null @@ -1,56 +0,0 @@ -plugins { - id 'java-library' - id 'net.corda.plugins.publish-utils' - id 'com.jfrog.artifactory' - id 'idea' -} -apply from: "${rootProject.projectDir}/deterministic.gradle" - -description 'Test utilities for deterministic contract verification' - -configurations { - deterministicArtifacts { - canBeResolved = false - } - - // Compile against the deterministic artifacts to ensure that we use only the deterministic API subset. - compileOnly.extendsFrom deterministicArtifacts - runtimeArtifacts.extendsFrom api -} - -dependencies { - deterministicArtifacts project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts') - deterministicArtifacts project(path: ':core-deterministic', configuration: 'deterministicArtifacts') - - runtimeArtifacts project(':serialization') - runtimeArtifacts project(':core') - - api "junit:junit:$junit_version" - runtimeOnly "org.junit.vintage:junit-vintage-engine:$junit_vintage_version" -} - -jar { - archiveBaseName = 'corda-deterministic-verifier' -} - -artifacts { - deterministicArtifacts jar - runtimeArtifacts jar - publish jar -} - -publish { - // Our published POM will contain dependencies on the non-deterministic Corda artifacts. - dependenciesFrom(configurations.runtimeArtifacts) { - defaultScope = 'compile' - } - name jar.archiveBaseName.get() -} - -idea { - module { - if (project.hasProperty("deterministic_idea_sdk")) { - jdkName project.property("deterministic_idea_sdk") as String - } - } -} diff --git a/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/LocalSerializationRule.kt b/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/LocalSerializationRule.kt deleted file mode 100644 index 35fbe9611c..0000000000 --- a/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/LocalSerializationRule.kt +++ /dev/null @@ -1,82 +0,0 @@ -package net.corda.deterministic.verifier - -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.SerializationContext.UseCase.P2P -import net.corda.core.serialization.SerializationCustomSerializer -import net.corda.core.serialization.SerializationWhitelist -import net.corda.core.serialization.internal.SerializationEnvironment -import net.corda.core.serialization.internal._driverSerializationEnv -import net.corda.serialization.internal.* -import net.corda.serialization.internal.amqp.* -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement -import kotlin.reflect.KClass -import kotlin.reflect.jvm.jvmName - -class LocalSerializationRule(private val label: String) : TestRule { - constructor(klass: KClass<*>) : this(klass.jvmName) - - private companion object { - private val AMQP_P2P_CONTEXT = SerializationContextImpl( - amqpMagic, - LocalSerializationRule::class.java.classLoader, - GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), - emptyMap(), - true, - P2P, - null - ) - } - - override fun apply(base: Statement, description: Description): Statement { - return object : Statement() { - override fun evaluate() { - init() - try { - base.evaluate() - } finally { - clear() - } - } - } - } - - fun reset() { - clear() - init() - } - - private fun init() { - _driverSerializationEnv.set(createTestSerializationEnv()) - } - - private fun clear() { - _driverSerializationEnv.set(null) - } - - private fun createTestSerializationEnv(): SerializationEnvironment { - val factory = SerializationFactoryImpl(mutableMapOf()).apply { - registerScheme(AMQPSerializationScheme(emptySet(), emptySet(), AccessOrderLinkedHashMap(128))) - } - return SerializationEnvironment.with(factory, AMQP_P2P_CONTEXT) - } - - private class AMQPSerializationScheme( - cordappCustomSerializers: Set>, - cordappSerializationWhitelists: Set, - serializerFactoriesForContexts: AccessOrderLinkedHashMap - ) : AbstractAMQPSerializationScheme(cordappCustomSerializers, cordappSerializationWhitelists, serializerFactoriesForContexts) { - override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory { - throw UnsupportedOperationException() - } - - override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory { - throw UnsupportedOperationException() - } - - override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean { - return canDeserializeVersion(magic) && target == P2P - } - } -} diff --git a/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/MockContractAttachment.kt b/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/MockContractAttachment.kt deleted file mode 100644 index 70b5bc2c10..0000000000 --- a/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/MockContractAttachment.kt +++ /dev/null @@ -1,19 +0,0 @@ -package net.corda.deterministic.verifier - -import net.corda.core.contracts.ContractClassName -import net.corda.core.crypto.SecureHash -import net.corda.core.identity.Party -import net.corda.core.internal.AbstractAttachment -import net.corda.core.serialization.CordaSerializable -import java.security.PublicKey - -// A valid zip file with 1 entry. -val simpleZip = byteArrayOf(80, 75, 3, 4, 20, 0, 8, 8, 8, 0, 15, 113, 79, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 47, 97, -2, -54, 0, 0, 75, 4, 0, 80, 75, 7, 8, 67, -66, -73, -24, 3, 0, 0, 0, 1, 0, 0, 0, 80, 75, 1, 2, 20, 0, 20, 0, 8, 8, 8, 0, 15, 113, 79, 78, 67, -66, -73, -24, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 97, -2, -54, 0, 0, 80, 75, 5, 6, 0, 0, 0, 0, 1, 0, 1, 0, 52, 0, 0, 0, 55, 0, 0, 0, 0, 0) - -@CordaSerializable -class MockContractAttachment( - override val id: SecureHash = SecureHash.zeroHash, - val contract: ContractClassName, - override val signerKeys: List = emptyList(), - override val signers: List = emptyList() -) : AbstractAttachment({ simpleZip }, "app") \ No newline at end of file diff --git a/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/SampleData.kt b/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/SampleData.kt deleted file mode 100644 index 9c4cfdcb59..0000000000 --- a/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/SampleData.kt +++ /dev/null @@ -1,6 +0,0 @@ -@file:JvmName("SampleData") -package net.corda.deterministic.verifier - -import net.corda.core.contracts.TypeOnlyCommandData - -object SampleCommandData : TypeOnlyCommandData() diff --git a/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/TransactionVerificationRequest.kt b/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/TransactionVerificationRequest.kt deleted file mode 100644 index 3c9fde9c06..0000000000 --- a/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/TransactionVerificationRequest.kt +++ /dev/null @@ -1,35 +0,0 @@ -package net.corda.deterministic.verifier - -import net.corda.core.contracts.Attachment -import net.corda.core.contracts.ContractAttachment -import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER -import net.corda.core.internal.toLtxDjvmInternal -import net.corda.core.node.NetworkParameters -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.SerializedBytes -import net.corda.core.serialization.deserialize -import net.corda.core.transactions.LedgerTransaction -import net.corda.core.transactions.WireTransaction - -@Suppress("MemberVisibilityCanBePrivate") -//TODO the use of deprecated toLedgerTransaction need to be revisited as resolveContractAttachment requires attachments of the transactions which created input states... -//TODO ...to check contract version non downgrade rule, currently dummy Attachment if not fund is used which sets contract version to '1' -@CordaSerializable -class TransactionVerificationRequest(val wtxToVerify: SerializedBytes, - val dependencies: Array>, - val attachments: Array, - val networkParameters: SerializedBytes) { - fun toLedgerTransaction(): LedgerTransaction { - val deps = dependencies.map { it.deserialize() }.associateBy(WireTransaction::id) - val attachments = attachments.map { it.deserialize() } - val attachmentMap = attachments - .mapNotNull { it as? MockContractAttachment } - .associateBy(Attachment::id) { ContractAttachment(it, it.contract, uploader = DEPLOYED_CORDAPP_UPLOADER) } - @Suppress("DEPRECATION") - return wtxToVerify.deserialize().toLtxDjvmInternal( - resolveAttachment = { attachmentMap[it] }, - resolveStateRef = { deps[it.txhash]?.outputs?.get(it.index) }, - resolveParameters = { networkParameters.deserialize() } - ) - } -} diff --git a/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/Verifier.kt b/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/Verifier.kt deleted file mode 100644 index e7a710d707..0000000000 --- a/core-deterministic/testing/verifier/src/main/kotlin/net/corda/deterministic/verifier/Verifier.kt +++ /dev/null @@ -1,21 +0,0 @@ -@file:JvmName("Verifier") -package net.corda.deterministic.verifier - -import net.corda.core.serialization.deserialize -import net.corda.core.transactions.LedgerTransaction - -/** - * We assume the signatures were already checked outside the sandbox: the purpose of this code - * is simply to check the sensitive, app-specific parts of a transaction. - * - * TODO: Transaction data is meant to be encrypted under an enclave-private key. - */ -@Throws(Exception::class) -fun verifyTransaction(reqBytes: ByteArray) { - deserialize(reqBytes).verify() -} - -private fun deserialize(reqBytes: ByteArray): LedgerTransaction { - return reqBytes.deserialize() - .toLedgerTransaction() -} diff --git a/core-tests/build.gradle b/core-tests/build.gradle index bfdda8dfc9..a27022fc12 100644 --- a/core-tests/build.gradle +++ b/core-tests/build.gradle @@ -93,6 +93,8 @@ dependencies { smokeTestCompile project(':smoke-test-utils') smokeTestCompile "org.assertj:assertj-core:${assertj_version}" + // used by FinalityFlowTests + testCompile project(':testing:cordapps:cashobservers') } configurations { diff --git a/core-tests/src/test/kotlin/net/corda/coretests/contracts/ConstraintsPropagationTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/contracts/ConstraintsPropagationTests.kt index c668c8a294..ee73c85bda 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/contracts/ConstraintsPropagationTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/contracts/ConstraintsPropagationTests.kt @@ -22,6 +22,7 @@ import net.corda.core.crypto.SecureHash.Companion.allOnesHash import net.corda.core.crypto.SecureHash.Companion.zeroHash import net.corda.core.crypto.SignableData import net.corda.core.crypto.SignatureMetadata +import net.corda.core.crypto.sign import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.internal.canBeTransitionedFrom @@ -61,6 +62,7 @@ class ConstraintsPropagationTests { val testSerialization = SerializationEnvironmentRule() private companion object { + val DUMMY_NOTARY_IDENTITY = TestIdentity(DUMMY_NOTARY_NAME, 20) val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party val ALICE = TestIdentity(CordaX500Name("ALICE", "London", "GB")) val ALICE_PARTY get() = ALICE.party @@ -389,7 +391,8 @@ class ConstraintsPropagationTests { requireSupportedHashType(wireTransaction) val nodeKey = ALICE_PUBKEY val sigs = listOf(keyManagementService.sign( - SignableData(wireTransaction.id, SignatureMetadata(4, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey)) + SignableData(wireTransaction.id, SignatureMetadata(4, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey), + DUMMY_NOTARY_IDENTITY.keyPair.sign(SignableData(wireTransaction.id, SignatureMetadata(4, Crypto.findSignatureScheme(DUMMY_NOTARY_IDENTITY.publicKey).schemeNumberID)))) recordTransactions(SignedTransaction(wireTransaction, sigs)) } diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowTest.kt index 54678ff3c6..e6994316a8 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowTest.kt @@ -30,7 +30,7 @@ import java.util.* class ContractUpgradeFlowTest : WithContracts, WithFinality { companion object { - private val classMockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, DUMMY_CONTRACTS_CORDAPP, enclosedCordapp())) + private val classMockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, FINANCE_WORKFLOWS_CORDAPP, DUMMY_CONTRACTS_CORDAPP, enclosedCordapp())) @JvmStatic @AfterClass diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt index 1d13b53c66..9c4ecf4d99 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt @@ -1,32 +1,95 @@ package net.corda.coretests.flows +import co.paralleluniverse.fibers.Suspendable import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat +import net.corda.core.contracts.Amount +import net.corda.core.contracts.PartyAndReference +import net.corda.core.contracts.StateAndContract +import net.corda.core.contracts.StateAndRef +import net.corda.core.contracts.TransactionVerificationException +import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.TransactionSignature import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowException +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.NotaryError +import net.corda.core.flows.NotaryException +import net.corda.core.flows.NotarySigCheck +import net.corda.core.flows.ReceiveFinalityFlow +import net.corda.core.flows.ReceiveTransactionFlow +import net.corda.core.flows.ReceiverDistributionRecord +import net.corda.core.flows.SendTransactionFlow +import net.corda.core.flows.SenderDistributionRecord +import net.corda.core.flows.StartableByRPC +import net.corda.core.flows.UnexpectedFlowEndException +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party +import net.corda.core.internal.FetchDataFlow +import net.corda.core.internal.PLATFORM_VERSION +import net.corda.core.internal.PlatformVersionSwitches +import net.corda.core.internal.ServiceHubCoreInternal +import net.corda.core.node.StatesToRecord +import net.corda.core.node.services.TransactionStatus import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow -import net.corda.coretests.flows.WithFinality.FinalityInvoker -import net.corda.finance.POUNDS -import net.corda.finance.contracts.asset.Cash -import net.corda.finance.issuedBy -import net.corda.testing.core.* +import net.corda.core.utilities.unwrap import net.corda.coretesting.internal.matchers.flow.willReturn import net.corda.coretesting.internal.matchers.flow.willThrow -import net.corda.testing.node.internal.* +import net.corda.coretests.flows.WithFinality.FinalityInvoker +import net.corda.coretests.flows.WithFinality.OldFinalityInvoker +import net.corda.finance.GBP +import net.corda.finance.POUNDS +import net.corda.finance.contracts.asset.Cash +import net.corda.finance.flows.CashIssueFlow +import net.corda.finance.flows.CashPaymentFlow +import net.corda.finance.issuedBy +import net.corda.finance.test.flows.CashIssueWithObserversFlow +import net.corda.finance.test.flows.CashPaymentWithObserversFlow +import net.corda.node.services.persistence.DBTransactionStorage +import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery.DBReceiverDistributionRecord +import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery.DBSenderDistributionRecord +import net.corda.node.services.persistence.HashedDistributionList +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.CHARLIE_NAME +import net.corda.testing.core.TestIdentity +import net.corda.testing.core.singleIdentity +import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP +import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP +import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP +import net.corda.testing.node.internal.InternalMockNetwork +import net.corda.testing.node.internal.InternalMockNodeParameters +import net.corda.testing.node.internal.MOCK_VERSION_INFO +import net.corda.testing.node.internal.TestCordappInternal +import net.corda.testing.node.internal.TestStartedNode +import net.corda.testing.node.internal.cordappWithPackages +import net.corda.testing.node.internal.enclosedCordapp +import net.corda.testing.node.internal.findCordapp import org.assertj.core.api.Assertions.assertThat import org.junit.After +import org.junit.Assert.assertNotNull import org.junit.Test +import java.sql.SQLException +import java.util.Random +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.fail class FinalityFlowTests : WithFinality { companion object { private val CHARLIE = TestIdentity(CHARLIE_NAME, 90).party } - override val mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, enclosedCordapp(), - CustomCordapp(targetPlatformVersion = 3, classes = setOf(FinalityFlow::class.java)))) - + override val mockNet = InternalMockNetwork(cordappsForAllNodes = setOf(FINANCE_CONTRACTS_CORDAPP, FINANCE_WORKFLOWS_CORDAPP, DUMMY_CONTRACTS_CORDAPP, enclosedCordapp(), + findCordapp("net.corda.finance.test.flows"))) private val aliceNode = makeNode(ALICE_NAME) private val notary = mockNet.defaultNotaryIdentity @@ -60,9 +123,8 @@ class FinalityFlowTests : WithFinality { fun `allow use of the old API if the CorDapp target version is 3`() { val oldBob = createBob(cordapps = listOf(tokenOldCordapp())) val stx = aliceNode.issuesCashTo(oldBob) - @Suppress("DEPRECATION") - aliceNode.startFlowAndRunNetwork(FinalityFlow(stx)).resultFuture.getOrThrow() - assertThat(oldBob.services.validatedTransactions.getTransaction(stx.id)).isNotNull() + aliceNode.startFlowAndRunNetwork(OldFinalityInvoker(stx)).resultFuture.getOrThrow() + assertThat(oldBob.services.validatedTransactions.getTransaction(stx.id)).isNotNull } @Test(timeout=300_000) @@ -76,12 +138,498 @@ class FinalityFlowTests : WithFinality { oldRecipients = setOf(oldBob.info.singleIdentity()) )).resultFuture resultFuture.getOrThrow() - assertThat(newCharlie.services.validatedTransactions.getTransaction(stx.id)).isNotNull() - assertThat(oldBob.services.validatedTransactions.getTransaction(stx.id)).isNotNull() + assertThat(newCharlie.services.validatedTransactions.getTransaction(stx.id)).isNotNull + assertThat(oldBob.services.validatedTransactions.getTransaction(stx.id)).isNotNull } - private fun createBob(cordapps: List = emptyList()): TestStartedNode { - return mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME, additionalCordapps = cordapps)) + @Test(timeout=300_000) + fun `two phase finality flow transaction`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY) + + val stx = aliceNode.startFlow(CashIssueFlow(Amount(1000L, GBP), OpaqueBytes.of(1), notary)).resultFuture.getOrThrow().stx + aliceNode.startFlowAndRunNetwork(CashPaymentFlow(Amount(100, GBP), bobNode.info.singleIdentity())).resultFuture.getOrThrow() + + assertThat(aliceNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + assertThat(bobNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + } + + @Test(timeout=300_000) + fun `two phase finality flow initiator to pre-2PF peer`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY - 1) + + val stx = aliceNode.startFlow(CashIssueFlow(Amount(1000L, GBP), OpaqueBytes.of(1), notary)).resultFuture.getOrThrow().stx + aliceNode.startFlowAndRunNetwork(CashPaymentFlow(Amount(100, GBP), bobNode.info.singleIdentity())).resultFuture.getOrThrow() + + assertThat(aliceNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + assertThat(bobNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + } + + @Test(timeout=300_000) + fun `pre-2PF initiator to two phase finality flow peer`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY - 1) + + val stx = bobNode.startFlow(CashIssueFlow(Amount(1000L, GBP), OpaqueBytes.of(1), notary)).resultFuture.getOrThrow().stx + bobNode.startFlowAndRunNetwork(CashPaymentFlow(Amount(100, GBP), aliceNode.info.singleIdentity())).resultFuture.getOrThrow() + + assertThat(aliceNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + assertThat(bobNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + } + + @Test(timeout=300_000) + fun `two phase finality flow double spend transaction`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY) + + val ref = aliceNode.startFlowAndRunNetwork(IssueFlow(notary)).resultFuture.getOrThrow() + val stx = aliceNode.startFlowAndRunNetwork(SpendFlow(ref, bobNode.info.singleIdentity())).resultFuture.getOrThrow() + + val (_, txnStatusAlice) = aliceNode.services.validatedTransactions.getTransactionWithStatus(stx.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusAlice) + val (_, txnStatusBob) = bobNode.services.validatedTransactions.getTransactionWithStatus(stx.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusBob) + + try { + aliceNode.startFlowAndRunNetwork(SpendFlow(ref, bobNode.info.singleIdentity())).resultFuture.getOrThrow() + } + catch (e: NotaryException) { + val stxId = (e.error as NotaryError.Conflict).txId + assertNull(aliceNode.services.validatedTransactions.getTransactionWithStatus(stxId)) + // Note: double spend error not propagated to peers by default (corDapp PV = 3) + // Un-notarised txn clean-up occurs in ReceiveFinalityFlow upon receipt of UnexpectedFlowEndException + assertNull(aliceNode.services.validatedTransactions.getTransactionWithStatus(stxId)) + assertTxnRemovedFromDatabase(aliceNode, stxId) + } + } + + @Test(timeout=300_000) + fun `two phase finality flow double spend transaction with double spend handling`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY) + + val ref = aliceNode.startFlowAndRunNetwork(IssueFlow(notary)).resultFuture.getOrThrow() + val stx = aliceNode.startFlowAndRunNetwork(SpendFlow(ref, bobNode.info.singleIdentity())).resultFuture.getOrThrow() + + val (_, txnStatusAlice) = aliceNode.services.validatedTransactions.getTransactionWithStatus(stx.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusAlice) + val (_, txnStatusBob) = bobNode.services.validatedTransactions.getTransactionWithStatus(stx.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusBob) + + try { + aliceNode.startFlowAndRunNetwork(SpendFlow(ref, bobNode.info.singleIdentity(), handlePropagatedNotaryError = true)).resultFuture.getOrThrow() + } + catch (e: NotaryException) { + val stxId = (e.error as NotaryError.Conflict).txId + assertNull(aliceNode.services.validatedTransactions.getTransactionWithStatus(stxId)) + assertTxnRemovedFromDatabase(aliceNode, stxId) + assertNull(bobNode.services.validatedTransactions.getTransactionWithStatus(stxId)) + assertTxnRemovedFromDatabase(bobNode, stxId) + } + + try { + aliceNode.startFlowAndRunNetwork(SpendFlow(ref, bobNode.info.singleIdentity(), handlePropagatedNotaryError = false)).resultFuture.getOrThrow() + } + catch (e: NotaryException) { + val stxId = (e.error as NotaryError.Conflict).txId + assertNull(aliceNode.services.validatedTransactions.getTransactionWithStatus(stxId)) + assertTxnRemovedFromDatabase(aliceNode, stxId) + val (_, txnStatus) = bobNode.services.validatedTransactions.getTransactionWithStatus(stxId) ?: fail() + assertEquals(TransactionStatus.IN_FLIGHT, txnStatus) + } + } + + private fun assertTxnRemovedFromDatabase(node: TestStartedNode, stxId: SecureHash) { + val fromDb = node.database.transaction { + session.createQuery( + "from ${DBTransactionStorage.DBTransaction::class.java.name} where tx_id = :transactionId", + DBTransactionStorage.DBTransaction::class.java + ).setParameter("transactionId", stxId.toString()).resultList + } + assertEquals(0, fromDb.size) + } + + @Test(timeout=300_000) + fun `two phase finality flow double spend transaction from pre-2PF initiator`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY - 1) + + val ref = bobNode.startFlowAndRunNetwork(IssueFlow(notary)).resultFuture.getOrThrow() + val stx = bobNode.startFlowAndRunNetwork(SpendFlow(ref, aliceNode.info.singleIdentity())).resultFuture.getOrThrow() + + val (_, txnStatusAlice) = aliceNode.services.validatedTransactions.getTransactionWithStatus(stx.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusAlice) + val (_, txnStatusBob) = bobNode.services.validatedTransactions.getTransactionWithStatus(stx.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusBob) + + try { + bobNode.startFlowAndRunNetwork(SpendFlow(ref, aliceNode.info.singleIdentity())).resultFuture.getOrThrow() + } + catch (e: NotaryException) { + val stxId = (e.error as NotaryError.Conflict).txId + assertNull(bobNode.services.validatedTransactions.getTransactionWithStatus(stxId)) + assertTxnRemovedFromDatabase(bobNode, stxId) + assertNull(aliceNode.services.validatedTransactions.getTransactionWithStatus(stxId)) + assertTxnRemovedFromDatabase(aliceNode, stxId) + } + } + + @Test(timeout=300_000) + fun `two phase finality flow double spend transaction to pre-2PF peer`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY - 1) + + val ref = aliceNode.startFlowAndRunNetwork(IssueFlow(notary)).resultFuture.getOrThrow() + val stx = aliceNode.startFlowAndRunNetwork(SpendFlow(ref, bobNode.info.singleIdentity())).resultFuture.getOrThrow() + + val (_, txnStatusAlice) = aliceNode.services.validatedTransactions.getTransactionWithStatus(stx.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusAlice) + val (_, txnStatusBob) = bobNode.services.validatedTransactions.getTransactionWithStatus(stx.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusBob) + + try { + aliceNode.startFlowAndRunNetwork(SpendFlow(ref, bobNode.info.singleIdentity())).resultFuture.getOrThrow() + } + catch (e: NotaryException) { + val stxId = (e.error as NotaryError.Conflict).txId + assertNull(aliceNode.services.validatedTransactions.getTransactionWithStatus(stxId)) + assertTxnRemovedFromDatabase(aliceNode, stxId) + assertNull(bobNode.services.validatedTransactions.getTransactionWithStatus(stxId)) + assertTxnRemovedFromDatabase(bobNode, stxId) + } + } + + @Test(timeout=300_000) + fun `two phase finality flow speedy spender`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY) + + val ref = aliceNode.startFlowAndRunNetwork(IssueFlow(notary)).resultFuture.getOrThrow() + val notarisedStxn1 = aliceNode.startFlowAndRunNetwork(SpeedySpendFlow(ref, bobNode.info.singleIdentity())).resultFuture.getOrThrow() + + val (_, txnStatusAlice) = aliceNode.services.validatedTransactions.getTransactionWithStatus(notarisedStxn1.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusAlice) + val (_, txnStatusBob) = bobNode.services.validatedTransactions.getTransactionWithStatus(notarisedStxn1.id) ?: fail() + assertEquals(TransactionStatus.IN_FLIGHT, txnStatusBob) + + // now lets attempt a new spend with the new output of the previous transaction + val newStateRef = notarisedStxn1.coreTransaction.outRef(1) + val notarisedStxn2 = aliceNode.startFlowAndRunNetwork(SpeedySpendFlow(newStateRef, bobNode.info.singleIdentity())).resultFuture.getOrThrow() + + // the original transaction is now finalised at Bob (despite the original flow not completing) because Bob resolved the + // original transaction from Alice in the second transaction (and Alice had already notarised and finalised the original transaction) + val (_, txnStatusBobAgain) = bobNode.services.validatedTransactions.getTransactionWithStatus(notarisedStxn1.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusBobAgain) + + val (_, txnStatusAlice2) = aliceNode.services.validatedTransactions.getTransactionWithStatus(notarisedStxn2.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusAlice2) + val (_, txnStatusBob2) = bobNode.services.validatedTransactions.getTransactionWithStatus(notarisedStxn2.id) ?: fail() + assertEquals(TransactionStatus.IN_FLIGHT, txnStatusBob2) + + // Validate attempt at flow finalisation by Bob has no effect on outcome. + val finaliseStxn1 = bobNode.startFlowAndRunNetwork(FinaliseSpeedySpendFlow(notarisedStxn1.id, notarisedStxn1.sigs)).resultFuture.getOrThrow() + val (_, txnStatusBobYetAgain) = bobNode.services.validatedTransactions.getTransactionWithStatus(finaliseStxn1.id) ?: fail() + assertEquals(TransactionStatus.VERIFIED, txnStatusBobYetAgain) + } + + @Test(timeout=300_000) + fun `two phase finality flow keeps un-notarised transaction where initiator fails to send notary signature`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY) + + val ref = aliceNode.startFlowAndRunNetwork(IssueFlow(notary)).resultFuture.getOrThrow() + try { + aliceNode.startFlowAndRunNetwork(MimicFinalityFailureFlow(ref, bobNode.info.singleIdentity())).resultFuture.getOrThrow() + } + catch (e: UnexpectedFlowEndException) { + val stxId = SecureHash.parse(e.message) + val (_, txnStatusBob) = bobNode.services.validatedTransactions.getTransactionWithStatus(stxId) ?: fail() + assertEquals(TransactionStatus.IN_FLIGHT, txnStatusBob) + } + } + + @Test(timeout=300_000) + fun `two phase finality flow issuance transaction with observers`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY) + + val stx = aliceNode.startFlowAndRunNetwork(CashIssueWithObserversFlow( + Amount(1000L, GBP), OpaqueBytes.of(1), notary, + observers = setOf(bobNode.info.singleIdentity()))).resultFuture.getOrThrow().stx + + assertThat(aliceNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + assertThat(bobNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + + val sdrs = getSenderRecoveryData(stx.id, aliceNode.database).apply { + assertEquals(1, this.size) + assertEquals(StatesToRecord.ONLY_RELEVANT, this[0].senderStatesToRecord) + assertEquals(StatesToRecord.ALL_VISIBLE, this[0].receiverStatesToRecord) + assertEquals(SecureHash.sha256(BOB_NAME.toString()), this[0].peerPartyId) + } + val rdr = getReceiverRecoveryData(stx.id, bobNode).apply { + assertNotNull(this) + val hashedDL = HashedDistributionList.decrypt(this!!.encryptedDistributionList.bytes, aliceNode.internals.encryptionService) + assertEquals(StatesToRecord.ONLY_RELEVANT, hashedDL.senderStatesToRecord) + assertEquals(SecureHash.sha256(aliceNode.info.singleIdentity().name.toString()), this.peerPartyId) + assertEquals(mapOf(SecureHash.sha256(BOB_NAME.toString()) to StatesToRecord.ALL_VISIBLE), hashedDL.peerHashToStatesToRecord) + } + validateSenderAndReceiverTimestamps(sdrs, rdr!!) + } + + @Test(timeout=300_000) + fun `two phase finality flow payment transaction with observers`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY) + val charlieNode = createNode(CHARLIE_NAME, platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY) + + // issue some cash + aliceNode.startFlow(CashIssueFlow(Amount(1000L, GBP), OpaqueBytes.of(1), notary)).resultFuture.getOrThrow().stx + + // standard issuance with observers passed in as FinalityFlow sessions + val stx = aliceNode.startFlowAndRunNetwork(CashPaymentWithObserversFlow( + amount = Amount(100L, GBP), + recipient = bobNode.info.singleIdentity(), + observers = setOf(charlieNode.info.singleIdentity()))).resultFuture.getOrThrow() + + assertThat(aliceNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + assertThat(bobNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + assertThat(charlieNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + + val sdrs = getSenderRecoveryData(stx.id, aliceNode.database).apply { + assertEquals(2, this.size) + assertEquals(StatesToRecord.ONLY_RELEVANT, this[0].senderStatesToRecord) + assertEquals(SecureHash.sha256(BOB_NAME.toString()), this[0].peerPartyId) + assertEquals(StatesToRecord.ALL_VISIBLE, this[1].receiverStatesToRecord) + assertEquals(SecureHash.sha256(CHARLIE_NAME.toString()), this[1].peerPartyId) + } + val rdr = getReceiverRecoveryData(stx.id, bobNode).apply { + assertNotNull(this) + val hashedDL = HashedDistributionList.decrypt(this!!.encryptedDistributionList.bytes, aliceNode.internals.encryptionService) + assertEquals(StatesToRecord.ONLY_RELEVANT, hashedDL.senderStatesToRecord) + assertEquals(SecureHash.sha256(aliceNode.info.singleIdentity().name.toString()), this.peerPartyId) + // note: Charlie assertion here is using the hinted StatesToRecord value passed to it from Alice + assertEquals(mapOf( + SecureHash.sha256(BOB_NAME.toString()) to StatesToRecord.ONLY_RELEVANT, + SecureHash.sha256(CHARLIE_NAME.toString()) to StatesToRecord.ALL_VISIBLE + ), hashedDL.peerHashToStatesToRecord) + } + validateSenderAndReceiverTimestamps(sdrs, rdr!!) + + // exercise the new FinalityFlow observerSessions constructor parameter + val stx3 = aliceNode.startFlowAndRunNetwork(CashPaymentWithObserversFlow( + amount = Amount(100L, GBP), + recipient = bobNode.info.singleIdentity(), + observers = setOf(charlieNode.info.singleIdentity()), + useObserverSessions = true)).resultFuture.getOrThrow() + + assertThat(aliceNode.services.validatedTransactions.getTransaction(stx3.id)).isNotNull + assertThat(bobNode.services.validatedTransactions.getTransaction(stx3.id)).isNotNull + assertThat(charlieNode.services.validatedTransactions.getTransaction(stx3.id)).isNotNull + + val senderDistributionRecords = getSenderRecoveryData(stx3.id, aliceNode.database).apply { + assertEquals(2, this.size) + assertEquals(this[0].timestamp, this[1].timestamp) + } + getReceiverRecoveryData(stx3.id, bobNode).apply { + assertThat(this).isNotNull + assertEquals(senderDistributionRecords[0].timestamp, this!!.timestamp) + } + getReceiverRecoveryData(stx3.id, charlieNode).apply { + assertThat(this).isNotNull + assertEquals(senderDistributionRecords[0].timestamp, this!!.timestamp) + } + } + + private fun validateSenderAndReceiverTimestamps(sdrs: List, rdr: ReceiverDistributionRecord) { + sdrs.map { + assertEquals(it.timestamp, rdr.timestamp) + } + } + + @Test(timeout=300_000) + fun `two phase finality flow payment transaction using confidential identities`() { + val bobNode = createBob(platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY) + + aliceNode.startFlow(CashIssueFlow(Amount(1000L, GBP), OpaqueBytes.of(1), notary)).resultFuture.getOrThrow().stx + val stx = aliceNode.startFlowAndRunNetwork(CashPaymentFlow( + amount = Amount(100L, GBP), + recipient = bobNode.info.singleIdentity(), + anonymous = true)).resultFuture.getOrThrow().stx + + assertThat(aliceNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + assertThat(bobNode.services.validatedTransactions.getTransaction(stx.id)).isNotNull + + val sdr = getSenderRecoveryData(stx.id, aliceNode.database).apply { + assertEquals(1, this.size) + assertEquals(StatesToRecord.ONLY_RELEVANT, this[0].senderStatesToRecord) + assertEquals(SecureHash.sha256(BOB_NAME.toString()), this[0].peerPartyId) + } + val rdr = getReceiverRecoveryData(stx.id, bobNode).apply { + assertNotNull(this) + val hashedDL = HashedDistributionList.decrypt(this!!.encryptedDistributionList.bytes, aliceNode.internals.encryptionService) + assertEquals(StatesToRecord.ONLY_RELEVANT, hashedDL.senderStatesToRecord) + assertEquals(SecureHash.sha256(aliceNode.info.singleIdentity().name.toString()), this.peerPartyId) + assertEquals(mapOf(SecureHash.sha256(BOB_NAME.toString()) to StatesToRecord.ONLY_RELEVANT), hashedDL.peerHashToStatesToRecord) + } + validateSenderAndReceiverTimestamps(sdr, rdr!!) + } + + private fun getSenderRecoveryData(id: SecureHash, database: CordaPersistence): List { + val fromDb = database.transaction { + session.createQuery( + "from ${DBSenderDistributionRecord::class.java.name} where transaction_id = :transactionId", + DBSenderDistributionRecord::class.java + ).setParameter("transactionId", id.toString()).resultList + } + return fromDb.map { it.toSenderDistributionRecord() } + } + + private fun getReceiverRecoveryData(txId: SecureHash, receiver: TestStartedNode): ReceiverDistributionRecord? { + return receiver.database.transaction { + session.createQuery( + "from ${DBReceiverDistributionRecord::class.java.name} where transaction_id = :transactionId", + DBReceiverDistributionRecord::class.java + ).setParameter("transactionId", txId.toString()).resultList + }.singleOrNull()?.toReceiverDistributionRecord() + } + + @StartableByRPC + class IssueFlow(val notary: Party) : FlowLogic>() { + + @Suspendable + override fun call(): StateAndRef { + val partyAndReference = PartyAndReference(ourIdentity, OpaqueBytes.of(1)) + val txBuilder = DummyContract.generateInitial(Random().nextInt(), notary, partyAndReference) + val signedTransaction = serviceHub.signInitialTransaction(txBuilder, ourIdentity.owningKey) + val notarised = subFlow(FinalityFlow(signedTransaction, emptySet())) + return notarised.coreTransaction.outRef(0) + } + } + + @StartableByRPC + @InitiatingFlow + class SpendFlow(private val stateAndRef: StateAndRef, private val newOwner: Party, + private val handlePropagatedNotaryError: Boolean = false) : FlowLogic() { + + @Suspendable + override fun call(): SignedTransaction { + val txBuilder = DummyContract.move(stateAndRef, newOwner) + val signedTransaction = serviceHub.signInitialTransaction(txBuilder, ourIdentity.owningKey) + val sessionWithCounterParty = initiateFlow(newOwner) + sessionWithCounterParty.send(handlePropagatedNotaryError) + return subFlow(FinalityFlow(signedTransaction, setOf(sessionWithCounterParty))) + } + } + + @Suppress("unused") + @InitiatedBy(SpendFlow::class) + class AcceptSpendFlow(private val otherSide: FlowSession) : FlowLogic() { + + @Suspendable + override fun call() { + val handleNotaryError = otherSide.receive().unwrap { it } + val stx = subFlow(ReceiveFinalityFlow(otherSide, handlePropagatedNotaryError = handleNotaryError)) + stx.verify(serviceHub) + } + } + + /** + * This flow allows an Initiator to race ahead of a Receiver when using Two Phase Finality. + * The initiator transaction will be finalised, so output states can be used in a follow-up transaction. + * The receiver transaction will not be finalised, causing ledger inconsistency. + */ + @StartableByRPC + @InitiatingFlow + class SpeedySpendFlow(private val stateAndRef: StateAndRef, private val newOwner: Party) : FlowLogic() { + + @Suspendable + override fun call(): SignedTransaction { + val newState = StateAndContract(DummyContract.SingleOwnerState(99999, ourIdentity), DummyContract.PROGRAM_ID) + val txBuilder = DummyContract.move(stateAndRef, newOwner).withItems(newState) + val signedTransaction = serviceHub.signInitialTransaction(txBuilder, ourIdentity.owningKey) + val sessionWithCounterParty = initiateFlow(newOwner) + try { + subFlow(FinalityFlow(signedTransaction, setOf(sessionWithCounterParty))) + } + catch (e: FinalisationFailedException) { + // expected (transaction has been notarised by Initiator) + return e.notarisedTxn + } + return signedTransaction + } + } + + @Suppress("unused") + @InitiatedBy(SpeedySpendFlow::class) + class AcceptSpeedySpendFlow(private val otherSideSession: FlowSession) : FlowLogic() { + + @Suspendable + override fun call(): SignedTransaction { + // Mimic ReceiveFinalityFlow but fail to finalise + try { + val stx = subFlow(ReceiveTransactionFlow(otherSideSession, false, StatesToRecord.ONLY_RELEVANT, true)) + require(NotarySigCheck.needsNotarySignature(stx)) + logger.info("Peer recording transaction without notary signature.") + (serviceHub as ServiceHubCoreInternal).recordUnnotarisedTransaction(stx) + otherSideSession.send(FetchDataFlow.Request.End) // Finish fetching data (overrideAutoAck) + logger.info("Peer recorded transaction without notary signature.") + + val notarySignatures = otherSideSession.receive>() + .unwrap { it } + logger.info("Peer received notarised signature.") + (serviceHub as ServiceHubCoreInternal).finalizeTransactionWithExtraSignatures(stx + notarySignatures, notarySignatures, StatesToRecord.ONLY_RELEVANT) + throw FinalisationFailedException(stx + notarySignatures) + } + catch (e: SQLException) { + logger.error("Peer failure upon recording or finalising transaction: $e") + otherSideSession.send(FetchDataFlow.Request.End) // Finish fetching data (overrideAutoAck) + throw UnexpectedFlowEndException("Peer failure upon recording or finalising transaction.", e.cause) + } + catch (uae: TransactionVerificationException.UntrustedAttachmentsException) { + logger.error("Peer failure upon receiving transaction: $uae") + otherSideSession.send(FetchDataFlow.Request.End) // Finish fetching data (overrideAutoAck) + throw uae + } + } + } + + class FinaliseSpeedySpendFlow(val id: SecureHash, private val sigs: List) : FlowLogic() { + + @Suspendable + override fun call(): SignedTransaction { + // Mimic ReceiveFinalityFlow finalisation + val stx = serviceHub.validatedTransactions.getTransaction(id) ?: throw FlowException("Missing transaction: $id") + (serviceHub as ServiceHubCoreInternal).finalizeTransactionWithExtraSignatures(stx + sigs, sigs, StatesToRecord.ONLY_RELEVANT) + logger.info("Peer finalised transaction with notary signature.") + + return stx + sigs + } + } + + @InitiatingFlow + class MimicFinalityFailureFlow(private val stateAndRef: StateAndRef, private val newOwner: Party) : FlowLogic() { + // Mimic FinalityFlow but trigger UnexpectedFlowEndException in ReceiveFinality whilst awaiting receipt of notary signature + @Suspendable + override fun call(): SignedTransaction { + val txBuilder = DummyContract.move(stateAndRef, newOwner) + val stxn = serviceHub.signInitialTransaction(txBuilder, ourIdentity.owningKey) + val sessionWithCounterParty = initiateFlow(newOwner) + subFlow(object : SendTransactionFlow(stxn, setOf(sessionWithCounterParty), emptySet(), StatesToRecord.ONLY_RELEVANT, true) { + override fun isFinality(): Boolean = true + }) + throw UnexpectedFlowEndException("${stxn.id}") + } + } + + @Suppress("unused") + @InitiatedBy(MimicFinalityFailureFlow::class) + class TriggerReceiveFinalityFlow(private val otherSide: FlowSession) : FlowLogic() { + @Suspendable + override fun call() { + subFlow(ReceiveFinalityFlow(otherSide)) + } + } + + class FinalisationFailedException(val notarisedTxn: SignedTransaction) : FlowException("Failed to finalise transaction with notary signature.") + + private fun createBob(cordapps: List = emptyList(), platformVersion: Int = PLATFORM_VERSION): TestStartedNode { + return mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME, additionalCordapps = cordapps, + version = MOCK_VERSION_INFO.copy(platformVersion = platformVersion))) + } + + private fun createNode(legalName: CordaX500Name, cordapps: List = emptyList(), platformVersion: Int = PLATFORM_VERSION): TestStartedNode { + return mockNet.createNode(InternalMockNodeParameters(legalName = legalName, additionalCordapps = cordapps, + version = MOCK_VERSION_INFO.copy(platformVersion = platformVersion))) } private fun TestStartedNode.issuesCashTo(recipient: TestStartedNode): SignedTransaction { diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/TestNoSecurityDataVendingFlow.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/TestNoSecurityDataVendingFlow.kt index c203e64d5b..ee5c04db1f 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/TestNoSecurityDataVendingFlow.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/TestNoSecurityDataVendingFlow.kt @@ -15,7 +15,7 @@ class TestNoSecurityDataVendingFlow(otherSideSession: FlowSession) : DataVending // Hack to not send the first message. otherSideSession.receive() } else { - super.sendPayloadAndReceiveDataRequest(this.otherSideSession, payload) + super.sendPayloadAndReceiveDataRequest(this.otherSessions.first(), payload) } } } \ No newline at end of file diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/WithFinality.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/WithFinality.kt index 9ed9b04679..714e8b85fa 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/WithFinality.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/WithFinality.kt @@ -4,7 +4,13 @@ import co.paralleluniverse.fibers.Suspendable import com.natpryce.hamkrest.MatchResult import com.natpryce.hamkrest.Matcher import com.natpryce.hamkrest.equalTo -import net.corda.core.flows.* +import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.ReceiveFinalityFlow +import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party import net.corda.core.internal.FlowStateMachineHandle import net.corda.core.messaging.CordaRPCOps @@ -58,4 +64,13 @@ interface WithFinality : WithMockNet { subFlow(ReceiveFinalityFlow(otherSide)) } } + + @StartableByRPC + class OldFinalityInvoker(private val transaction: SignedTransaction) : FlowLogic() { + @Suspendable + override fun call(): SignedTransaction { + @Suppress("DEPRECATION") + return subFlow(FinalityFlow(transaction)) + } + } } diff --git a/core-tests/src/test/kotlin/net/corda/coretests/internal/ResolveTransactionsFlowTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/internal/ResolveTransactionsFlowTest.kt index ec7986082c..c6b9858914 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/internal/ResolveTransactionsFlowTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/internal/ResolveTransactionsFlowTest.kt @@ -99,12 +99,17 @@ class ResolveTransactionsFlowTest { // DOCEND 1 @Test(timeout=300_000) - fun `dependency with an error`() { - val stx = makeTransactions(signFirstTX = false).second - val p = TestFlow(setOf(stx.id), megaCorp) - val future = miniCorpNode.startFlow(p) - mockNet.runNetwork() - assertFailsWith(SignedTransaction.SignaturesMissingException::class) { future.getOrThrow() } + fun `dependency with an error fails fast upon prior attempt to record transaction with missing signature`() { + val exception = assertFailsWith(IllegalStateException::class) { + val stx = makeTransactions(signFirstTX = false).second + // fails fast in above operation + // prior to platform version 13, same failure would occur upon transaction resolution + val p = TestFlow(setOf(stx.id), megaCorp) + val future = miniCorpNode.startFlow(p) + mockNet.runNetwork() + future.getOrThrow() + } + assertTrue(exception.cause.toString().contains("SignaturesMissingException")) } @Test(timeout=300_000) diff --git a/core-tests/src/test/kotlin/net/corda/coretests/node/NetworkParametersTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/node/NetworkParametersTest.kt index a9da9eada4..aabda7d94d 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/node/NetworkParametersTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/node/NetworkParametersTest.kt @@ -30,6 +30,8 @@ import java.time.Duration import java.time.Instant import kotlin.test.assertEquals import kotlin.test.assertFails +import kotlin.test.assertNotEquals +import kotlin.test.assertNull class NetworkParametersTest { private val mockNet = InternalMockNetwork( @@ -93,6 +95,32 @@ class NetworkParametersTest { assertEquals(twoDays, nm2.eventHorizon) } + @Test(timeout=300_000) + fun `that transactionRecoveryPeriod and confidentialIdentityPreGenerationPeriod aren't required`() { + // this is defensive tests in response to CORDA-2769 + val aliceNotaryParty = TestIdentity(ALICE_NAME).party + val aliceNotaryInfo = NotaryInfo(aliceNotaryParty, false) + val nm1 = NetworkParameters( + minimumPlatformVersion = 1, + notaries = listOf(aliceNotaryInfo), + maxMessageSize = Int.MAX_VALUE, + maxTransactionSize = Int.MAX_VALUE, + modifiedTime = Instant.now(), + epoch = 1, + whitelistedContractImplementations = mapOf("MyClass" to listOf(AttachmentId.allOnesHash)), + eventHorizon = Duration.ofDays(1) + ) + + assertNull(nm1.recoveryMaximumBackupInterval) + assertNull(nm1.confidentialIdentityMinimumBackupInterval) + + val nm2 = nm1.copy(recoveryMaximumBackupInterval = 10.days, confidentialIdentityMinimumBackupInterval = 10.days) + + assertNotEquals(nm1.recoveryMaximumBackupInterval, nm2.recoveryMaximumBackupInterval) + assertNotEquals(nm1.confidentialIdentityMinimumBackupInterval, nm2.confidentialIdentityMinimumBackupInterval) + + } + // Notaries tests @Test(timeout=300_000) fun `choosing notary not specified in network parameters will fail`() { diff --git a/core-tests/src/test/kotlin/net/corda/coretests/transactions/LedgerTransactionQueryTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/transactions/LedgerTransactionQueryTests.kt index 81cba121e6..f222925b30 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/transactions/LedgerTransactionQueryTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/transactions/LedgerTransactionQueryTests.kt @@ -80,7 +80,7 @@ class LedgerTransactionQueryTests { .addOutputState(dummyState, DummyContract.PROGRAM_ID) .addCommand(dummyCommand()) ) - services.recordTransactions(fakeIssueTx) + services.recordTransactions(fakeIssueTx, disableSignatureVerification = true) val dummyStateRef = StateRef(fakeIssueTx.id, 0) return StateAndRef(TransactionState(dummyState, DummyContract.PROGRAM_ID, DUMMY_NOTARY, constraint = AlwaysAcceptAttachmentConstraint), dummyStateRef) } diff --git a/core/build.gradle b/core/build.gradle index fc022ad5e9..36ca45311b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -9,7 +9,6 @@ apply plugin: 'com.jfrog.artifactory' description 'Corda core' -// required by DJVM and Avian JVM (for running inside the SGX enclave) which only supports Java 8. targetCompatibility = VERSION_1_8 sourceSets { diff --git a/core/src/main/java/net/corda/core/crypto/Base58.java b/core/src/main/java/net/corda/core/crypto/Base58.java index 0e259eb9a8..80f58689c0 100644 --- a/core/src/main/java/net/corda/core/crypto/Base58.java +++ b/core/src/main/java/net/corda/core/crypto/Base58.java @@ -1,7 +1,5 @@ package net.corda.core.crypto; -import net.corda.core.KeepForDJVM; - import java.math.BigInteger; import java.util.Arrays; @@ -29,7 +27,6 @@ import java.util.Arrays; * NB: This class originally comes from the Apache licensed bitcoinj library. The original author of this code is the * same as the original author of the R3 repository. */ -@KeepForDJVM public class Base58 { private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); private static final char ENCODED_ZERO = ALPHABET[0]; diff --git a/core/src/main/java/net/corda/core/flows/IdentifiableException.java b/core/src/main/java/net/corda/core/flows/IdentifiableException.java index 2e87a8957b..93c9549685 100644 --- a/core/src/main/java/net/corda/core/flows/IdentifiableException.java +++ b/core/src/main/java/net/corda/core/flows/IdentifiableException.java @@ -1,14 +1,11 @@ package net.corda.core.flows; -import net.corda.core.KeepForDJVM; - import javax.annotation.Nullable; /** * An exception that may be identified with an ID. If an exception originates in a counter-flow this ID will be * propagated. This allows correlation of error conditions across different flows. */ -@KeepForDJVM public interface IdentifiableException { /** * @return the ID of the error, or null if the error doesn't have it set (yet). diff --git a/core/src/main/kotlin/net/corda/core/ClientRelevantError.kt b/core/src/main/kotlin/net/corda/core/ClientRelevantError.kt index 59c4b38438..a7e4d20ad3 100644 --- a/core/src/main/kotlin/net/corda/core/ClientRelevantError.kt +++ b/core/src/main/kotlin/net/corda/core/ClientRelevantError.kt @@ -6,6 +6,5 @@ import net.corda.core.serialization.CordaSerializable * Allows an implementing [Throwable] to be propagated to clients. */ @CordaSerializable -@KeepForDJVM @Deprecated("This is no longer used as the exception obfuscation feature is no longer available.") interface ClientRelevantError \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/CordaException.kt b/core/src/main/kotlin/net/corda/core/CordaException.kt index 7b5f822452..52bbd82172 100644 --- a/core/src/main/kotlin/net/corda/core/CordaException.kt +++ b/core/src/main/kotlin/net/corda/core/CordaException.kt @@ -4,7 +4,6 @@ import net.corda.core.serialization.CordaSerializable import java.util.* @CordaSerializable -@KeepForDJVM interface CordaThrowable { var originalExceptionClassName: String? val originalMessage: String? @@ -13,7 +12,6 @@ interface CordaThrowable { fun addSuppressed(suppressed: Array) } -@KeepForDJVM open class CordaException internal constructor(override var originalExceptionClassName: String? = null, private var _message: String? = null, private var _cause: Throwable? = null) : Exception(null, null, true, true), CordaThrowable { @@ -61,7 +59,6 @@ open class CordaException internal constructor(override var originalExceptionCla } } -@KeepForDJVM open class CordaRuntimeException(override var originalExceptionClassName: String?, private var _message: String?, private var _cause: Throwable?) : RuntimeException(null, null, true, true), CordaThrowable { diff --git a/core/src/main/kotlin/net/corda/core/CordaOID.kt b/core/src/main/kotlin/net/corda/core/CordaOID.kt index 0a6bf3b658..60a9e09281 100644 --- a/core/src/main/kotlin/net/corda/core/CordaOID.kt +++ b/core/src/main/kotlin/net/corda/core/CordaOID.kt @@ -6,7 +6,6 @@ import net.corda.core.crypto.internal.AliasPrivateKey * OIDs used for the Corda platform. All entries MUST be defined in this file only and they MUST NOT be removed. * If an OID is incorrectly assigned, it should be marked deprecated and NEVER be reused again. */ -@KeepForDJVM object CordaOID { /** Assigned to R3, see http://www.oid-info.com/cgi-bin/display?oid=1.3.6.1.4.1.50530&action=display */ const val R3_ROOT = "1.3.6.1.4.1.50530" diff --git a/core/src/main/kotlin/net/corda/core/DeleteForDJVM.kt b/core/src/main/kotlin/net/corda/core/DeleteForDJVM.kt deleted file mode 100644 index b4b92d1df8..0000000000 --- a/core/src/main/kotlin/net/corda/core/DeleteForDJVM.kt +++ /dev/null @@ -1,24 +0,0 @@ -package net.corda.core - -import kotlin.annotation.AnnotationRetention.BINARY -import kotlin.annotation.AnnotationTarget.* - -/** - * Declare the annotated element to unsuitable for the deterministic version of Corda. - */ -// DOCSTART 01 -@Target( - FILE, - CLASS, - CONSTRUCTOR, - FUNCTION, - PROPERTY_GETTER, - PROPERTY_SETTER, - PROPERTY, - FIELD, - TYPEALIAS -) -@Retention(BINARY) -@CordaInternal -annotation class DeleteForDJVM -// DOCEND 01 diff --git a/core/src/main/kotlin/net/corda/core/KeepForDJVM.kt b/core/src/main/kotlin/net/corda/core/KeepForDJVM.kt deleted file mode 100644 index beaa8281be..0000000000 --- a/core/src/main/kotlin/net/corda/core/KeepForDJVM.kt +++ /dev/null @@ -1,18 +0,0 @@ -package net.corda.core - -import kotlin.annotation.AnnotationRetention.BINARY -import kotlin.annotation.AnnotationTarget.CLASS -import kotlin.annotation.AnnotationTarget.FILE - -/** - * This annotates a class or file that we want to include into the deterministic version of Corda Core. - * We don't expect everything within that class/file to be deterministic; those non-deterministic - * elements need to be annotated with either [DeleteForDJVM] or [StubOutForDJVM] so that they - * can be deleted. - */ -// DOCSTART 01 -@Target(FILE, CLASS) -@Retention(BINARY) -@CordaInternal -annotation class KeepForDJVM -// DOCEND 01 \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/StubOutForDJVM.kt b/core/src/main/kotlin/net/corda/core/StubOutForDJVM.kt deleted file mode 100644 index c38c38569a..0000000000 --- a/core/src/main/kotlin/net/corda/core/StubOutForDJVM.kt +++ /dev/null @@ -1,22 +0,0 @@ -package net.corda.core - -import kotlin.annotation.AnnotationRetention.BINARY -import kotlin.annotation.AnnotationTarget.* - -/** - * We expect that almost every non-deterministic element can have its bytecode - * deleted entirely from the deterministic version of Corda. This annotation is - * for those (hopefully!) few occasions where the non-deterministic function - * cannot be deleted. In these cases, the function will be stubbed out instead. - */ -// DOCSTART 01 -@Target( - CONSTRUCTOR, - FUNCTION, - PROPERTY_GETTER, - PROPERTY_SETTER -) -@Retention(BINARY) -@CordaInternal -annotation class StubOutForDJVM -// DOCEND 01 diff --git a/core/src/main/kotlin/net/corda/core/context/InvocationContext.kt b/core/src/main/kotlin/net/corda/core/context/InvocationContext.kt index dbcfd0c192..7c3afc1c24 100644 --- a/core/src/main/kotlin/net/corda/core/context/InvocationContext.kt +++ b/core/src/main/kotlin/net/corda/core/context/InvocationContext.kt @@ -1,7 +1,5 @@ package net.corda.core.context -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.contracts.ScheduledStateRef import net.corda.core.identity.CordaX500Name import net.corda.core.internal.telemetry.SerializedTelemetry @@ -52,7 +50,6 @@ data class InvocationContext( /** * Creates an [InvocationContext] with a [Trace] that defaults to a [java.util.UUID] as value and [java.time.Instant.now] timestamp. */ - @DeleteForDJVM @JvmStatic @JvmOverloads @Suppress("LongParameterList") @@ -70,7 +67,6 @@ data class InvocationContext( /** * Creates an [InvocationContext] with [InvocationOrigin.RPC] origin. */ - @DeleteForDJVM @JvmStatic @JvmOverloads @Suppress("LongParameterList") @@ -86,28 +82,24 @@ data class InvocationContext( /** * Creates an [InvocationContext] with [InvocationOrigin.Peer] origin. */ - @DeleteForDJVM @JvmStatic fun peer(party: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null, impersonatedActor: Actor? = null): InvocationContext = newInstance(InvocationOrigin.Peer(party), trace, null, externalTrace, impersonatedActor) /** * Creates an [InvocationContext] with [InvocationOrigin.Service] origin. */ - @DeleteForDJVM @JvmStatic fun service(serviceClassName: String, owningLegalIdentity: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(InvocationOrigin.Service(serviceClassName, owningLegalIdentity), trace, null, externalTrace) /** * Creates an [InvocationContext] with [InvocationOrigin.Scheduled] origin. */ - @DeleteForDJVM @JvmStatic fun scheduled(scheduledState: ScheduledStateRef, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(InvocationOrigin.Scheduled(scheduledState), trace, null, externalTrace) /** * Creates an [InvocationContext] with [InvocationOrigin.Shell] origin. */ - @DeleteForDJVM @JvmStatic fun shell(trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = InvocationContext(InvocationOrigin.Shell, trace, null, externalTrace) } @@ -161,7 +153,6 @@ data class InvocationContext( /** * Models an initiator in Corda, can be a user, a service, etc. */ -@KeepForDJVM @CordaSerializable data class Actor(val id: Id, val serviceId: AuthServiceId, val owningLegalIdentity: CordaX500Name) { @@ -173,7 +164,6 @@ data class Actor(val id: Id, val serviceId: AuthServiceId, val owningLegalIdenti /** * Actor id. */ - @KeepForDJVM @CordaSerializable data class Id(val value: String) } @@ -181,7 +171,6 @@ data class Actor(val id: Id, val serviceId: AuthServiceId, val owningLegalIdenti /** * Represents the source of an action such as a flow start, an RPC, a shell command etc. */ -@DeleteForDJVM @CordaSerializable sealed class InvocationOrigin { /** @@ -230,6 +219,5 @@ sealed class InvocationOrigin { /** * Authentication / Authorisation Service ID. */ -@KeepForDJVM @CordaSerializable -data class AuthServiceId(val value: String) \ No newline at end of file +data class AuthServiceId(val value: String) diff --git a/core/src/main/kotlin/net/corda/core/context/Trace.kt b/core/src/main/kotlin/net/corda/core/context/Trace.kt index 281d7fae28..6774460570 100644 --- a/core/src/main/kotlin/net/corda/core/context/Trace.kt +++ b/core/src/main/kotlin/net/corda/core/context/Trace.kt @@ -1,6 +1,5 @@ package net.corda.core.context -import net.corda.core.DeleteForDJVM import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.Id import net.corda.core.utilities.UuidGenerator @@ -17,7 +16,6 @@ data class Trace(val invocationId: InvocationId, val sessionId: SessionId) { /** * Creates a trace using a [InvocationId.newInstance] with default arguments and a [SessionId] matching the value and timestamp from the invocation id.. */ - @DeleteForDJVM @JvmStatic fun newInstance(invocationId: InvocationId = InvocationId.newInstance(), sessionId: SessionId = SessionId(invocationId.value, invocationId.timestamp)) = Trace(invocationId, sessionId) } @@ -34,7 +32,6 @@ data class Trace(val invocationId: InvocationId, val sessionId: SessionId) { /** * Creates an invocation id using a [java.util.UUID] as value and [Instant.now] as timestamp. */ - @DeleteForDJVM @JvmStatic fun newInstance(value: String = UuidGenerator.next().toString(), timestamp: Instant = Instant.now()) = InvocationId(value, timestamp) } @@ -52,9 +49,8 @@ data class Trace(val invocationId: InvocationId, val sessionId: SessionId) { /** * Creates a session id using a [java.util.UUID] as value and [Instant.now] as timestamp. */ - @DeleteForDJVM @JvmStatic fun newInstance(value: String = UuidGenerator.next().toString(), timestamp: Instant = Instant.now()) = SessionId(value, timestamp) } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/net/corda/core/contracts/Amount.kt b/core/src/main/kotlin/net/corda/core/contracts/Amount.kt index d010ab5aa5..3355954ca8 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Amount.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Amount.kt @@ -1,6 +1,5 @@ package net.corda.core.contracts -import net.corda.core.KeepForDJVM import net.corda.core.crypto.CompositeKey import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable @@ -37,7 +36,6 @@ interface TokenizableAssetInfo { * @property token the type of token this is an amount of. This is usually a singleton. * @param T the type of the token, for example [Currency]. T should implement [TokenizableAssetInfo] if automatic conversion to/from a display format is required. */ -@KeepForDJVM @CordaSerializable data class Amount(val quantity: Long, val displayTokenSize: BigDecimal, val token: T) : Comparable> { // TODO Proper lookup of currencies in a locale and context sensitive fashion is not supported and is left to the application. diff --git a/core/src/main/kotlin/net/corda/core/contracts/Attachment.kt b/core/src/main/kotlin/net/corda/core/contracts/Attachment.kt index 9d95250b56..4cb6c42ba6 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Attachment.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Attachment.kt @@ -1,7 +1,6 @@ package net.corda.core.contracts import net.corda.core.DoNotImplement -import net.corda.core.KeepForDJVM import net.corda.core.identity.Party import net.corda.core.internal.extractFile import net.corda.core.serialization.CordaSerializable @@ -31,7 +30,6 @@ import java.util.jar.JarInputStream * Finally, using ZIPs ensures files have a timestamp associated with them, and enables informational attachments * to be password protected (although in current releases password protected ZIPs are likely to fail to work). */ -@KeepForDJVM @CordaSerializable @DoNotImplement interface Attachment : NamedByHash { diff --git a/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt b/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt index 0540933683..e7efccdde9 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt @@ -2,7 +2,6 @@ package net.corda.core.contracts import net.corda.core.CordaInternal import net.corda.core.DoNotImplement -import net.corda.core.KeepForDJVM import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint.isSatisfiedBy import net.corda.core.crypto.SecureHash import net.corda.core.crypto.isFulfilledBy @@ -38,7 +37,6 @@ interface AttachmentConstraint { } /** An [AttachmentConstraint] where [isSatisfiedBy] always returns true. */ -@KeepForDJVM object AlwaysAcceptAttachmentConstraint : AttachmentConstraint { override fun isSatisfiedBy(attachment: Attachment) = true } @@ -48,7 +46,6 @@ object AlwaysAcceptAttachmentConstraint : AttachmentConstraint { * The state protected by this constraint can only be used in a transaction created with that version of the jar. * And a receiving node will only accept it if a cordapp with that hash has (is) been deployed on the node. */ -@KeepForDJVM data class HashAttachmentConstraint(val attachmentId: SecureHash) : AttachmentConstraint { companion object { val disableHashConstraints = System.getProperty("net.corda.node.disableHashConstraints")?.toBoolean() ?: false @@ -69,7 +66,6 @@ data class HashAttachmentConstraint(val attachmentId: SecureHash) : AttachmentCo * See: [net.corda.core.node.NetworkParameters.whitelistedContractImplementations] * It allows for centralized control over the cordapps that can be used. */ -@KeepForDJVM object WhitelistedByZoneAttachmentConstraint : AttachmentConstraint { override fun isSatisfiedBy(attachment: Attachment): Boolean { return if (attachment is AttachmentWithContext) { @@ -83,7 +79,6 @@ object WhitelistedByZoneAttachmentConstraint : AttachmentConstraint { } } -@KeepForDJVM @Deprecated( "The name is no longer valid as multiple constraints were added.", replaceWith = ReplaceWith("AutomaticPlaceholderConstraint"), @@ -102,7 +97,6 @@ object AutomaticHashConstraint : AttachmentConstraint { * The resolution occurs in [TransactionBuilder.toWireTransaction] and is based on the input states and the attachments. * If the [Contract] was not annotated with [NoConstraintPropagation], then the platform will ensure the correct constraint propagation. */ -@KeepForDJVM object AutomaticPlaceholderConstraint : AttachmentConstraint { override fun isSatisfiedBy(attachment: Attachment): Boolean { throw UnsupportedOperationException("Contracts cannot be satisfied by an AutomaticPlaceholderConstraint placeholder.") @@ -115,7 +109,6 @@ object AutomaticPlaceholderConstraint : AttachmentConstraint { * * @property key A [PublicKey] that must be fulfilled by the owning keys of the attachment's signing parties. */ -@KeepForDJVM data class SignatureAttachmentConstraint(val key: PublicKey) : AttachmentConstraint { override fun isSatisfiedBy(attachment: Attachment): Boolean { log.debug("Checking signature constraints: verifying $key in contract attachment signer keys: ${attachment.signerKeys}") diff --git a/core/src/main/kotlin/net/corda/core/contracts/ContractAttachment.kt b/core/src/main/kotlin/net/corda/core/contracts/ContractAttachment.kt index 1b59a8c472..0b56707ee1 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/ContractAttachment.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/ContractAttachment.kt @@ -1,7 +1,6 @@ package net.corda.core.contracts import net.corda.core.CordaInternal -import net.corda.core.KeepForDJVM import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION import java.security.PublicKey @@ -12,7 +11,6 @@ import java.security.PublicKey * @property contract The contract name contained within the JAR. A Contract attachment has to contain at least 1 contract. * @property additionalContracts Additional contract names contained within the JAR. */ -@KeepForDJVM class ContractAttachment private constructor( val attachment: Attachment, val contract: ContractClassName, diff --git a/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt b/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt index 3b89363658..7cef40b7f6 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt @@ -1,6 +1,5 @@ package net.corda.core.contracts -import net.corda.core.KeepForDJVM import net.corda.core.identity.AbstractParty import net.corda.core.serialization.CordaSerializable @@ -12,7 +11,6 @@ import net.corda.core.serialization.CordaSerializable * notary is responsible for ensuring there is no "double spending" by only signing a transaction if the input states * are all free. */ -@KeepForDJVM @CordaSerializable interface ContractState { /** diff --git a/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt b/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt index ac0052c442..763af0163a 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt @@ -1,8 +1,6 @@ @file:JvmName("ContractsDSL") -@file:KeepForDJVM package net.corda.core.contracts -import net.corda.core.KeepForDJVM import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.internal.uncheckedCast @@ -18,7 +16,6 @@ import java.util.* //// Requirements ///////////////////////////////////////////////////////////////////////////////////////////////////// -@KeepForDJVM object Requirements { /** Throws [IllegalArgumentException] if the given expression evaluates to false. */ @Suppress("NOTHING_TO_INLINE") // Inlining this takes it out of our committed ABI. diff --git a/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt b/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt index 78878461f4..a787db3ed7 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt @@ -1,6 +1,5 @@ package net.corda.core.contracts -import net.corda.core.KeepForDJVM import net.corda.core.flows.FlowException import net.corda.core.identity.AbstractParty import net.corda.core.serialization.SerializableCalculatedProperty @@ -27,7 +26,6 @@ class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException * @param T a type that represents the asset in question. This should describe the basic type of the asset * (GBP, USD, oil, shares in company , etc.) and any additional metadata (issuer, grade, class, etc.). */ -@KeepForDJVM interface FungibleAsset : FungibleState>, OwnableState { /** * Amount represents a positive quantity of some issued product which can be cash, tokens, assets, or generally diff --git a/core/src/main/kotlin/net/corda/core/contracts/FungibleState.kt b/core/src/main/kotlin/net/corda/core/contracts/FungibleState.kt index 0e4a241ccd..38ea0656d6 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/FungibleState.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/FungibleState.kt @@ -1,7 +1,5 @@ package net.corda.core.contracts -import net.corda.core.KeepForDJVM - /** * Interface to represent things which are fungible, this means that there is an expectation that these things can * be split and merged. That's the only assumption made by this interface. @@ -25,7 +23,6 @@ import net.corda.core.KeepForDJVM * [TokenizableAssetInfo]. */ // DOCSTART 1 -@KeepForDJVM interface FungibleState : ContractState { /** * Amount represents a positive quantity of some token which can be cash, tokens, stock, agreements, or generally diff --git a/core/src/main/kotlin/net/corda/core/contracts/StatePointer.kt b/core/src/main/kotlin/net/corda/core/contracts/StatePointer.kt index 547eaab33d..b133ef165c 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/StatePointer.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/StatePointer.kt @@ -1,8 +1,6 @@ package net.corda.core.contracts -import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement -import net.corda.core.KeepForDJVM import net.corda.core.node.ServiceHub import net.corda.core.node.services.Vault import net.corda.core.node.services.queryBy @@ -18,7 +16,6 @@ import net.corda.core.transactions.LedgerTransaction * [StaticPointer]s are for use with any type of [ContractState]. */ @CordaSerializable -@KeepForDJVM @DoNotImplement sealed class StatePointer { @@ -70,7 +67,6 @@ sealed class StatePointer { * * @param services a [ServiceHub] implementation is required to resolve the pointer. */ - @DeleteForDJVM abstract fun resolve(services: ServiceHub): StateAndRef /** @@ -89,7 +85,6 @@ sealed class StatePointer { * - The [ContractState] may not be known by the node performing the look-up in which case the [resolve] method will * throw a [TransactionResolutionException] */ -@KeepForDJVM class StaticPointer( override val pointer: StateRef, override val type: Class, @@ -110,7 +105,6 @@ class StaticPointer( */ @Throws(TransactionResolutionException::class) @Suppress("UNCHECKED_CAST") - @DeleteForDJVM override fun resolve(services: ServiceHub): StateAndRef { val transactionState = services.loadState(pointer) as TransactionState val castState: T = type.cast(transactionState.data) @@ -148,7 +142,6 @@ class StaticPointer( * then the transaction with such a reference state cannot be committed to the ledger until the most up-to-date version * of the [LinearState] is available. See reference states documentation on docs.corda.net for more info. */ -@KeepForDJVM class LinearPointer( override val pointer: UniqueIdentifier, override val type: Class, @@ -171,7 +164,6 @@ class LinearPointer( * @param services a [ServiceHub] implementation is required to perform a vault query. */ @Suppress("UNCHECKED_CAST") - @DeleteForDJVM override fun resolve(services: ServiceHub): StateAndRef { // Return the latest version of the linear state. // This query will only ever return one or zero states. diff --git a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt index 9172236207..a678b28684 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt @@ -1,9 +1,6 @@ @file:JvmName("Structures") -@file:KeepForDJVM package net.corda.core.contracts -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.crypto.SecureHash import net.corda.core.crypto.secureRandomBytes import net.corda.core.crypto.toStringShort @@ -45,7 +42,6 @@ interface NamedByHash { * of product may differentiate different kinds of asset within the same logical class e.g the currency, or * it may just be a type marker for a single custom asset. */ -@KeepForDJVM @CordaSerializable data class Issued(val issuer: PartyAndReference, val product: P) { init { @@ -72,13 +68,11 @@ fun Amount>.withoutIssuer(): Amount = Amount(quantity, di /** * Return structure for [OwnableState.withNewOwner] */ -@KeepForDJVM data class CommandAndState(val command: CommandData, val ownableState: OwnableState) /** * A contract state that can have a single owner. */ -@KeepForDJVM interface OwnableState : ContractState { /** There must be a MoveCommand signed by this key to claim the amount. */ val owner: AbstractParty @@ -89,7 +83,6 @@ interface OwnableState : ContractState { // DOCEND 3 /** Something which is scheduled to happen at a point in time. */ -@KeepForDJVM interface Scheduled { val scheduledAt: Instant } @@ -102,7 +95,6 @@ interface Scheduled { * lifecycle processing needs to take place. e.g. a fixing or a late payment etc. */ @CordaSerializable -@KeepForDJVM data class ScheduledStateRef(val ref: StateRef, override val scheduledAt: Instant) : Scheduled /** @@ -117,7 +109,6 @@ data class ScheduledStateRef(val ref: StateRef, override val scheduledAt: Instan * for a particular [ContractState] have been processed/fired etc. If the activity is not "on ledger" then the * scheduled activity shouldn't be either. */ -@KeepForDJVM data class ScheduledActivity(val logicRef: FlowLogicRef, override val scheduledAt: Instant) : Scheduled // DOCSTART 2 @@ -126,7 +117,6 @@ data class ScheduledActivity(val logicRef: FlowLogicRef, override val scheduledA * * This simplifies the job of tracking the current version of certain types of state in e.g. a vault. */ -@KeepForDJVM interface LinearState : ContractState { /** * Unique id shared by all LinearState states throughout history within the vaults of all parties. @@ -136,7 +126,6 @@ interface LinearState : ContractState { val linearId: UniqueIdentifier } // DOCEND 2 -@KeepForDJVM interface SchedulableState : ContractState { /** * Indicate whether there is some activity to be performed at some future point in time with respect to this @@ -160,7 +149,6 @@ fun ContractState.hash(algorithm: String): SecureHash = SecureHash.hashAs(algori * A stateref is a pointer (reference) to a state, this is an equivalent of an "outpoint" in Bitcoin. It records which * transaction defined the state and where in that transaction it was. */ -@KeepForDJVM @CordaSerializable // DOCSTART 8 data class StateRef(val txhash: SecureHash, val index: Int) { @@ -169,7 +157,6 @@ data class StateRef(val txhash: SecureHash, val index: Int) { // DOCEND 8 /** A StateAndRef is simply a (state, ref) pair. For instance, a vault (which holds available assets) contains these. */ -@KeepForDJVM @CordaSerializable // DOCSTART 7 data class StateAndRef(val state: TransactionState, val ref: StateRef) { @@ -179,7 +166,6 @@ data class StateAndRef(val state: TransactionState, va // DOCEND 7 /** A wrapper for a [StateAndRef] indicating that it should be added to a transaction as a reference input state. */ -@KeepForDJVM data class ReferencedStateAndRef(val stateAndRef: StateAndRef) /** Filters a list of [StateAndRef] objects according to the type of the states */ @@ -191,7 +177,6 @@ inline fun Iterable>.filt * Reference to something being stored or issued by a party e.g. in a vault or (more likely) on their normal * ledger. The reference is intended to be encrypted so it's meaningless to anyone other than the party. */ -@KeepForDJVM @CordaSerializable data class PartyAndReference(val party: AbstractParty, val reference: OpaqueBytes) { override fun toString() = "$party$reference" @@ -202,14 +187,12 @@ data class PartyAndReference(val party: AbstractParty, val reference: OpaqueByte interface CommandData /** Commands that inherit from this are intended to have no data items: it's only their presence that matters. */ -@KeepForDJVM abstract class TypeOnlyCommandData : CommandData { override fun equals(other: Any?) = other?.javaClass == javaClass override fun hashCode() = javaClass.name.hashCode() } /** Command data/content plus pubkey pair: the signature is stored at the end of the serialized bytes */ -@KeepForDJVM @CordaSerializable data class Command(val value: T, val signers: List) { // TODO Introduce NonEmptyList? @@ -224,7 +207,6 @@ data class Command(val value: T, val signers: List) } /** A common move command for contract states which can change owner. */ -@KeepForDJVM interface MoveCommand : CommandData { /** * Contract code the moved state(s) are for the attention of, for example to indicate that the states are moved in @@ -236,7 +218,6 @@ interface MoveCommand : CommandData { // DOCSTART 6 /** A [Command] where the signing parties have been looked up if they have a well known/recognised institutional key. */ -@KeepForDJVM @CordaSerializable data class CommandWithParties( val signers: List, @@ -256,7 +237,6 @@ data class CommandWithParties( * * TODO: Contract serialization is likely to change, so the annotation is likely temporary. */ -@KeepForDJVM @CordaSerializable interface Contract { /** @@ -288,7 +268,6 @@ annotation class LegalProseReference(val uri: String) * more than one state). * @param NewState the upgraded contract state. */ -@KeepForDJVM interface UpgradedContract : Contract { /** * Name of the contract this is an upgraded version of, used as part of verification of upgrade transactions. @@ -307,7 +286,6 @@ interface UpgradedContract : UpgradedContract { /** * A validator for the legacy (pre-upgrade) contract attachments on the transaction. @@ -325,14 +303,11 @@ interface UpgradedContractWithLegacyConstraint) : this(txId, @@ -205,7 +188,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S * transactions are not supported and thus two encumbered states with different notaries cannot be consumed * in the same transaction. */ - @KeepForDJVM class TransactionNotaryMismatchEncumbranceException(txId: SecureHash, message: String) : TransactionVerificationException(txId, message, null) { constructor(txId: SecureHash, encumberedIndex: Int, encumbranceIndex: Int, encumberedNotary: Party, encumbranceNotary: Party) : @@ -222,7 +204,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S * @param state The [TransactionState] whose bundled state and contract are in conflict. * @param requiredContractClassName The class name of the contract to which the state belongs. */ - @KeepForDJVM class TransactionContractConflictException(txId: SecureHash, message: String) : TransactionVerificationException(txId, message, null) { constructor(txId: SecureHash, state: TransactionState, requiredContractClassName: String): this(txId, @@ -233,7 +214,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S } // TODO: add reference to documentation - @KeepForDJVM class TransactionRequiredContractUnspecifiedException(txId: SecureHash, message: String) : TransactionVerificationException(txId, message, null) { constructor(txId: SecureHash, state: TransactionState) : this(txId, @@ -247,7 +227,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S /** * If the network parameters associated with an input or reference state in a transaction are more recent than the network parameters of the new transaction itself. */ - @KeepForDJVM class TransactionNetworkParameterOrderingException(txId: SecureHash, message: String) : TransactionVerificationException(txId, message, null) { constructor(txId: SecureHash, @@ -265,7 +244,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S * @param txId Id of the transaction that has missing parameters hash in the resolution chain * @param missingNetworkParametersHash Missing hash of the network parameters associated to this transaction */ - @KeepForDJVM class MissingNetworkParametersException(txId: SecureHash, message: String) : TransactionVerificationException(txId, message, null) { constructor(txId: SecureHash, missingNetworkParametersHash: SecureHash) : @@ -275,13 +253,11 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S /** * @param txId Id of the transaction that Corda is no longer able to verify. */ - @KeepForDJVM class BrokenTransactionException(txId: SecureHash, message: String) : TransactionVerificationException(txId, message, null) /** Whether the inputs or outputs list contains an encumbrance issue, see [TransactionMissingEncumbranceException]. */ @CordaSerializable - @KeepForDJVM enum class Direction { /** Issue in the inputs list. */ INPUT, @@ -294,30 +270,25 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S // as a cause. /** @suppress This class is not used: duplicate inputs throw a [IllegalStateException] instead. */ @Deprecated("This class is not used: duplicate inputs throw a [IllegalStateException] instead.") - @DeleteForDJVM class DuplicateInputStates(txId: SecureHash, val duplicates: NonEmptySet) : TransactionVerificationException(txId, "Duplicate inputs: ${duplicates.joinToString()}", null) /** @suppress This class is obsolete and nothing has ever used it. */ @Deprecated("This class is obsolete and nothing has ever used it.") - @DeleteForDJVM class MoreThanOneNotary(txId: SecureHash) : TransactionVerificationException(txId, "More than one notary", null) /** @suppress This class is obsolete and nothing has ever used it. */ @Deprecated("This class is obsolete and nothing has ever used it.") - @DeleteForDJVM class SignersMissing(txId: SecureHash, val missing: List) : TransactionVerificationException(txId, "Signers missing: ${missing.joinToString()}", null) /** @suppress This class is obsolete and nothing has ever used it. */ @Deprecated("This class is obsolete and nothing has ever used it.") - @DeleteForDJVM class InvalidNotaryChange(txId: SecureHash) : TransactionVerificationException(txId, "Detected a notary change. Outputs must use the same notary as inputs", null) /** * Thrown when multiple attachments provide the same file when building the AttachmentsClassloader for a transaction. */ - @KeepForDJVM class OverlappingAttachmentsException(txId: SecureHash, val path: String) : TransactionVerificationException(txId, "Multiple attachments define a file at $path.", null) /** @@ -336,16 +307,12 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S // TODO: Make this descend from TransactionVerificationException so that untrusted attachments cause flows to be hospitalized. /** Thrown during classloading upon encountering an untrusted attachment (eg. not in the [TRUSTED_UPLOADERS] list) */ - @KeepForDJVM class UntrustedAttachmentsException(val txId: SecureHash, val ids: List) : CordaException("Attempting to load untrusted transaction attachments: $ids. " + - "At this time these are not loadable because the DJVM sandbox has not yet been integrated. " + "You will need to manually install the CorDapp to whitelist it for use.") - @KeepForDJVM class UnsupportedHashTypeException(txId: SecureHash) : TransactionVerificationException(txId, "The transaction Id is defined by an unsupported hash type", null) - @KeepForDJVM class AttachmentTooBigException(txId: SecureHash) : TransactionVerificationException( txId, "The transaction attachments are too large and exceed both max transaction size and the maximum allowed compression ratio", null) diff --git a/core/src/main/kotlin/net/corda/core/contracts/UniqueIdentifier.kt b/core/src/main/kotlin/net/corda/core/contracts/UniqueIdentifier.kt index ac4a390c34..20ef1ca7b9 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/UniqueIdentifier.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/UniqueIdentifier.kt @@ -1,7 +1,5 @@ package net.corda.core.contracts -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.internal.VisibleForTesting import net.corda.core.serialization.CordaSerializable import java.util.* @@ -19,8 +17,7 @@ import java.util.* * Subsequent copies and evolutions of a state should just copy the [externalId] and [id] fields unmodified. */ @CordaSerializable -@KeepForDJVM -data class UniqueIdentifier @JvmOverloads @DeleteForDJVM constructor(val externalId: String? = null, val id: UUID = UUID.randomUUID()) : Comparable { +data class UniqueIdentifier @JvmOverloads constructor(val externalId: String? = null, val id: UUID = UUID.randomUUID()) : Comparable { override fun toString(): String = if (externalId != null) "${externalId}_$id" else id.toString() companion object { diff --git a/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt b/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt index f10a17ef86..040a5455a8 100644 --- a/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt +++ b/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt @@ -1,6 +1,5 @@ package net.corda.core.cordapp -import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement import net.corda.core.cordapp.Cordapp.Info.* import net.corda.core.crypto.SecureHash @@ -41,7 +40,6 @@ import java.net.URL * @property targetPlatformVersion The target platform version this CorDapp was designed and tested on. */ @DoNotImplement -@DeleteForDJVM interface Cordapp { val name: String val contractClassNames: List diff --git a/core/src/main/kotlin/net/corda/core/cordapp/CordappContext.kt b/core/src/main/kotlin/net/corda/core/cordapp/CordappContext.kt index da8790a66f..2441f04e3b 100644 --- a/core/src/main/kotlin/net/corda/core/cordapp/CordappContext.kt +++ b/core/src/main/kotlin/net/corda/core/cordapp/CordappContext.kt @@ -1,7 +1,6 @@ package net.corda.core.cordapp import net.corda.core.CordaInternal -import net.corda.core.DeleteForDJVM import net.corda.core.crypto.SecureHash import java.lang.UnsupportedOperationException @@ -18,7 +17,6 @@ import java.lang.UnsupportedOperationException * @property classLoader the classloader used to load this cordapp's classes * @property config Configuration for this CorDapp */ -@DeleteForDJVM class CordappContext private constructor( val cordapp: Cordapp, val attachmentId: SecureHash?, diff --git a/core/src/main/kotlin/net/corda/core/cordapp/CordappProvider.kt b/core/src/main/kotlin/net/corda/core/cordapp/CordappProvider.kt index bec2d6ab59..bf7864ee95 100644 --- a/core/src/main/kotlin/net/corda/core/cordapp/CordappProvider.kt +++ b/core/src/main/kotlin/net/corda/core/cordapp/CordappProvider.kt @@ -1,6 +1,5 @@ package net.corda.core.cordapp -import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement import net.corda.core.contracts.ContractClassName import net.corda.core.node.services.AttachmentId @@ -9,7 +8,6 @@ import net.corda.core.node.services.AttachmentId * Provides access to what the node knows about loaded applications. */ @DoNotImplement -@DeleteForDJVM interface CordappProvider { /** * Exposes the current CorDapp context which will contain information and configuration of the CorDapp that diff --git a/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt b/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt index be744a6b80..e968257931 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt @@ -1,10 +1,16 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.exactAdd import net.corda.core.utilities.sequence -import org.bouncycastle.asn1.* +import org.bouncycastle.asn1.ASN1EncodableVector +import org.bouncycastle.asn1.ASN1Encoding +import org.bouncycastle.asn1.ASN1Integer +import org.bouncycastle.asn1.ASN1Object +import org.bouncycastle.asn1.ASN1Primitive +import org.bouncycastle.asn1.ASN1Sequence +import org.bouncycastle.asn1.DERBitString +import org.bouncycastle.asn1.DERSequence import org.bouncycastle.asn1.x509.AlgorithmIdentifier import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import java.security.PublicKey @@ -27,7 +33,6 @@ import java.util.* * @property threshold specifies the minimum total weight required (in the simple case – the minimum number of child * signatures required) to satisfy the sub-tree rooted at this node. */ -@KeepForDJVM class CompositeKey private constructor(val threshold: Int, children: List) : PublicKey { companion object { const val KEY_ALGORITHM = "COMPOSITE" @@ -144,7 +149,6 @@ class CompositeKey private constructor(val threshold: Int, children: List, ASN1Object() { init { @@ -162,7 +166,7 @@ class CompositeKey private constructor(val threshold: Int, children: List = mutableListOf() diff --git a/core/src/main/kotlin/net/corda/core/crypto/CompositeKeyFactory.kt b/core/src/main/kotlin/net/corda/core/crypto/CompositeKeyFactory.kt index 7340e6e96b..df7fc7c433 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CompositeKeyFactory.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CompositeKeyFactory.kt @@ -1,6 +1,5 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import java.security.* import java.security.spec.InvalidKeySpecException import java.security.spec.KeySpec @@ -9,7 +8,6 @@ import java.security.spec.X509EncodedKeySpec /** * Factory for generating composite keys from ASN.1 format key specifications. This is used by [CordaSecurityProvider]. */ -@KeepForDJVM class CompositeKeyFactory : KeyFactorySpi() { @Throws(InvalidKeySpecException::class) diff --git a/core/src/main/kotlin/net/corda/core/crypto/CompositeSignature.kt b/core/src/main/kotlin/net/corda/core/crypto/CompositeSignature.kt index e960590c1d..8cac221158 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CompositeSignature.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CompositeSignature.kt @@ -1,6 +1,5 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import net.corda.core.serialization.deserialize import java.io.ByteArrayOutputStream import java.security.InvalidAlgorithmParameterException @@ -15,7 +14,6 @@ import java.security.spec.AlgorithmParameterSpec /** * Dedicated class for storing a set of signatures that comprise [CompositeKey]. */ -@KeepForDJVM class CompositeSignature : Signature(SIGNATURE_ALGORITHM) { companion object { const val SIGNATURE_ALGORITHM = "COMPOSITESIG" diff --git a/core/src/main/kotlin/net/corda/core/crypto/CompositeSignaturesWithKeys.kt b/core/src/main/kotlin/net/corda/core/crypto/CompositeSignaturesWithKeys.kt index 1175f80837..0ea62b1f0e 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CompositeSignaturesWithKeys.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CompositeSignaturesWithKeys.kt @@ -1,6 +1,5 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializable /** @@ -8,7 +7,6 @@ import net.corda.core.serialization.CordaSerializable * serialization format. */ @CordaSerializable -@KeepForDJVM data class CompositeSignaturesWithKeys(val sigs: List) { companion object { val EMPTY = CompositeSignaturesWithKeys(emptyList()) diff --git a/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt b/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt index 91a6611e97..4530312eec 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt @@ -1,7 +1,5 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM -import net.corda.core.StubOutForDJVM import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_KEY import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_SIGNATURE import net.corda.core.crypto.internal.PlatformSecureRandomService @@ -10,13 +8,14 @@ import java.security.Provider import java.util.* import java.util.concurrent.ConcurrentHashMap -@KeepForDJVM @Suppress("DEPRECATION") // JDK11: should replace with Provider(String name, double version, String info) (since 9) class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") { companion object { const val PROVIDER_NAME = "Corda" } + private val services = ConcurrentHashMap, Optional>() + init { put("KeyFactory.${CompositeKey.KEY_ALGORITHM}", CompositeKeyFactory::class.java.name) put("Alg.Alias.KeyFactory.$COMPOSITE_KEY", CompositeKey.KEY_ALGORITHM) @@ -27,47 +26,19 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur putPlatformSecureRandomService() } - @StubOutForDJVM private fun putPlatformSecureRandomService() { putService(PlatformSecureRandomService(this)) } - override fun getService(type: String, algorithm: String): Service? = serviceFactory(type, algorithm) - - // Used to work around banning of ConcurrentHashMap in DJVM - @Suppress("TooGenericExceptionCaught") - private val serviceFactory: (String, String) -> Service? = try { - // Will throw UnsupportedOperationException in DJVM - makeCachingFactory() - } catch (e: Exception) { - makeFactory() + override fun getService(type: String, algorithm: String): Service? { + return services.getOrPut(Pair(type, algorithm)) { + Optional.ofNullable(superGetService(type, algorithm)) + }.orElse(null) } private fun superGetService(type: String, algorithm: String): Service? = super.getService(type, algorithm) - - @StubOutForDJVM - private fun makeCachingFactory(): Function2 { - return object : Function2 { - private val services = ConcurrentHashMap, Optional>() - - override fun invoke(type: String, algorithm: String): Service? { - return services.getOrPut(Pair(type, algorithm)) { - Optional.ofNullable(superGetService(type, algorithm)) - }.orElse(null) - } - } - } - - private fun makeFactory(): Function2 { - return object : Function2 { - override fun invoke(type: String, algorithm: String): Service? { - return superGetService(type, algorithm) - } - } - } } -@KeepForDJVM object CordaObjectIdentifier { // UUID-based OID // TODO define and use an official Corda OID in [CordaOID]. We didn't do yet for backwards compatibility purposes, diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt index 612052740c..8018c68892 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt @@ -1,10 +1,9 @@ package net.corda.core.crypto import net.corda.core.CordaOID -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.crypto.internal.AliasPrivateKey import net.corda.core.crypto.internal.Instances.withSignature +import net.corda.core.crypto.internal.PublicKeyCache import net.corda.core.crypto.internal.bouncyCastlePQCProvider import net.corda.core.crypto.internal.cordaBouncyCastleProvider import net.corda.core.crypto.internal.cordaSecurityProvider @@ -12,6 +11,7 @@ import net.corda.core.crypto.internal.`id-Curve25519ph` import net.corda.core.crypto.internal.providerMap import net.corda.core.internal.utilities.PrivateInterner import net.corda.core.serialization.serialize +import net.corda.core.utilities.ByteSequence import net.i2p.crypto.eddsa.EdDSAEngine import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPublicKey @@ -81,7 +81,6 @@ import javax.crypto.spec.SecretKeySpec *
  • SPHINCS256_SHA512 (SPHINCS-256 hash-based signature scheme using SHA512 as hash algorithm). * */ -@KeepForDJVM object Crypto { /** * RSA PKCS#1 signature scheme using SHA256 for message hashing. @@ -227,7 +226,6 @@ object Crypto { @JvmStatic fun supportedSignatureSchemes(): List = ArrayList(signatureSchemeMap.values) - @DeleteForDJVM @JvmStatic fun findProvider(name: String): Provider { return providerMap[name] ?: throw IllegalArgumentException("Unrecognised provider: $name") @@ -281,7 +279,7 @@ object Crypto { */ @JvmStatic fun findSignatureScheme(key: PublicKey): SignatureScheme { - val keyInfo = SubjectPublicKeyInfo.getInstance(key.encoded) + val keyInfo = SubjectPublicKeyInfo.getInstance(encodePublicKey(key)) return findSignatureScheme(keyInfo.algorithm) } @@ -306,7 +304,6 @@ object Crypto { * @throws IllegalArgumentException on not supported scheme or if the given key specification * is inappropriate for this key factory to produce a private key. */ - @DeleteForDJVM @JvmStatic fun decodePrivateKey(encodedKey: ByteArray): PrivateKey { val keyInfo = PrivateKeyInfo.getInstance(encodedKey) @@ -318,7 +315,6 @@ object Crypto { return convertIfBCEdDSAPrivateKey(keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))) } - @DeleteForDJVM private fun decodeAliasPrivateKey(keyInfo: PrivateKeyInfo): PrivateKey { val encodable = keyInfo.parsePrivateKey() as DLSequence val derutF8String = encodable.getObjectAt(0) @@ -334,7 +330,6 @@ object Crypto { * @throws IllegalArgumentException on not supported scheme or if the given key specification * is inappropriate for this key factory to produce a private key. */ - @DeleteForDJVM @JvmStatic @Throws(InvalidKeySpecException::class) fun decodePrivateKey(schemeCodeName: String, encodedKey: ByteArray): PrivateKey { @@ -373,10 +368,17 @@ object Crypto { */ @JvmStatic fun decodePublicKey(encodedKey: ByteArray): PublicKey { - val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey) - val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm) - val keyFactory = keyFactory(signatureScheme) - return convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))) + return PublicKeyCache.publicKeyForCachedBytes(ByteSequence.of(encodedKey)) ?: { + val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey) + val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm) + val keyFactory = keyFactory(signatureScheme) + convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))) + }() + } + + @JvmStatic + fun encodePublicKey(key: PublicKey): ByteArray { + return PublicKeyCache.bytesForCachedPublicKey(key)?.bytes ?: key.encoded } /** @@ -429,7 +431,6 @@ object Crypto { * @throws InvalidKeyException if the private key is invalid. * @throws SignatureException if signing is not possible due to malformed data or private key. */ - @DeleteForDJVM @JvmStatic @Throws(InvalidKeyException::class, SignatureException::class) fun doSign(privateKey: PrivateKey, clearData: ByteArray): ByteArray = doSign(findSignatureScheme(privateKey), privateKey, clearData) @@ -444,7 +445,6 @@ object Crypto { * @throws InvalidKeyException if the private key is invalid. * @throws SignatureException if signing is not possible due to malformed data or private key. */ - @DeleteForDJVM @JvmStatic @Throws(InvalidKeyException::class, SignatureException::class) fun doSign(schemeCodeName: String, privateKey: PrivateKey, clearData: ByteArray): ByteArray { @@ -461,7 +461,6 @@ object Crypto { * @throws InvalidKeyException if the private key is invalid. * @throws SignatureException if signing is not possible due to malformed data or private key. */ - @DeleteForDJVM @JvmStatic @Throws(InvalidKeyException::class, SignatureException::class) fun doSign(signatureScheme: SignatureScheme, privateKey: PrivateKey, clearData: ByteArray): ByteArray { @@ -501,7 +500,6 @@ object Crypto { * @throws InvalidKeyException if the private key is invalid. * @throws SignatureException if signing is not possible due to malformed data or private key. */ - @DeleteForDJVM @JvmStatic @Throws(InvalidKeyException::class, SignatureException::class) fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature { @@ -688,7 +686,6 @@ object Crypto { * @return a KeyPair for the requested signature scheme code name. * @throws IllegalArgumentException if the requested signature scheme is not supported. */ - @DeleteForDJVM @JvmStatic fun generateKeyPair(schemeCodeName: String): KeyPair = generateKeyPair(findSignatureScheme(schemeCodeName)) @@ -699,7 +696,6 @@ object Crypto { * @return a new [KeyPair] for the requested [SignatureScheme]. * @throws IllegalArgumentException if the requested signature scheme is not supported. */ - @DeleteForDJVM @JvmOverloads @JvmStatic fun generateKeyPair(signatureScheme: SignatureScheme = DEFAULT_SIGNATURE_SCHEME): KeyPair { @@ -993,7 +989,8 @@ object Crypto { } private val interner = PrivateInterner() - private fun internPublicKey(key: PublicKey): PublicKey = interner.intern(key) + private fun internPublicKey(key: PublicKey): PublicKey = PublicKeyCache.cachePublicKey(interner.intern(key)) + private fun convertIfBCEdDSAPublicKey(key: PublicKey): PublicKey { return internPublicKey(when (key) { @@ -1049,7 +1046,6 @@ object Crypto { * @throws IllegalArgumentException on not supported scheme or if the given key specification * is inappropriate for a supported key factory to produce a private key. */ - @DeleteForDJVM @JvmStatic fun toSupportedPrivateKey(key: PrivateKey): PrivateKey { return when (key) { @@ -1086,7 +1082,6 @@ object Crypto { * CRL & CSR checks etc.). */ // TODO: perform all cryptographic operations via Crypto. - @DeleteForDJVM @JvmStatic fun registerProviders() { providerMap @@ -1097,7 +1092,6 @@ object Crypto { setBouncyCastleRNG() } - @DeleteForDJVM private fun setBouncyCastleRNG() { CryptoServicesRegistrar.setSecureRandom(newSecureRandom()) } diff --git a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt index c1c0d88cc0..3fc649f989 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt @@ -1,11 +1,7 @@ @file:Suppress("MatchingDeclarationName") -@file:KeepForDJVM @file:JvmName("CryptoUtils") - package net.corda.core.crypto -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.contracts.PrivacySalt import net.corda.core.crypto.internal.platformSecureRandomFactory import net.corda.core.serialization.SerializationDefaults @@ -32,7 +28,6 @@ import java.security.SignatureException * @throws InvalidKeyException if the private key is invalid. * @throws SignatureException if signing is not possible due to malformed data or private key. */ -@DeleteForDJVM @Throws(InvalidKeyException::class, SignatureException::class) fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature(Crypto.doSign(this, bytesToSign)) @@ -45,7 +40,6 @@ fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature * @throws InvalidKeyException if the private key is invalid. * @throws SignatureException if signing is not possible due to malformed data or private key. */ -@DeleteForDJVM @Throws(InvalidKeyException::class, SignatureException::class) fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey { return DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes) @@ -59,12 +53,10 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignat * @throws InvalidKeyException if the private key is invalid. * @throws SignatureException if signing is not possible due to malformed data or private key. */ -@DeleteForDJVM @Throws(InvalidKeyException::class, SignatureException::class) fun KeyPair.sign(bytesToSign: ByteArray): DigitalSignature.WithKey = private.sign(bytesToSign, public) /** Helper function to sign the bytes of [bytesToSign] with a key pair. */ -@DeleteForDJVM @Throws(InvalidKeyException::class, SignatureException::class) fun KeyPair.sign(bytesToSign: OpaqueBytes): DigitalSignature.WithKey = sign(bytesToSign.bytes) @@ -76,7 +68,6 @@ fun KeyPair.sign(bytesToSign: OpaqueBytes): DigitalSignature.WithKey = sign(byte * @throws InvalidKeyException if the private key is invalid. * @throws SignatureException if signing is not possible due to malformed data or private key. */ -@DeleteForDJVM @Throws(InvalidKeyException::class, SignatureException::class) fun KeyPair.sign(signableData: SignableData): TransactionSignature = Crypto.doSign(this, signableData) @@ -151,7 +142,6 @@ operator fun KeyPair.component1(): PrivateKey = this.private operator fun KeyPair.component2(): PublicKey = this.public /** A simple wrapper that will make it easier to swap out the signature algorithm we use in future. */ -@DeleteForDJVM fun generateKeyPair(): KeyPair = Crypto.generateKeyPair() /** @@ -196,7 +186,6 @@ fun KeyPair.verify(signatureData: ByteArray, clearData: ByteArray): Boolean = Cr * or if no strong [SecureRandom] implementations are available or if Security.getProperty("securerandom.strongAlgorithms") is null or empty, * which should never happen and suggests an unusual JVM or non-standard Java library. */ -@DeleteForDJVM @Throws(NoSuchAlgorithmException::class) fun secureRandomBytes(numOfBytes: Int): ByteArray = ByteArray(numOfBytes).apply { newSecureRandom().nextBytes(this) } @@ -241,7 +230,6 @@ object DummySecureRandom : SecureRandom(DummySecureRandomSpi(), null) * or if no strong SecureRandom implementations are available or if Security.getProperty("securerandom.strongAlgorithms") is null or empty, * which should never happen and suggests an unusual JVM or non-standard Java library. */ -@DeleteForDJVM @Throws(NoSuchAlgorithmException::class) fun newSecureRandom(): SecureRandom = platformSecureRandomFactory() @@ -249,7 +237,6 @@ fun newSecureRandom(): SecureRandom = platformSecureRandomFactory() * Returns a random positive non-zero long generated using a secure RNG. This function sacrifies a bit of entropy in order * to avoid potential bugs where the value is used in a context where negative numbers or zero are not expected. */ -@DeleteForDJVM fun random63BitValue(): Long { while (true) { val candidate = Math.abs(newSecureRandom().nextLong()) diff --git a/core/src/main/kotlin/net/corda/core/crypto/DigestAlgorithm.kt b/core/src/main/kotlin/net/corda/core/crypto/DigestAlgorithm.kt index 13610eba5e..548e4ea6c8 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/DigestAlgorithm.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/DigestAlgorithm.kt @@ -1,11 +1,8 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM - /** * Interface for injecting custom digest implementation bypassing JCA. */ -@KeepForDJVM interface DigestAlgorithm { /** * Algorithm identifier. diff --git a/core/src/main/kotlin/net/corda/core/crypto/DigestService.kt b/core/src/main/kotlin/net/corda/core/crypto/DigestService.kt index e163993443..843d47cd29 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/DigestService.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/DigestService.kt @@ -1,7 +1,5 @@ package net.corda.core.crypto -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.contracts.PrivacySalt import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationDefaults @@ -24,13 +22,11 @@ import java.security.MessageDigest * @param hashAlgorithm the name of the hash algorithm to be used for the instance */ @CordaSerializable -@KeepForDJVM data class DigestService(val hashAlgorithm: String) { init { require(hashAlgorithm.isNotEmpty()) { "Hash algorithm name unavailable or not specified" } } - @KeepForDJVM companion object { private const val NONCE_SIZE = 8 /** @@ -114,5 +110,4 @@ data class DigestService(val hashAlgorithm: String) { } } -@DeleteForDJVM fun DigestService.randomHash(): SecureHash = SecureHash.random(this.hashAlgorithm) diff --git a/core/src/main/kotlin/net/corda/core/crypto/DigitalSignature.kt b/core/src/main/kotlin/net/corda/core/crypto/DigitalSignature.kt index da48067fb1..b7b9c031e4 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/DigitalSignature.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/DigitalSignature.kt @@ -1,6 +1,5 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.OpaqueBytes import java.security.InvalidKeyException @@ -9,10 +8,8 @@ import java.security.SignatureException /** A wrapper around a digital signature. */ @CordaSerializable -@KeepForDJVM open class DigitalSignature(bytes: ByteArray) : OpaqueBytes(bytes) { /** A digital signature that identifies who the public key is owned by. */ - @KeepForDJVM open class WithKey(val by: PublicKey, bytes: ByteArray) : DigitalSignature(bytes) { /** * Utility to simplify the act of verifying a signature. diff --git a/core/src/main/kotlin/net/corda/core/crypto/MerkleTree.kt b/core/src/main/kotlin/net/corda/core/crypto/MerkleTree.kt index 692d4e1345..321f717f12 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/MerkleTree.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/MerkleTree.kt @@ -1,6 +1,5 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import java.util.* /** @@ -15,8 +14,9 @@ import java.util.* sealed class MerkleTree { abstract val hash: SecureHash - @KeepForDJVM data class Leaf(override val hash: SecureHash) : MerkleTree() - @KeepForDJVM data class Node(override val hash: SecureHash, val left: MerkleTree, val right: MerkleTree) : MerkleTree() + data class Leaf(override val hash: SecureHash) : MerkleTree() + + data class Node(override val hash: SecureHash, val left: MerkleTree, val right: MerkleTree) : MerkleTree() companion object { private fun isPow2(num: Int): Boolean = num and (num - 1) == 0 diff --git a/core/src/main/kotlin/net/corda/core/crypto/NullKeys.kt b/core/src/main/kotlin/net/corda/core/crypto/NullKeys.kt index 082163c5ef..a828fd9425 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/NullKeys.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/NullKeys.kt @@ -1,10 +1,8 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import net.corda.core.identity.AnonymousParty import java.security.PublicKey -@KeepForDJVM object NullKeys { object NullPublicKey : PublicKey, Comparable { override fun getAlgorithm() = "NULL" diff --git a/core/src/main/kotlin/net/corda/core/crypto/PartialMerkleTree.kt b/core/src/main/kotlin/net/corda/core/crypto/PartialMerkleTree.kt index f3aab735ff..4fbffc24d7 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/PartialMerkleTree.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/PartialMerkleTree.kt @@ -2,12 +2,10 @@ package net.corda.core.crypto import net.corda.core.CordaException import net.corda.core.CordaInternal -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.DeprecatedConstructorForDeserialization import java.util.* -@KeepForDJVM @CordaSerializable class MerkleTreeException(val reason: String) : CordaException("Partial Merkle Tree exception. Reason: $reason") @@ -45,7 +43,6 @@ class MerkleTreeException(val reason: String) : CordaException("Partial Merkle T * (there can be a difference in obtained leaves ordering - that's why it's a set comparison not hashing leaves into a tree). * If both equalities hold, we can assume that l3 and l5 belong to the transaction with root h15. */ -@KeepForDJVM @CordaSerializable class PartialMerkleTree(val root: PartialTree) { /** @@ -57,9 +54,9 @@ class PartialMerkleTree(val root: PartialTree) { */ @CordaSerializable sealed class PartialTree { - @KeepForDJVM data class IncludedLeaf(val hash: SecureHash) : PartialTree() - @KeepForDJVM data class Leaf(val hash: SecureHash) : PartialTree() - @KeepForDJVM data class Node(val left: PartialTree, val right: PartialTree, val hashAlgorithm: String? = SecureHash.SHA2_256) : PartialTree(){ + data class IncludedLeaf(val hash: SecureHash) : PartialTree() + data class Leaf(val hash: SecureHash) : PartialTree() + data class Node(val left: PartialTree, val right: PartialTree, val hashAlgorithm: String? = SecureHash.SHA2_256) : PartialTree() { /** * Old version of [PartialTree.Node] constructor for ABI compatibility. */ diff --git a/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt b/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt index 0e9dbb6d20..a930aa254d 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt @@ -1,11 +1,8 @@ @file:Suppress("TooManyFunctions", "MagicNumber") -@file:KeepForDJVM package net.corda.core.crypto import io.netty.util.concurrent.FastThreadLocal import net.corda.core.CordaInternal -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.crypto.internal.DigestAlgorithmFactory import net.corda.core.internal.utilities.Internable import net.corda.core.internal.utilities.PrivateInterner @@ -13,7 +10,6 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.parseAsHex import net.corda.core.utilities.toHexString -import java.nio.ByteBuffer import java.security.MessageDigest import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentMap @@ -23,7 +19,6 @@ import java.util.function.Supplier * Container for a cryptographically secure hash value. * Provides utilities for generating a cryptographic hash using different algorithms (currently only SHA-256 supported). */ -@KeepForDJVM @CordaSerializable sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) { /** SHA-256 is part of the SHA-2 hash function family. Generated hash is fixed size, 256-bits (32-bytes). */ @@ -39,9 +34,10 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) { return true } - // This is an efficient hashCode, because there is no point in performing a hash calculation on a cryptographic hash. - // It just takes the first 4 bytes and transforms them into an Int. - override fun hashCode() = ByteBuffer.wrap(bytes).int + override fun hashCode(): Int { + // Hash code not overridden on purpose (super class impl will do), but don't delete or have to deal with detekt and API checker. + return super.hashCode() + } /** * Convert the hash value to an uppercase hexadecimal [String]. @@ -62,7 +58,10 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) { } } - override fun hashCode() = ByteBuffer.wrap(bytes).int + override fun hashCode(): Int { + // Hash code not overridden on purpose (super class impl will do), but don't delete or have to deal with detekt and API checker. + return super.hashCode() + } override fun toString(): String { return "$algorithm$DELIMITER${toHexString()}" @@ -288,14 +287,12 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) { /** * Generates a random SHA-256 value. */ - @DeleteForDJVM @JvmStatic fun randomSHA256() = sha256(secureRandomBytes(32)) /** * Generates a random hash value. */ - @DeleteForDJVM @JvmStatic fun random(algorithm: String): SecureHash { return if (algorithm == SHA2_256) { diff --git a/core/src/main/kotlin/net/corda/core/crypto/SignableData.kt b/core/src/main/kotlin/net/corda/core/crypto/SignableData.kt index cfe0ec96a8..124794b730 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/SignableData.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/SignableData.kt @@ -1,6 +1,5 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializable /** @@ -13,5 +12,4 @@ import net.corda.core.serialization.CordaSerializable * @param signatureMetadata meta data required. */ @CordaSerializable -@KeepForDJVM data class SignableData(val txId: SecureHash, val signatureMetadata: SignatureMetadata) diff --git a/core/src/main/kotlin/net/corda/core/crypto/SignatureMetadata.kt b/core/src/main/kotlin/net/corda/core/crypto/SignatureMetadata.kt index 6c8d9c33e6..99335bde4c 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/SignatureMetadata.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/SignatureMetadata.kt @@ -1,6 +1,5 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializable /** @@ -13,5 +12,4 @@ import net.corda.core.serialization.CordaSerializable * @param schemeNumberID number id of the signature scheme used based on signer's key-pair, see [SignatureScheme.schemeNumberID]. */ @CordaSerializable -@KeepForDJVM data class SignatureMetadata(val platformVersion: Int, val schemeNumberID: Int) diff --git a/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt b/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt index 4696e9b2db..27b3fc4750 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt @@ -1,6 +1,5 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import org.bouncycastle.asn1.x509.AlgorithmIdentifier import java.security.KeyFactory import java.security.Signature @@ -22,7 +21,6 @@ import java.security.spec.AlgorithmParameterSpec * @param keySize the private key size (currently used for RSA only). * @param desc a human-readable description for this scheme. */ -@KeepForDJVM data class SignatureScheme( val schemeNumberID: Int, val schemeCodeName: String, diff --git a/core/src/main/kotlin/net/corda/core/crypto/SignedData.kt b/core/src/main/kotlin/net/corda/core/crypto/SignedData.kt index c33ac597fd..87a4ba8a32 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/SignedData.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/SignedData.kt @@ -1,6 +1,5 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializedBytes @@ -15,7 +14,6 @@ import java.security.SignatureException * @param sig the (unverified) signature for the data. */ @CordaSerializable -@KeepForDJVM open class SignedData(val raw: SerializedBytes, val sig: DigitalSignature.WithKey) { /** * Return the deserialized data if the signature can be verified. diff --git a/core/src/main/kotlin/net/corda/core/crypto/TransactionSignature.kt b/core/src/main/kotlin/net/corda/core/crypto/TransactionSignature.kt index a26139db1d..29d0a0d212 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/TransactionSignature.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/TransactionSignature.kt @@ -1,6 +1,5 @@ package net.corda.core.crypto -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializable import java.security.InvalidKeyException import java.security.PublicKey @@ -16,7 +15,6 @@ import java.util.* * @property partialMerkleTree required when multi-transaction signing is utilised. */ @CordaSerializable -@KeepForDJVM class TransactionSignature(bytes: ByteArray, val by: PublicKey, val signatureMetadata: SignatureMetadata, val partialMerkleTree: PartialMerkleTree?) : DigitalSignature(bytes) { /** * Construct a [TransactionSignature] with [partialMerkleTree] set to null. diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt index 6990905d89..fc7336f855 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt @@ -1,7 +1,5 @@ package net.corda.core.crypto.internal -import net.corda.core.DeleteForDJVM -import net.corda.core.StubOutForDJVM import net.corda.core.crypto.SignatureScheme import net.corda.core.internal.LazyPool import java.security.Provider @@ -25,13 +23,7 @@ object Instances { fun getSignatureInstance(algorithm: String, provider: Provider?) = signatureFactory.borrow(algorithm, provider) fun releaseSignatureInstance(sig: Signature) = signatureFactory.release(sig) - // Used to work around banning of ConcurrentHashMap in DJVM - private val signatureFactory: SignatureFactory = try { - makeCachingFactory() - } catch (e: UnsupportedOperationException) { - // Thrown by DJVM for method stubbed out below. - makeFactory() - } + private val signatureFactory: SignatureFactory = CachingSignatureFactory() // The provider itself is a very bad key class as hashCode() is expensive and contended. So use name and version instead. private data class SignatureKey(val algorithm: String, val providerName: String?, val providerVersion: Double?) { @@ -39,12 +31,6 @@ object Instances { @Suppress("DEPRECATION") provider?.version) // JDK11: should replace with getVersionStr() (since 9) } - @StubOutForDJVM - private fun makeCachingFactory(): SignatureFactory { - return CachingSignatureFactory() - } - - @DeleteForDJVM private class CachingSignatureFactory : SignatureFactory { private val signatureInstances = ConcurrentHashMap>() diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt index cd598dafad..3a0062b251 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt @@ -1,9 +1,7 @@ @file:JvmName("PlatformSecureRandom") -@file:DeleteForDJVM package net.corda.core.crypto.internal import io.netty.util.concurrent.FastThreadLocal -import net.corda.core.DeleteForDJVM import net.corda.core.crypto.DummySecureRandom import net.corda.core.utilities.SgxSupport import net.corda.core.utilities.loggerFor @@ -30,7 +28,6 @@ internal val platformSecureRandom: () -> SecureRandom = when { } } -@DeleteForDJVM class PlatformSecureRandomService(provider: Provider) : Provider.Service(provider, "SecureRandom", algorithm, PlatformSecureRandomSpi::javaClass.name, null, null) { @@ -54,7 +51,6 @@ class PlatformSecureRandomService(provider: Provider) override fun newInstance(constructorParameter: Any?) = instance } -@DeleteForDJVM private class PlatformSecureRandomSpi : SecureRandomSpi() { private val threadLocalSecureRandom = object : FastThreadLocal() { override fun initialValue() = SecureRandom.getInstanceStrong() @@ -67,7 +63,6 @@ private class PlatformSecureRandomSpi : SecureRandomSpi() { override fun engineGenerateSeed(numBytes: Int): ByteArray = secureRandom.generateSeed(numBytes) } -@DeleteForDJVM @Suppress("TooGenericExceptionCaught", "TooGenericExceptionThrown") private class LinuxSecureRandomSpi : SecureRandomSpi() { private fun openURandom(): InputStream { diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt index 3b17e644f1..0ac52cdedb 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt @@ -1,6 +1,5 @@ package net.corda.core.crypto.internal -import net.corda.core.DeleteForDJVM import net.corda.core.crypto.CordaSecurityProvider import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 import net.corda.core.crypto.Crypto.decodePrivateKey @@ -62,5 +61,4 @@ internal val providerMap: Map = unmodifiableMap( .associateByTo(LinkedHashMap(), Provider::getName) ) -@DeleteForDJVM fun platformSecureRandomFactory(): SecureRandom = platformSecureRandom() // To minimise diff of CryptoUtils against open-source. diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/PublicKeyCache.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/PublicKeyCache.kt new file mode 100644 index 0000000000..2c648d812a --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/PublicKeyCache.kt @@ -0,0 +1,59 @@ +package net.corda.core.crypto.internal + +import net.corda.core.utilities.ByteSequence +import java.lang.ref.ReferenceQueue +import java.lang.ref.WeakReference +import java.security.PublicKey +import java.util.concurrent.ConcurrentHashMap + +object PublicKeyCache { + private val DISABLE = java.lang.Boolean.getBoolean("net.corda.core.pubkeycache.disable") + + private val collectedWeakPubKeys = ReferenceQueue() + + private class WeakPubKey(key: PublicKey, val bytes: ByteSequence? = null) : WeakReference(key, collectedWeakPubKeys) { + private val hashCode = key.hashCode() + + override fun hashCode(): Int = hashCode + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is WeakPubKey) return false + if(this.hashCode != other.hashCode) return false + val thisGet = this.get() + val otherGet = other.get() + if(thisGet == null || otherGet == null) return false + return thisGet == otherGet + } + } + + private val pubKeyToBytes = ConcurrentHashMap() + private val bytesToPubKey = ConcurrentHashMap() + + private fun reapCollectedWeakPubKeys() { + while(true) { + val weakPubKey = (collectedWeakPubKeys.poll() as? WeakPubKey) ?: break + pubKeyToBytes.remove(weakPubKey) + bytesToPubKey.remove(weakPubKey.bytes!!) + } + } + + fun bytesForCachedPublicKey(key: PublicKey): ByteSequence? { + if (DISABLE) return null + val weakPubKey = WeakPubKey(key) + return pubKeyToBytes[weakPubKey] + } + + fun publicKeyForCachedBytes(bytes: ByteSequence): PublicKey? { + if (DISABLE) return null + return bytesToPubKey[bytes]?.get() + } + + fun cachePublicKey(key: PublicKey): PublicKey { + if (DISABLE) return key + reapCollectedWeakPubKeys() + val weakPubKey = WeakPubKey(key, ByteSequence.of(key.encoded)) + pubKeyToBytes.putIfAbsent(weakPubKey, weakPubKey.bytes!!) + bytesToPubKey.putIfAbsent(weakPubKey.bytes, weakPubKey) + return key + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt index 7d5a1505c1..a2f3e23609 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt @@ -3,23 +3,29 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.CordaInternal import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.isFulfilledBy import net.corda.core.identity.Party import net.corda.core.identity.groupAbstractPartyByWellKnownParty +import net.corda.core.internal.PlatformVersionSwitches +import net.corda.core.internal.ServiceHubCoreInternal import net.corda.core.internal.pushToLoggingContext import net.corda.core.internal.telemetry.telemetryServiceInternal import net.corda.core.internal.warnOnce import net.corda.core.node.StatesToRecord import net.corda.core.node.StatesToRecord.ONLY_RELEVANT +import net.corda.core.serialization.DeprecatedConstructorForDeserialization import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker +import net.corda.core.utilities.Try import net.corda.core.utilities.debug +import java.time.Duration /** * Verifies the given transaction, then sends it to the named notary. If the notary agrees that the transaction * is acceptable then it is from that point onwards committed to the ledger, and will be written through to the - * vault. Additionally it will be distributed to the parties reflected in the participants list of the states. + * vault. Additionally, it will be distributed to the parties reflected in the participants list of the states. * * By default, the initiating flow will commit states that are relevant to the initiating party as indicated by * [StatesToRecord.ONLY_RELEVANT]. Relevance is determined by the union of all participants to states which have been @@ -33,6 +39,9 @@ import net.corda.core.utilities.debug * can also be included, but they must specify [StatesToRecord.ALL_VISIBLE] for statesToRecord if they wish to record the * contract states into their vaults. * + * As of 4.11 a list of observer [FlowSession] can be specified to indicate sessions with transaction non-participants (e.g. observers). + * This enables ledger recovery to default these sessions associated StatesToRecord value to [StatesToRecord.ALL_VISIBLE]. + * * The flow returns the same transaction but with the additional signatures from the notary. * * NOTE: This is an inlined flow but for backwards compatibility is annotated with [InitiatingFlow]. @@ -40,19 +49,21 @@ import net.corda.core.utilities.debug // To maintain backwards compatibility with the old API, FinalityFlow can act both as an initiating flow and as an inlined flow. // This is only possible because a flow is only truly initiating when the first call to initiateFlow is made (where the // presence of @InitiatingFlow is checked). So the new API is inlined simply because that code path doesn't call initiateFlow. +@Suppress("TooManyFunctions") @InitiatingFlow class FinalityFlow private constructor(val transaction: SignedTransaction, private val oldParticipants: Collection, override val progressTracker: ProgressTracker, private val sessions: Collection, private val newApi: Boolean, - private val statesToRecord: StatesToRecord = ONLY_RELEVANT) : FlowLogic() { + private val statesToRecord: StatesToRecord = ONLY_RELEVANT, + private val observerSessions: Collection = emptySet()) : FlowLogic() { @CordaInternal - data class ExtraConstructorArgs(val oldParticipants: Collection, val sessions: Collection, val newApi: Boolean, val statesToRecord: StatesToRecord) + data class ExtraConstructorArgs(val oldParticipants: Collection, val sessions: Collection, val newApi: Boolean, val statesToRecord: StatesToRecord, val observerSessions: Collection) @CordaInternal - fun getExtraConstructorArgs() = ExtraConstructorArgs(oldParticipants, sessions, newApi, statesToRecord) + fun getExtraConstructorArgs() = ExtraConstructorArgs(oldParticipants, sessions, newApi, statesToRecord, observerSessions) @Deprecated(DEPRECATION_MSG) constructor(transaction: SignedTransaction, extraRecipients: Set, progressTracker: ProgressTracker) : this( @@ -124,6 +135,10 @@ class FinalityFlow private constructor(val transaction: SignedTransaction, progressTracker: ProgressTracker ) : this(transaction, oldParticipants, progressTracker, sessions, true) + constructor(transaction: SignedTransaction, + sessions: Collection, + observerSessions: Collection) : this(transaction, emptyList(), tracker(), sessions, true, observerSessions = observerSessions) + companion object { private const val DEPRECATION_MSG = "It is unsafe to use this constructor as it requires nodes to automatically " + "accept notarised transactions without first checking their relevancy. Instead, use one of the constructors " + @@ -133,13 +148,26 @@ class FinalityFlow private constructor(val transaction: SignedTransaction, override fun childProgressTracker() = NotaryFlow.Client.tracker() } - object BROADCASTING : ProgressTracker.Step("Broadcasting transaction to participants") + @Suppress("ClassNaming") + object RECORD_UNNOTARISED : ProgressTracker.Step("Recording un-notarised transaction locally") + @Suppress("ClassNaming") + object BROADCASTING_PRE_NOTARISATION : ProgressTracker.Step("Broadcasting un-notarised transaction") + @Suppress("ClassNaming") + object BROADCASTING_POST_NOTARISATION : ProgressTracker.Step("Broadcasting notary signature") + @Suppress("ClassNaming") + object BROADCASTING_NOTARY_ERROR : ProgressTracker.Step("Broadcasting notary error") + @Suppress("ClassNaming") + object FINALISING_TRANSACTION : ProgressTracker.Step("Finalising transaction locally") + object BROADCASTING : ProgressTracker.Step("Broadcasting notarised transaction to other participants") @JvmStatic - fun tracker() = ProgressTracker(NOTARISING, BROADCASTING) + fun tracker() = ProgressTracker(RECORD_UNNOTARISED, BROADCASTING_PRE_NOTARISATION, NOTARISING, BROADCASTING_POST_NOTARISATION, BROADCASTING_NOTARY_ERROR, FINALISING_TRANSACTION, BROADCASTING) } + private lateinit var externalTxParticipants: Set + @Suspendable + @Suppress("ComplexMethod", "NestedBlockDepth") @Throws(NotaryException::class) override fun call(): SignedTransaction { if (!newApi) { @@ -149,68 +177,217 @@ class FinalityFlow private constructor(val transaction: SignedTransaction, require(sessions.none { serviceHub.myInfo.isLegalIdentity(it.counterparty) }) { "Do not provide flow sessions for the local node. FinalityFlow will record the notarised transaction locally." } + sessions.intersect(observerSessions.toSet()).let { + require(it.isEmpty()) { "The following parties are specified both in flow sessions and observer flow sessions: $it" } + } } // Note: this method is carefully broken up to minimize the amount of data reachable from the stack at // the point where subFlow is invoked, as that minimizes the checkpointing work to be done. // // Lookup the resolved transactions and use them to map each signed transaction to the list of participants. - // Then send to the notary if needed, record locally and distribute. transaction.pushToLoggingContext() logCommandData() val ledgerTransaction = verifyTx() - val externalTxParticipants = extractExternalParticipants(ledgerTransaction) + externalTxParticipants = extractExternalParticipants(ledgerTransaction) if (newApi) { - val sessionParties = sessions.map { it.counterparty } - val missingRecipients = externalTxParticipants - sessionParties - oldParticipants + val sessionParties = sessions.map { it.counterparty }.toSet() + val missingRecipients = externalTxParticipants - sessionParties - oldParticipants.toSet() require(missingRecipients.isEmpty()) { "Flow sessions were not provided for the following transaction participants: $missingRecipients" } - sessionParties.intersect(oldParticipants).let { + sessionParties.intersect(oldParticipants.toSet()).let { require(it.isEmpty()) { "The following parties are specified both in flow sessions and in the oldParticipants list: $it" } } } - val notarised = notariseAndRecord() + // Recoverability + // As of platform version 13 we introduce a 2-phase finality protocol whereby + // - record un-notarised transaction locally and broadcast to external participants to record + // - notarise transaction + // - broadcast notary signature to external participants (finalise remotely) + // - finalise locally - progressTracker.currentStep = BROADCASTING + val (oldPlatformSessions, newPlatformSessions) = (sessions + observerSessions).partition { + serviceHub.networkMapCache.getNodeByLegalIdentity(it.counterparty)?.platformVersion!! < PlatformVersionSwitches.TWO_PHASE_FINALITY + } - if (newApi) { - oldV3Broadcast(notarised, oldParticipants.toSet()) - for (session in sessions) { + val requiresNotarisation = needsNotarySignature(transaction) + val useTwoPhaseFinality = serviceHub.myInfo.platformVersion >= PlatformVersionSwitches.TWO_PHASE_FINALITY + if (useTwoPhaseFinality) { + val stxn = if (requiresNotarisation) { + recordLocallyAndBroadcast(newPlatformSessions, transaction) try { - subFlow(SendTransactionFlow(session, notarised)) - logger.info("Party ${session.counterparty} received the transaction.") + val (notarisedTxn, notarySignatures) = notarise() + if (newPlatformSessions.isNotEmpty()) { + broadcastSignaturesAndFinalise(newPlatformSessions, notarySignatures) + } else { + finaliseLocally(notarisedTxn, notarySignatures) + } + notarisedTxn + } catch (e: NotaryException) { + (serviceHub as ServiceHubCoreInternal).removeUnnotarisedTransaction(transaction.id) + if (newPlatformSessions.isNotEmpty()) { + broadcastNotaryError(newPlatformSessions, e) + } else sleep(Duration.ZERO) // force checkpoint to persist db update. + throw e + } + } + else { + if (newPlatformSessions.isNotEmpty()) + finaliseLocallyAndBroadcast(newPlatformSessions, transaction) + else + recordTransactionLocally(transaction) + transaction + } + broadcastToOtherParticipants(externalTxParticipants, oldPlatformSessions, stxn) + return stxn + } + else { + val stxn = if (requiresNotarisation) { + notarise().first + } else transaction + recordTransactionLocally(stxn) + broadcastToOtherParticipants(externalTxParticipants, newPlatformSessions + oldPlatformSessions, stxn) + return stxn + } + } + + @Suspendable + private fun recordLocallyAndBroadcast(sessions: Collection, tx: SignedTransaction) { + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#recordLocallyAndBroadcast", flowLogic = this) { + recordUnnotarisedTransaction(tx) + progressTracker.currentStep = BROADCASTING_PRE_NOTARISATION + broadcast(sessions, tx) + } + } + + @Suspendable + private fun finaliseLocallyAndBroadcast(sessions: Collection, tx: SignedTransaction) { + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#finaliseLocallyAndBroadcast", flowLogic = this) { + finaliseLocally(tx) + progressTracker.currentStep = BROADCASTING + broadcast(sessions, tx) + } + } + + @Suspendable + private fun broadcast(sessions: Collection, tx: SignedTransaction) { + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#broadcast", flowLogic = this) { + try { + logger.debug { "Sending transaction to party sessions: $sessions." } + val (participantSessions, observerSessions) = deriveSessions(sessions) + subFlow(object : SendTransactionFlow(tx, participantSessions, observerSessions, statesToRecord, true) { + override fun isFinality(): Boolean = true + }) + } catch (e: UnexpectedFlowEndException) { + throw UnexpectedFlowEndException( + "One of the sessions ${sessions.map { it.counterparty }} has finished prematurely and we're trying to send them a transaction." + + "Did they forget to call ReceiveFinalityFlow? (${e.message})", + e.cause, + e.originalErrorId + ) + } + } + } + + private fun deriveSessions(newPlatformSessions: Collection) = + Pair(newPlatformSessions.filter { it.counterparty in externalTxParticipants }.toSet(), + (observerSessions + newPlatformSessions.filter { it.counterparty !in externalTxParticipants }).toSet()) + + @Suspendable + private fun broadcastSignaturesAndFinalise(sessions: Collection, notarySignatures: List) { + progressTracker.currentStep = BROADCASTING_POST_NOTARISATION + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#broadcastSignaturesAndFinalise", flowLogic = this) { + logger.info("Transaction notarised and broadcasting notary signature.") + sessions.forEach { session -> + try { + logger.debug { "Sending notary signature to party $session." } + session.send(Try.Success(notarySignatures)) + // remote will finalise txn with notary signature } catch (e: UnexpectedFlowEndException) { throw UnexpectedFlowEndException( - "${session.counterparty} has finished prematurely and we're trying to send them the finalised transaction. " + + "${session.counterparty} has finished prematurely and we're trying to send them the notary signature. " + "Did they forget to call ReceiveFinalityFlow? (${e.message})", e.cause, e.originalErrorId ) } } - } else { - oldV3Broadcast(notarised, (externalTxParticipants + oldParticipants).toSet()) + finaliseLocally(transaction, notarySignatures) } + } - logger.info("All parties received the transaction successfully.") + @Suspendable + private fun finaliseLocally(stx: SignedTransaction, notarySignatures: List = emptyList()) { + progressTracker.currentStep = FINALISING_TRANSACTION + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#finaliseLocally", flowLogic = this) { + if (notarySignatures.isEmpty()) { + (serviceHub as ServiceHubCoreInternal).finalizeTransaction(stx, statesToRecord) + logger.info("Finalised transaction locally.") + } else { + (serviceHub as ServiceHubCoreInternal).finalizeTransactionWithExtraSignatures(stx, notarySignatures, statesToRecord) + logger.info("Finalised transaction locally with notary signature.") + } + } + } - return notarised + @Suspendable + private fun broadcastNotaryError(sessions: Collection, error: NotaryException) { + progressTracker.currentStep = BROADCASTING_NOTARY_ERROR + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#broadcastDoubleSpendError", flowLogic = this) { + logger.info("Broadcasting notary error.") + sessions.forEach { session -> + try { + logger.debug { "Sending notary error to party $session." } + session.send(Try.Failure>(error)) + } catch (e: UnexpectedFlowEndException) { + throw UnexpectedFlowEndException( + "${session.counterparty} has finished prematurely and we're trying to send them a notary error. " + + "Did they forget to call ReceiveFinalityFlow? (${e.message})", + e.cause, + e.originalErrorId + ) + } + } + } + } + + @Suspendable + private fun broadcastToOtherParticipants(externalTxParticipants: Set, sessions: Collection, tx: SignedTransaction) { + if (externalTxParticipants.isEmpty() && sessions.isEmpty() && oldParticipants.isEmpty()) return + progressTracker.currentStep = BROADCASTING + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#broadcastToOtherParticipants", flowLogic = this) { + logger.info("Broadcasting complete transaction to other participants.") + if (newApi) { + oldV3Broadcast(tx, oldParticipants.toSet()) + try { + logger.debug { "Sending transaction to party sessions $sessions." } + subFlow(SendTransactionFlow(tx, sessions.toSet(), emptySet(), statesToRecord, true)) + } catch (e: UnexpectedFlowEndException) { + throw UnexpectedFlowEndException( + "One of the sessions ${sessions.map { it.counterparty }} has finished prematurely and we're trying to send them the finalised transaction. " + + "Did they forget to call ReceiveFinalityFlow? (${e.message})", + e.cause, + e.originalErrorId + ) + } + } else { + oldV3Broadcast(tx, (externalTxParticipants + oldParticipants).toSet()) + } + logger.info("Broadcasted complete transaction to other participants.") + } } @Suspendable private fun oldV3Broadcast(notarised: SignedTransaction, recipients: Set) { - for (recipient in recipients) { - if (!serviceHub.myInfo.isLegalIdentity(recipient)) { - logger.debug { "Sending transaction to party $recipient." } - val session = initiateFlow(recipient) - subFlow(SendTransactionFlow(session, notarised)) - logger.info("Party $recipient received the transaction.") - } - } + val remoteRecipients = recipients.filter { !serviceHub.myInfo.isLegalIdentity(it) } + logger.debug { "Sending transaction to parties $remoteRecipients." } + val sessions = remoteRecipients.map { initiateFlow(it) }.toSet() + subFlow(SendTransactionFlow(notarised, sessions, emptySet(), statesToRecord)) + logger.info("Parties $remoteRecipients received the transaction.") } private fun logCommandData() { @@ -221,22 +398,30 @@ class FinalityFlow private constructor(val transaction: SignedTransaction, } @Suspendable - private fun notariseAndRecord(): SignedTransaction { - serviceHub.telemetryServiceInternal.span("${this::class.java.name}#notariseAndRecord", flowLogic = this) { - val notarised = if (needsNotarySignature(transaction)) { - progressTracker.currentStep = NOTARISING - val notarySignatures = subFlow(NotaryFlow.Client(transaction, skipVerification = true)) - transaction + notarySignatures - } else { - logger.info("No need to notarise this transaction.") - transaction - } - serviceHub.telemetryServiceInternal.span("${this::class.java.name}#notariseAndRecord:recordTransactions", flowLogic = this) { - logger.info("Recording transaction locally.") - serviceHub.recordTransactions(statesToRecord, listOf(notarised)) - logger.info("Recorded transaction locally successfully.") - } - return notarised + private fun recordTransactionLocally(tx: SignedTransaction): SignedTransaction { + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#recordTransactionLocally", flowLogic = this) { + serviceHub.recordTransactions(statesToRecord, listOf(tx)) + logger.info("Recorded transaction locally.") + return tx + } + } + + @Suspendable + private fun recordUnnotarisedTransaction(tx: SignedTransaction): SignedTransaction { + progressTracker.currentStep = RECORD_UNNOTARISED + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#recordUnnotarisedTransaction", flowLogic = this) { + (serviceHub as ServiceHubCoreInternal).recordUnnotarisedTransaction(tx) + logger.info("Recorded un-notarised transaction locally.") + return tx + } + } + + @Suspendable + private fun notarise(): Pair> { + return serviceHub.telemetryServiceInternal.span("${this::class.java.name}#notariseOrRecord", flowLogic = this) { + progressTracker.currentStep = NOTARISING + val notarySignatures = subFlow(NotaryFlow.Client(transaction, skipVerification = true)) + Pair(transaction + notarySignatures, notarySignatures) } } @@ -254,7 +439,7 @@ class FinalityFlow private constructor(val transaction: SignedTransaction, private fun extractExternalParticipants(ltx: LedgerTransaction): Set { val participants = ltx.outputStates.flatMap { it.participants } + ltx.inputStates.flatMap { it.participants } - return groupAbstractPartyByWellKnownParty(serviceHub, participants).keys - serviceHub.myInfo.legalIdentities + return groupAbstractPartyByWellKnownParty(serviceHub, participants).keys - serviceHub.myInfo.legalIdentities.toSet() } private fun verifyTx(): LedgerTransaction { @@ -268,6 +453,20 @@ class FinalityFlow private constructor(val transaction: SignedTransaction, } } +object NotarySigCheck { + fun needsNotarySignature(stx: SignedTransaction): Boolean { + val wtx = stx.tx + val needsNotarisation = wtx.inputs.isNotEmpty() || wtx.references.isNotEmpty() || wtx.timeWindow != null + return needsNotarisation && hasNoNotarySignature(stx) + } + + private fun hasNoNotarySignature(stx: SignedTransaction): Boolean { + val notaryKey = stx.tx.notary?.owningKey + val signers = stx.sigs.asSequence().map { it.by }.toSet() + return notaryKey?.isFulfilledBy(signers) != true + } +} + /** * The receiving counterpart to [FinalityFlow]. * @@ -281,13 +480,22 @@ class FinalityFlow private constructor(val transaction: SignedTransaction, * @param expectedTxId Expected ID of the transaction that's about to be received. This is typically retrieved from * [SignTransactionFlow]. Setting it to null disables the expected transaction ID check. * @param statesToRecord Which states to commit to the vault. Defaults to [StatesToRecord.ONLY_RELEVANT]. + * @param handlePropagatedNotaryError Whether to catch and propagate Double Spend exception to peers. */ -class ReceiveFinalityFlow @JvmOverloads constructor(private val otherSideSession: FlowSession, - private val expectedTxId: SecureHash? = null, - private val statesToRecord: StatesToRecord = ONLY_RELEVANT) : FlowLogic() { +class ReceiveFinalityFlow(private val otherSideSession: FlowSession, + private val expectedTxId: SecureHash? = null, + private val statesToRecord: StatesToRecord = ONLY_RELEVANT, + private val handlePropagatedNotaryError: Boolean? = null) : FlowLogic() { + + @DeprecatedConstructorForDeserialization(version = 1) + @JvmOverloads constructor(otherSideSession: FlowSession, + expectedTxId: SecureHash? = null, + statesToRecord: StatesToRecord = ONLY_RELEVANT) : this(otherSideSession, expectedTxId, statesToRecord, null) + + @Suppress("ComplexMethod", "NestedBlockDepth") @Suspendable override fun call(): SignedTransaction { - return subFlow(object : ReceiveTransactionFlow(otherSideSession, checkSufficientSignatures = true, statesToRecord = statesToRecord) { + return subFlow(object : ReceiveTransactionFlow(otherSideSession, checkSufficientSignatures = true, statesToRecord = statesToRecord, handlePropagatedNotaryError = handlePropagatedNotaryError) { override fun checkBeforeRecording(stx: SignedTransaction) { require(expectedTxId == null || expectedTxId == stx.id) { "We expected to receive transaction with ID $expectedTxId but instead got ${stx.id}. Transaction was" + diff --git a/core/src/main/kotlin/net/corda/core/flows/FinalityRecoveryFlow.kt b/core/src/main/kotlin/net/corda/core/flows/FinalityRecoveryFlow.kt new file mode 100644 index 0000000000..1d03305243 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/flows/FinalityRecoveryFlow.kt @@ -0,0 +1,99 @@ +package net.corda.core.flows + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.CordaInternal +import net.corda.core.crypto.SecureHash +import net.corda.core.flows.FinalityFlow.Companion.tracker +import net.corda.core.identity.CordaX500Name +import net.corda.core.serialization.CordaSerializable +import net.corda.core.utilities.ProgressTracker +import java.time.Instant + +/** + * TWO_PHASE_FINALITY Recovery Flow + * This flow is exposed via the Core API for use by any CorDapp but its implementation is available in Enterprise only. + */ +@StartableByRPC +@InitiatingFlow +class FinalityRecoveryFlow( + private val txIds: Collection = emptySet(), + private val flowIds: Collection = emptySet(), + private val matchingCriteria: FlowRecoveryQuery? = null, + private val forceRecover: Boolean = false, + private val recoverAll: Boolean = false, + private val forceRecoverFlowIds: Collection = emptySet(), + override val progressTracker: ProgressTracker = ProgressTracker()) : FlowLogic>() { + + @CordaInternal + data class ExtraConstructorArgs(val txIds: Collection, + val flowIds: Collection, + val matchingCriteria: FlowRecoveryQuery?, + val forceRecover: Boolean, + val recoverAll: Boolean, + val forceRecoverFlowIds: Collection) + @CordaInternal + fun getExtraConstructorArgs() = ExtraConstructorArgs(txIds, flowIds, matchingCriteria, forceRecover, recoverAll, forceRecoverFlowIds) + + constructor(txId: SecureHash, forceRecover: Boolean = false) : this(setOf(txId), forceRecover) + constructor(txIds: Collection, forceRecover: Boolean = false, recoverAll: Boolean = false) : this(txIds, emptySet(), null, forceRecover, recoverAll, emptySet(), tracker()) + constructor(flowId: StateMachineRunId, forceRecover: Boolean = false) : this(emptySet(), setOf(flowId), null, forceRecover) + constructor(flowIds: Collection, forceRecover: Boolean = false) : this(emptySet(), flowIds, null, forceRecover, false, emptySet(), tracker()) + constructor(recoverAll: Boolean, forceRecover: Boolean = false) : this(emptySet(), emptySet(), null, forceRecover, recoverAll, emptySet(), tracker()) + constructor(matchingCriteria: FlowRecoveryQuery, forceRecover: Boolean = false) : this(emptySet(), emptySet(), matchingCriteria, forceRecover, false, emptySet(), tracker()) + + @Suspendable + @Throws(FlowRecoveryException::class) + override fun call(): Map { + throw NotImplementedError("Enterprise only feature") + } +} + +@CordaSerializable +class FlowRecoveryException(message: String, cause: Throwable? = null) : FlowException(message, cause) { + constructor(txnId: SecureHash, message: String, cause: Throwable? = null) : this("Flow recovery failed for transaction $txnId: $message", cause) +} + +@CordaSerializable +data class FlowRecoveryQuery( + val timeframe: FlowTimeWindow? = null, + val initiatedBy: List? = null, + val counterParties: List? = null) { + init { + require(timeframe != null || initiatedBy != null || counterParties != null) { + "Must specify at least one recovery criteria" + } + } +} + +@CordaSerializable +data class FlowTimeWindow(val fromTime: Instant? = null, val untilTime: Instant? = null) { + + init { + if (fromTime == null && untilTime == null) + throw IllegalArgumentException("Must specify one or both of fromTime or/and untilTime") + fromTime?.let { startTime -> + untilTime?.let { endTime -> + if (endTime < startTime) { + throw IllegalArgumentException(FlowTimeWindow::fromTime.name + " must be before or equal to " + FlowTimeWindow::untilTime.name) + } + } + } + } + + companion object { + @JvmStatic + fun between(fromTime: Instant, untilTime: Instant): FlowTimeWindow { + return FlowTimeWindow(fromTime, untilTime) + } + + @JvmStatic + fun fromOnly(fromTime: Instant): FlowTimeWindow { + return FlowTimeWindow(fromTime = fromTime) + } + + @JvmStatic + fun untilOnly(untilTime: Instant): FlowTimeWindow { + return FlowTimeWindow(untilTime = untilTime) + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt index 8c207766fc..7520eae9ce 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt @@ -3,7 +3,6 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.strands.Strand import net.corda.core.CordaInternal -import net.corda.core.DeleteForDJVM import net.corda.core.contracts.StateRef import net.corda.core.crypto.SecureHash import net.corda.core.identity.AbstractParty @@ -66,12 +65,10 @@ import java.util.LinkedHashMap * relevant database transactions*. Only set this option to true if you know what you're doing. */ @Suppress("DEPRECATION", "DeprecatedCallableAddReplaceWith") -@DeleteForDJVM abstract class FlowLogic { /** This is where you should log things to. */ val logger: Logger get() = stateMachine.logger - @DeleteForDJVM companion object { /** * Return the outermost [FlowLogic] instance, or null if not in a flow. diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt index 1b08620e2a..7781c38b95 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt @@ -1,9 +1,7 @@ package net.corda.core.flows import net.corda.core.CordaInternal -import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializable /** @@ -13,13 +11,11 @@ import net.corda.core.serialization.CordaSerializable * the flow to run at the scheduled time. */ @DoNotImplement -@KeepForDJVM interface FlowLogicRefFactory { /** * Construct a FlowLogicRef. This is intended for cases where the calling code has the relevant class already * and can provide it directly. */ - @DeleteForDJVM fun create(flowClass: Class>, vararg args: Any?): FlowLogicRef /** @@ -34,14 +30,12 @@ interface FlowLogicRefFactory { * [SchedulableFlow] annotation. */ @CordaInternal - @DeleteForDJVM fun createForRPC(flowClass: Class>, vararg args: Any?): FlowLogicRef /** * Converts a [FlowLogicRef] object that was obtained from the calls above into a [FlowLogic], after doing some * validation to ensure it points to a legitimate flow class. */ - @DeleteForDJVM fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*> } @@ -65,5 +59,4 @@ class IllegalFlowLogicException(val type: String, msg: String) : // TODO: align this with the existing [FlowRef] in the bank-side API (probably replace some of the API classes) @CordaSerializable @DoNotImplement -@KeepForDJVM interface FlowLogicRef \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowSession.kt b/core/src/main/kotlin/net/corda/core/flows/FlowSession.kt index dd09a9d481..f40160b15a 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowSession.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowSession.kt @@ -2,7 +2,6 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.DoNotImplement -import net.corda.core.KeepForDJVM import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.utilities.UntrustworthyData @@ -214,5 +213,4 @@ abstract class FlowSession { * in future releases. */ @DoNotImplement -@KeepForDJVM interface Destination diff --git a/core/src/main/kotlin/net/corda/core/flows/LedgerRecoverFlow.kt b/core/src/main/kotlin/net/corda/core/flows/LedgerRecoverFlow.kt new file mode 100644 index 0000000000..3e16d044ad --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/flows/LedgerRecoverFlow.kt @@ -0,0 +1,84 @@ +package net.corda.core.flows + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.CordaInternal +import net.corda.core.identity.Party +import net.corda.core.serialization.CordaSerializable +import net.corda.core.utilities.ProgressTracker + +/** + * Ledger Recovery Flow (available in Enterprise only). + */ +@StartableByRPC +class LedgerRecoveryFlow( + private val parameters: LedgerRecoveryParameters, + override val progressTracker: ProgressTracker = ProgressTracker()) : FlowLogic() { + + // constructors added to aid Corda Node Shell flow command invocation + constructor(recoveryPeer: Party) : this(LedgerRecoveryParameters(setOf(recoveryPeer))) + constructor(recoveryPeers: Collection) : this(LedgerRecoveryParameters(recoveryPeers)) + constructor(useAllNetworkNodes: Boolean) : this(LedgerRecoveryParameters(emptySet(), useAllNetworkNodes = useAllNetworkNodes)) + constructor(recoveryPeer: Party, timeWindow: RecoveryTimeWindow) : + this(LedgerRecoveryParameters(setOf(recoveryPeer), timeWindow)) + constructor(recoveryPeer: Party, timeWindow: RecoveryTimeWindow, dryRun: Boolean) : + this(LedgerRecoveryParameters(setOf(recoveryPeer), timeWindow, dryRun = dryRun)) + constructor(recoveryPeer: Party, timeWindow: RecoveryTimeWindow, dryRun: Boolean, verboseLogging: Boolean) : + this(LedgerRecoveryParameters(setOf(recoveryPeer), timeWindow, dryRun = dryRun, verboseLogging = verboseLogging)) + constructor(recoveryPeer: Party, timeWindow: RecoveryTimeWindow, dryRun: Boolean, verboseLogging: Boolean, alsoFinalize: Boolean) : + this(LedgerRecoveryParameters(setOf(recoveryPeer), timeWindow, dryRun = dryRun, verboseLogging = verboseLogging, alsoFinalize = alsoFinalize)) + constructor(recoveryPeers: Collection, timeWindow: RecoveryTimeWindow) : + this(LedgerRecoveryParameters(recoveryPeers, timeWindow)) + constructor(recoveryPeers: Collection, timeWindow: RecoveryTimeWindow, dryRun: Boolean) : + this(LedgerRecoveryParameters(recoveryPeers, timeWindow, dryRun = dryRun)) + constructor(recoveryPeers: Collection, timeWindow: RecoveryTimeWindow, dryRun: Boolean, verboseLogging: Boolean) : + this(LedgerRecoveryParameters(recoveryPeers, timeWindow, dryRun = dryRun, verboseLogging = verboseLogging)) + constructor(recoveryPeers: Collection, timeWindow: RecoveryTimeWindow, dryRun: Boolean, verboseLogging: Boolean, alsoFinalize: Boolean) : + this(LedgerRecoveryParameters(recoveryPeers, timeWindow, dryRun = dryRun, verboseLogging = verboseLogging, alsoFinalize = alsoFinalize)) + constructor(useAllNetworkNodes: Boolean, timeWindow: RecoveryTimeWindow) : + this(LedgerRecoveryParameters(emptySet(), timeWindow, useAllNetworkNodes = useAllNetworkNodes)) + constructor(useAllNetworkNodes: Boolean, timeWindow: RecoveryTimeWindow, dryRun: Boolean) : + this(LedgerRecoveryParameters(emptySet(), timeWindow, useAllNetworkNodes = useAllNetworkNodes, dryRun = dryRun)) + constructor(useAllNetworkNodes: Boolean, timeWindow: RecoveryTimeWindow, dryRun: Boolean, verboseLogging: Boolean) : + this(LedgerRecoveryParameters(emptySet(), timeWindow, useAllNetworkNodes = useAllNetworkNodes, dryRun = dryRun, verboseLogging = verboseLogging)) + constructor(useAllNetworkNodes: Boolean, timeWindow: RecoveryTimeWindow, dryRun: Boolean, verboseLogging: Boolean, recoveryBatchSize: Int, alsoFinalize: Boolean) : + this(LedgerRecoveryParameters(emptySet(), timeWindow, useAllNetworkNodes = useAllNetworkNodes, dryRun = dryRun, verboseLogging = verboseLogging, recoveryBatchSize = recoveryBatchSize, alsoFinalize = alsoFinalize)) + constructor(useAllNetworkNodes: Boolean, timeWindow: RecoveryTimeWindow, dryRun: Boolean, verboseLogging: Boolean, recoveryBatchSize: Int) : + this(LedgerRecoveryParameters(emptySet(), timeWindow, useAllNetworkNodes = useAllNetworkNodes, dryRun = dryRun, verboseLogging = verboseLogging, recoveryBatchSize = recoveryBatchSize)) + constructor(recoveryPeers: Collection, timeWindow: RecoveryTimeWindow, useAllNetworkNodes: Boolean, dryRun: Boolean, useTimeWindowNarrowing: Boolean, verboseLogging: Boolean, recoveryBatchSize: Int) : + this(LedgerRecoveryParameters(recoveryPeers, timeWindow, useAllNetworkNodes, + dryRun = dryRun, useTimeWindowNarrowing = useTimeWindowNarrowing, verboseLogging = verboseLogging, recoveryBatchSize = recoveryBatchSize)) + + @CordaInternal + data class ExtraConstructorArgs(val parameters: LedgerRecoveryParameters) + @CordaInternal + fun getExtraConstructorArgs() = ExtraConstructorArgs(parameters) + + @Suspendable + @Throws(LedgerRecoveryException::class) + override fun call(): LedgerRecoveryResult { + throw NotImplementedError("Enterprise only feature") + } +} + +@CordaSerializable +class LedgerRecoveryException(message: String) : FlowException("Ledger recovery failed: $message") + +@CordaSerializable +data class LedgerRecoveryParameters( + val recoveryPeers: Collection, + val timeWindow: RecoveryTimeWindow? = null, + val useAllNetworkNodes: Boolean = false, + val dryRun: Boolean = false, + val useTimeWindowNarrowing: Boolean = true, + val verboseLogging: Boolean = false, + val recoveryBatchSize: Int = 1000, + val alsoFinalize: Boolean = false +) + +@CordaSerializable +data class LedgerRecoveryResult( + val totalRecoveredRecords: Long, + val totalRecoveredTransactions: Long, + val totalRecoveredInFlightTransactions: Long, + val totalErrors: Long +) diff --git a/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt b/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt index 096ea1280b..d109b53f48 100644 --- a/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt @@ -6,14 +6,22 @@ import net.corda.core.contracts.ContractState import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.TransactionResolutionException import net.corda.core.contracts.TransactionVerificationException +import net.corda.core.crypto.TransactionSignature +import net.corda.core.internal.FetchDataFlow +import net.corda.core.internal.PlatformVersionSwitches import net.corda.core.internal.ResolveTransactionsFlow +import net.corda.core.internal.ServiceHubCoreInternal import net.corda.core.internal.checkParameterHash import net.corda.core.internal.pushToLoggingContext +import net.corda.core.internal.telemetry.telemetryServiceInternal import net.corda.core.node.StatesToRecord import net.corda.core.transactions.SignedTransaction +import net.corda.core.utilities.Try +import net.corda.core.utilities.debug import net.corda.core.utilities.trace import net.corda.core.utilities.unwrap import java.security.SignatureException +import java.time.Duration /** * The [ReceiveTransactionFlow] should be called in response to the [SendTransactionFlow]. @@ -31,10 +39,21 @@ import java.security.SignatureException * @property otherSideSession session to the other side which is calling [SendTransactionFlow]. * @property checkSufficientSignatures if true checks all required signatures are present. See [SignedTransaction.verify]. * @property statesToRecord which transaction states should be recorded in the vault, if any. + * @property deferredAck if set then the caller of this flow is responsible for explicitly sending a FetchDataFlow.Request.End + * acknowledgement to indicate transaction resolution is complete. See usage within [FinalityFlow]. + * Not recommended for 3rd party use. */ -open class ReceiveTransactionFlow @JvmOverloads constructor(private val otherSideSession: FlowSession, - private val checkSufficientSignatures: Boolean = true, - private val statesToRecord: StatesToRecord = StatesToRecord.NONE) : FlowLogic() { +open class ReceiveTransactionFlow constructor(private val otherSideSession: FlowSession, + private val checkSufficientSignatures: Boolean = true, + private val statesToRecord: StatesToRecord = StatesToRecord.NONE, + private val handlePropagatedNotaryError: Boolean? = null) : FlowLogic() { + @JvmOverloads + constructor( + otherSideSession: FlowSession, + checkSufficientSignatures: Boolean = true, + statesToRecord: StatesToRecord = StatesToRecord.NONE + ) : this(otherSideSession, checkSufficientSignatures, statesToRecord, null) + @Suppress("KDocMissingDocumentation") @Suspendable @Throws(SignatureException::class, @@ -47,31 +66,97 @@ open class ReceiveTransactionFlow @JvmOverloads constructor(private val otherSid } else { logger.trace { "Receiving a transaction (but without checking the signatures) from ${otherSideSession.counterparty}" } } - val stx = otherSideSession.receive().unwrap { - it.pushToLoggingContext() + + val payload = otherSideSession.receive().unwrap { it } + return if (isReallyReceiveFinality(payload)) { + doReceiveFinality(payload) + } else { + val deferredAck = isDeferredAck(payload) + val stx = resolvePayload(payload) + stx.pushToLoggingContext() logger.info("Received transaction acknowledgement request from party ${otherSideSession.counterparty}.") - subFlow(ResolveTransactionsFlow(it, otherSideSession, statesToRecord)) - checkParameterHash(it.networkParametersHash) + subFlow(ResolveTransactionsFlow(stx, otherSideSession, statesToRecord, deferredAck)) + checkParameterHash(stx.networkParametersHash) logger.info("Transaction dependencies resolution completed.") - try { - it.verify(serviceHub, checkSufficientSignatures) - it - } catch (e: Exception) { - logger.warn("Transaction verification failed.") + verifyTx(stx, checkSufficientSignatures) + if (checkSufficientSignatures) { + // We should only send a transaction to the vault for processing if we did in fact fully verify it, and + // there are no missing signatures. We don't want partly signed stuff in the vault. + checkBeforeRecording(stx) + logger.info("Successfully received fully signed tx. Sending it to the vault for processing.") + serviceHub.recordTransactions(statesToRecord, setOf(stx)) + logger.info("Successfully recorded received transaction locally.") + if (deferredAck) otherSideSession.send(FetchDataFlow.Request.End) // Finish fetching data (deferredAck) + } + stx + } + } + + private fun verifyTx(stx: SignedTransaction, localCheckSufficientSignatures: Boolean) { + try { + stx.verify(serviceHub, localCheckSufficientSignatures) + } catch (e: Exception) { + logger.warn("Transaction verification failed.") + throw e + } + } + + private fun isDeferredAck(payload: Any): Boolean { + return payload is SignedTransactionWithDistributionList && checkSufficientSignatures && payload.isFinality + } + + @Suspendable + private fun doReceiveFinality(payload: Any): SignedTransaction { + val stx = resolvePayload(payload) + stx.pushToLoggingContext() + logger.info("Received transaction acknowledgement request from party ${otherSideSession.counterparty}.") + checkParameterHash(stx.networkParametersHash) + subFlow(ResolveTransactionsFlow(stx, otherSideSession, statesToRecord, true)) + logger.info("Transaction dependencies resolution completed.") + verifyTx(stx, false) + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#recordUnnotarisedTransaction", flowLogic = this) { + logger.debug { "Peer recording transaction without notary signature." } + (serviceHub as ServiceHubCoreInternal).recordUnnotarisedTransaction(stx) + } + otherSideSession.send(FetchDataFlow.Request.End) // Finish fetching data (deferredAck) + logger.info("Peer recorded transaction without notary signature. Waiting to receive notary signature.") + try { + val notarySignatures = otherSideSession.receive>>().unwrap { it.getOrThrow() } + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#finalizeTransactionWithExtraSignatures", flowLogic = this) { + logger.debug { "Peer received notarised signature." } + (serviceHub as ServiceHubCoreInternal).finalizeTransactionWithExtraSignatures(stx, notarySignatures, statesToRecord) + logger.info("Peer finalised transaction with notary signature.") + } + return stx + notarySignatures + } catch (e: NotaryException) { + logger.info("Peer received notary error.") + val overrideHandlePropagatedNotaryError = handlePropagatedNotaryError + ?: (serviceHub.cordappProvider.getAppContext().cordapp.targetPlatformVersion >= PlatformVersionSwitches.TWO_PHASE_FINALITY) + if (overrideHandlePropagatedNotaryError) { + (serviceHub as ServiceHubCoreInternal).removeUnnotarisedTransaction(stx.id) + sleep(Duration.ZERO) // force checkpoint to persist db update. throw e + } else { + otherSideSession.receive() // simulate unexpected flow end } } - if (checkSufficientSignatures) { - // We should only send a transaction to the vault for processing if we did in fact fully verify it, and - // there are no missing signatures. We don't want partly signed stuff in the vault. - checkBeforeRecording(stx) - logger.info("Successfully received fully signed tx. Sending it to the vault for processing.") - serviceHub.recordTransactions(statesToRecord, setOf(stx)) - logger.info("Successfully recorded received transaction locally.") - } return stx } + private fun isReallyReceiveFinality(payload: Any): Boolean { + return payload is SignedTransactionWithDistributionList && checkSufficientSignatures && payload.isFinality && NotarySigCheck.needsNotarySignature(payload.stx) + } + + open fun resolvePayload(payload: Any): SignedTransaction { + return if (payload is SignedTransactionWithDistributionList) { + if (checkSufficientSignatures) { + (serviceHub as ServiceHubCoreInternal).recordReceiverTransactionRecoveryMetadata(payload.stx.id, otherSideSession.counterparty.name, + TransactionMetadata(otherSideSession.counterparty.name, DistributionList.ReceiverDistributionList(payload.distributionList, statesToRecord))) + payload.stx + } else payload.stx + } else payload as SignedTransaction + } + /** * Hook to perform extra checks on the received transaction just before it's recorded. The transaction has already * been resolved and verified at this point. diff --git a/core/src/main/kotlin/net/corda/core/flows/RecoveryTypes.kt b/core/src/main/kotlin/net/corda/core/flows/RecoveryTypes.kt new file mode 100644 index 0000000000..b04de15187 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/flows/RecoveryTypes.kt @@ -0,0 +1,151 @@ +package net.corda.core.flows + +import net.corda.core.DoNotImplement +import net.corda.core.contracts.NamedByHash +import net.corda.core.crypto.SecureHash +import net.corda.core.identity.CordaX500Name +import net.corda.core.node.StatesToRecord +import net.corda.core.node.services.TransactionStatus +import net.corda.core.serialization.CordaSerializable +import net.corda.core.utilities.OpaqueBytes +import java.time.Instant +import java.time.temporal.ChronoUnit + +/** + * Transaction recovery type information. + */ + +@CordaSerializable +data class FlowTransactionInfo( + val stateMachineRunId: StateMachineRunId, + val txId: String, + val status: TransactionStatus, + val timestamp: Instant, + val metadata: TransactionMetadata? +) { + fun isInitiator(myCordaX500Name: CordaX500Name) = + this.metadata?.initiator == myCordaX500Name +} + +@CordaSerializable +data class TransactionMetadata( + val initiator: CordaX500Name, + val distributionList: DistributionList +) + +@CordaSerializable +@DoNotImplement +sealed class DistributionList { + + @CordaSerializable + data class SenderDistributionList( + val senderStatesToRecord: StatesToRecord, + val peersToStatesToRecord: Map + ) : DistributionList() + + @CordaSerializable + data class ReceiverDistributionList( + val opaqueData: ByteArray, // decipherable only by sender + val receiverStatesToRecord: StatesToRecord // inferred or actual + ) : DistributionList() +} + +@CordaSerializable +class DistributionRecords( + val senderRecords: List = emptyList(), + val receiverRecords: List = emptyList() +) { + val size = senderRecords.size + receiverRecords.size +} + +@CordaSerializable +@DoNotImplement +abstract class DistributionRecord : NamedByHash { + abstract val txId: SecureHash + abstract val peerPartyId: SecureHash + abstract val timestamp: Instant + abstract val timestampDiscriminator: Int +} + +@CordaSerializable +data class SenderDistributionRecord( + override val txId: SecureHash, + override val peerPartyId: SecureHash, + override val timestamp: Instant, + override val timestampDiscriminator: Int, + val senderStatesToRecord: StatesToRecord, + val receiverStatesToRecord: StatesToRecord +) : DistributionRecord() { + override val id: SecureHash + get() = this.txId +} + +@CordaSerializable +data class ReceiverDistributionRecord( + override val txId: SecureHash, + override val peerPartyId: SecureHash, + override val timestamp: Instant, + override val timestampDiscriminator: Int, + val encryptedDistributionList: OpaqueBytes, + val receiverStatesToRecord: StatesToRecord +) : DistributionRecord() { + override val id: SecureHash + get() = this.txId + + override fun toString(): String { + return "txId: $txId, peerPartyId: $peerPartyId, timestamp: $timestamp, timestampDiscriminator: $timestampDiscriminator, receiverStatesToRecord: $receiverStatesToRecord" + } +} + +@CordaSerializable +enum class DistributionRecordType { + SENDER, RECEIVER, ALL +} +@CordaSerializable +data class DistributionRecordKey( + val txnId: SecureHash, + val timestamp: Instant, + val timestampDiscriminator: Int +) + +@CordaSerializable +data class RecoveryTimeWindow(val fromTime: Instant, val untilTime: Instant = Instant.now()) { + + init { + if (untilTime < fromTime) { + throw IllegalArgumentException("$fromTime must be before $untilTime") + } + } + + companion object { + @JvmStatic + fun between(fromTime: Instant, untilTime: Instant): RecoveryTimeWindow { + return RecoveryTimeWindow(fromTime, untilTime) + } + + @JvmStatic + fun fromOnly(fromTime: Instant): RecoveryTimeWindow { + return RecoveryTimeWindow(fromTime = fromTime) + } + + @JvmStatic + fun untilOnly(untilTime: Instant): RecoveryTimeWindow { + return RecoveryTimeWindow(fromTime = Instant.EPOCH, untilTime = untilTime) + } + } +} + +@CordaSerializable +data class ComparableRecoveryTimeWindow( + val fromTime: Instant, + val fromTimestampDiscriminator: Int, + val untilTime: Instant, + val untilTimestampDiscriminator: Int +) { + companion object { + fun from(timeWindow: RecoveryTimeWindow) = + ComparableRecoveryTimeWindow( + timeWindow.fromTime.truncatedTo(ChronoUnit.SECONDS), 0, + timeWindow.untilTime.truncatedTo(ChronoUnit.SECONDS), Int.MAX_VALUE) + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/flows/SendTransactionFlow.kt b/core/src/main/kotlin/net/corda/core/flows/SendTransactionFlow.kt index 233a89236b..173107dd5f 100644 --- a/core/src/main/kotlin/net/corda/core/flows/SendTransactionFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/SendTransactionFlow.kt @@ -4,14 +4,25 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.NamedByHash import net.corda.core.contracts.StateAndRef import net.corda.core.crypto.SecureHash -import net.corda.core.internal.* +import net.corda.core.flows.DistributionList.SenderDistributionList +import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.FetchDataFlow +import net.corda.core.internal.NetworkParametersStorage +import net.corda.core.internal.PlatformVersionSwitches +import net.corda.core.internal.RetrieveAnyTransactionPayload +import net.corda.core.internal.ServiceHubCoreInternal +import net.corda.core.internal.readFully +import net.corda.core.node.ServicesForResolution +import net.corda.core.node.StatesToRecord import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.DeprecatedConstructorForDeserialization import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.transactions.SignedTransaction -import net.corda.core.utilities.unwrap import net.corda.core.utilities.trace +import net.corda.core.utilities.unwrap +import kotlin.collections.toSet /** * In the words of Matt working code is more important then pretty code. This class that contains code that may @@ -22,7 +33,12 @@ import net.corda.core.utilities.trace */ @CordaSerializable class MaybeSerializedSignedTransaction(override val id: SecureHash, val serialized: SerializedBytes?, - val nonSerialised: SignedTransaction?) : NamedByHash { + val nonSerialised: SignedTransaction?, + val inFlight: Boolean) : NamedByHash { + + @DeprecatedConstructorForDeserialization(version = 1) + constructor(id: SecureHash, serialized: SerializedBytes?, nonSerialised: SignedTransaction?) : this(id, serialized, nonSerialised, false) + init { check(serialized == null || nonSerialised == null) { "MaybeSerializedSignedTransaction: Serialized and non-serialized may not both be non-null." @@ -64,10 +80,46 @@ class MaybeSerializedSignedTransaction(override val id: SecureHash, val serializ * the right point in the conversation to receive the sent transaction and perform the resolution back-and-forth required * to check the dependencies and download any missing attachments. * - * @param otherSide the target party. - * @param stx the [SignedTransaction] being sent to the [otherSideSession]. + * @param stx the [SignedTransaction] being sent to the [otherSessions]. + * @param participantSessions the target parties which are participants to the transaction. + * @param observerSessions the target parties which are observers to the transaction. + * @param senderStatesToRecord the [StatesToRecord] relevancy information of the sender. + * @param recordMetaDataEvenIfNotFullySigned whether to store recovery metadata when a txn is not fully signed. */ -open class SendTransactionFlow(otherSide: FlowSession, stx: SignedTransaction) : DataVendingFlow(otherSide, stx) +open class SendTransactionFlow(val stx: SignedTransaction, + val participantSessions: Set, + val observerSessions: Set, + val senderStatesToRecord: StatesToRecord, + private val recordMetaDataEvenIfNotFullySigned: Boolean = false) + : DataVendingFlow(participantSessions + observerSessions, stx, + makeMetaData(stx, recordMetaDataEvenIfNotFullySigned, senderStatesToRecord, participantSessions, observerSessions)) { + + constructor(otherSide: FlowSession, stx: SignedTransaction) : this(stx, setOf(otherSide), emptySet(), StatesToRecord.NONE) + + // Note: DUMMY_PARTICIPANT_NAME to be substituted with actual "ourIdentity.name" in flow call() + companion object { + val DUMMY_PARTICIPANT_NAME = CordaX500Name("Transaction Participant", "London", "GB") + + fun makeMetaData(stx: SignedTransaction, recordMetaDataEvenIfNotFullySigned: Boolean, senderStatesToRecord: StatesToRecord, participantSessions: Set, observerSessions: Set): TransactionMetadata? { + return if (recordMetaDataEvenIfNotFullySigned || isFullySignedAndStoredLocally(stx)) + TransactionMetadata(DUMMY_PARTICIPANT_NAME, + SenderDistributionList(senderStatesToRecord, + (participantSessions.map { it.counterparty.name to StatesToRecord.ONLY_RELEVANT }).toMap() + + (observerSessions.map { it.counterparty.name to StatesToRecord.ALL_VISIBLE }).toMap())) + else null + } + + private fun isFullySigned(stx: SignedTransaction): Boolean { + val serviceHub = (currentTopLevel?.serviceHub as? ServicesForResolution) + return if (serviceHub != null) + stx.resolveTransactionWithSignatures(serviceHub).getMissingSigners().isEmpty() + else false + } + + private fun isFullySignedAndStoredLocally(stx: SignedTransaction) = isFullySigned(stx) + && (currentTopLevel?.serviceHub?.validatedTransactions?.getTransaction(stx.id) != null) + } +} /** * The [SendStateAndRefFlow] should be used to send a list of input [StateAndRef] to another peer that wishes to verify @@ -80,7 +132,13 @@ open class SendTransactionFlow(otherSide: FlowSession, stx: SignedTransaction) : */ open class SendStateAndRefFlow(otherSideSession: FlowSession, stateAndRefs: List>) : DataVendingFlow(otherSideSession, stateAndRefs) -open class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any) : FlowLogic() { +open class DataVendingFlow(val otherSessions: Set, val payload: Any, private val txnMetadata: TransactionMetadata? = null) : FlowLogic() { + constructor(otherSideSession: FlowSession, payload: Any, txnMetadata: TransactionMetadata? = null) : this(setOf(otherSideSession), payload, txnMetadata) + constructor(otherSideSession: FlowSession, payload: Any) : this(otherSideSession, payload, null) + + @Deprecated("Use otherSessions: Set", replaceWith = ReplaceWith("otherSessions.single()")) + val otherSideSession: FlowSession get() = otherSessions.single() + @Suspendable protected open fun sendPayloadAndReceiveDataRequest(otherSideSession: FlowSession, payload: Any) = otherSideSession.sendAndReceive(payload) @@ -89,6 +147,9 @@ open class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any) // User can override this method to perform custom request verification. } + protected open fun isFinality(): Boolean = false + + @Suppress("ComplexCondition", "ComplexMethod", "LongMethod", "TooGenericExceptionThrown") @Suspendable override fun call(): Void? { val networkMaxMessageSize = serviceHub.networkParameters.maxMessageSize @@ -107,119 +168,142 @@ open class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any) is NotarisationPayload -> TransactionAuthorisationFilter().addAuthorised(getInputTransactions(payload.signedTransaction)) is SignedTransaction -> TransactionAuthorisationFilter().addAuthorised(getInputTransactions(payload)) is RetrieveAnyTransactionPayload -> TransactionAuthorisationFilter(acceptAll = true) - is List<*> -> TransactionAuthorisationFilter().addAuthorised(payload.flatMap { stateAndRef -> - if (stateAndRef is StateAndRef<*>) { - getInputTransactions(serviceHub.validatedTransactions.getTransaction(stateAndRef.ref.txhash)!!) + stateAndRef.ref.txhash + is List<*> -> TransactionAuthorisationFilter().addAuthorised(payload.flatMap { someObject -> + if (someObject is StateAndRef<*>) { + getInputTransactions(serviceHub.validatedTransactions.getTransaction(someObject.ref.txhash)!!) + someObject.ref.txhash + } + else if (someObject is NamedByHash) { + setOf(someObject.id) } else { - throw Exception("Unknown payload type: ${stateAndRef!!::class.java} ?") + throw Exception("Unknown payload type: ${someObject!!::class.java} ?") } }.toSet()) else -> throw Exception("Unknown payload type: ${payload::class.java} ?") } - // This loop will receive [FetchDataFlow.Request] continuously until the `otherSideSession` has all the data they need - // to resolve the transaction, a [FetchDataFlow.EndRequest] will be sent from the `otherSideSession` to indicate end of - // data request. - var loopCount = 0 - while (true) { - val loopCnt = loopCount++ - logger.trace { "DataVendingFlow: Main While [$loopCnt]..." } - val dataRequest = sendPayloadAndReceiveDataRequest(otherSideSession, payload).unwrap { request -> - logger.trace { "sendPayloadAndReceiveDataRequest(): ${request.javaClass.name}" } - when (request) { - is FetchDataFlow.Request.Data -> { - // Security TODO: Check for abnormally large or malformed data requests - verifyDataRequest(request) - request - } - FetchDataFlow.Request.End -> { - logger.trace { "DataVendingFlow: END" } - return null - } - } - } + // store and share transaction recovery metadata if required + val useTwoPhaseFinality = serviceHub.myInfo.platformVersion >= PlatformVersionSwitches.TWO_PHASE_FINALITY + val toTwoPhaseFinalityNode = otherSessions.any { otherSideSession -> + serviceHub.networkMapCache.getNodeByLegalIdentity(otherSideSession.counterparty)?.platformVersion!! >= PlatformVersionSwitches.TWO_PHASE_FINALITY + } + // record transaction recovery metadata once + val payloadWithMetadata = + if (txnMetadata != null && toTwoPhaseFinalityNode && useTwoPhaseFinality && payload is SignedTransaction) { + val encryptedDistributionList = (serviceHub as ServiceHubCoreInternal).recordSenderTransactionRecoveryMetadata(payload.id, txnMetadata.copy(initiator = ourIdentity.name)) + SignedTransactionWithDistributionList(payload, encryptedDistributionList!!, isFinality()) + } else null - logger.trace { "Sending data (Type = ${dataRequest.dataType.name})" } - var totalByteCount = 0 - var firstItem = true - var batchFetchCountExceeded = false - var numSent = 0 - payload = when (dataRequest.dataType) { - FetchDataFlow.DataType.TRANSACTION -> dataRequest.hashes.map { txId -> - logger.trace { "Sending: TRANSACTION (dataRequest.hashes.size=${dataRequest.hashes.size})" } - if (!authorisedTransactions.isAuthorised(txId)) { - throw FetchDataFlow.IllegalTransactionRequest(txId) + otherSessions.forEachIndexed { idx, otherSideSession -> + if (payloadWithMetadata != null) + payload = payloadWithMetadata + // This loop will receive [FetchDataFlow.Request] continuously until the `otherSideSession` has all the data they need + // to resolve the transaction, a [FetchDataFlow.EndRequest] will be sent from the `otherSideSession` to indicate end of + // data request. + var loopCount = 0 + while (true) { + val loopCnt = loopCount++ + logger.trace { "DataVendingFlow: Main While [$loopCnt]..." } + val dataRequest = sendPayloadAndReceiveDataRequest(otherSideSession, payload).unwrap { request -> + logger.trace { "sendPayloadAndReceiveDataRequest(): ${request.javaClass.name}" } + when (request) { + is FetchDataFlow.Request.Data -> { + // Security TODO: Check for abnormally large or malformed data requests + verifyDataRequest(request) + request + } + FetchDataFlow.Request.End -> { + logger.trace { "DataVendingFlow: END" } + return@forEachIndexed + } } - val tx = serviceHub.validatedTransactions.getTransaction(txId) - ?: throw FetchDataFlow.HashNotFound(txId) - authorisedTransactions.removeAuthorised(tx.id) - authorisedTransactions.addAuthorised(getInputTransactions(tx)) - totalByteCount += tx.txBits.size - numSent++ - tx } - // Loop on all items returned using dataRequest.hashes.map: - FetchDataFlow.DataType.BATCH_TRANSACTION -> dataRequest.hashes.map { txId -> - if (!authorisedTransactions.isAuthorised(txId)) { - throw FetchDataFlow.IllegalTransactionRequest(txId) - } - // Maybe we should not just throw here as it's not recoverable on the client side. Might be better to send a reason code or - // remove the restriction on sending once. - logger.trace { "Transaction authorised OK: '$txId'" } - var serialized: SerializedBytes? = null - if (!batchFetchCountExceeded) { - // Only fetch and serialize if we have not already exceeded the maximum byte count. Once we have, no more fetching - // is required, just reject all additional items. + + logger.trace { "Sending data (Type = ${dataRequest.dataType.name})" } + var totalByteCount = 0 + var firstItem = true + var batchFetchCountExceeded = false + var numSent = 0 + payload = when (dataRequest.dataType) { + FetchDataFlow.DataType.TRANSACTION -> dataRequest.hashes.map { txId -> + logger.trace { "Sending: TRANSACTION (dataRequest.hashes.size=${dataRequest.hashes.size})" } + if (!authorisedTransactions.isAuthorised(txId)) { + throw FetchDataFlow.IllegalTransactionRequest(txId) + } val tx = serviceHub.validatedTransactions.getTransaction(txId) ?: throw FetchDataFlow.HashNotFound(txId) - logger.trace { "Transaction get OK: '$txId'" } - serialized = tx.serialize() - - val itemByteCount = serialized.size - logger.trace { "Batch-Send '$txId': first = $firstItem, Total bytes = $totalByteCount, Item byte count = $itemByteCount, Maximum = $maxPayloadSize" } - if (firstItem || (totalByteCount + itemByteCount) < maxPayloadSize) { - totalByteCount += itemByteCount - numSent++ - // Always include at least one item else if the max is set too low nothing will ever get returned. - // Splitting items will be a separate Jira if need be + if (idx == otherSessions.size - 1) authorisedTransactions.removeAuthorised(tx.id) - authorisedTransactions.addAuthorised(getInputTransactions(tx)) - logger.trace { "Adding item to return set: '$txId'" } - } else { - logger.trace { "Fetch block size EXCEEDED at '$txId'." } - batchFetchCountExceeded = true - } - } // end - - if (batchFetchCountExceeded) { - logger.trace { "Excluding '$txId' from return set due to exceeded count." } + authorisedTransactions.addAuthorised(getInputTransactions(tx)) + totalByteCount += tx.txBits.size + numSent++ + tx } + FetchDataFlow.DataType.TRANSACTION_RECOVERY -> throw NotImplementedError("Enterprise only feature") + // Loop on all items returned using dataRequest.hashes.map: + FetchDataFlow.DataType.BATCH_TRANSACTION -> dataRequest.hashes.map { txId -> + if (!authorisedTransactions.isAuthorised(txId)) { + throw FetchDataFlow.IllegalTransactionRequest(txId) + } + // Maybe we should not just throw here as it's not recoverable on the client side. Might be better to send a reason code or + // remove the restriction on sending once. + logger.trace { "Transaction authorised OK: '$txId'" } + var serialized: SerializedBytes? = null + if (!batchFetchCountExceeded) { + // Only fetch and serialize if we have not already exceeded the maximum byte count. Once we have, no more fetching + // is required, just reject all additional items. + val tx = serviceHub.validatedTransactions.getTransaction(txId) + ?: throw FetchDataFlow.HashNotFound(txId) + logger.trace { "Transaction get OK: '$txId'" } + serialized = tx.serialize() - // Send null if limit is exceeded - val maybeserialized = MaybeSerializedSignedTransaction(txId, if (batchFetchCountExceeded) { - null - } else { - serialized - }, null) - firstItem = false - maybeserialized - } // Batch response loop end - FetchDataFlow.DataType.ATTACHMENT -> dataRequest.hashes.map { - logger.trace { "Sending: Attachments for '$it'" } - serviceHub.attachments.openAttachment(it)?.open()?.readFully() - ?: throw FetchDataFlow.HashNotFound(it) - } - FetchDataFlow.DataType.PARAMETERS -> dataRequest.hashes.map { - logger.trace { "Sending: Parameters for '$it'" } - (serviceHub.networkParametersService as NetworkParametersStorage).lookupSigned(it) - ?: throw FetchDataFlow.MissingNetworkParameters(it) - } - FetchDataFlow.DataType.UNKNOWN -> dataRequest.hashes.map { - logger.warn("Message from from a future version of Corda with UNKNOWN enum value for FetchDataFlow.DataType: ID='$it'") + val itemByteCount = serialized.size + logger.trace { "Batch-Send '$txId': first = $firstItem, Total bytes = $totalByteCount, Item byte count = $itemByteCount, Maximum = $maxPayloadSize" } + if (firstItem || (totalByteCount + itemByteCount) < maxPayloadSize) { + totalByteCount += itemByteCount + numSent++ + // Always include at least one item else if the max is set too low nothing will ever get returned. + // Splitting items will be a separate Jira if need be + if (idx == otherSessions.size - 1) + authorisedTransactions.removeAuthorised(tx.id) + authorisedTransactions.addAuthorised(getInputTransactions(tx)) + logger.trace { "Adding item to return set: '$txId'" } + } else { + logger.trace { "Fetch block size EXCEEDED at '$txId'." } + batchFetchCountExceeded = true + } + } // end + + if (batchFetchCountExceeded) { + logger.trace { "Excluding '$txId' from return set due to exceeded count." } + } + + // Send null if limit is exceeded + val maybeserialized = MaybeSerializedSignedTransaction(txId, if (batchFetchCountExceeded) { + null + } else { + serialized + }, null) + firstItem = false + maybeserialized + } // Batch response loop end + FetchDataFlow.DataType.ATTACHMENT -> dataRequest.hashes.map { + logger.trace { "Sending: Attachments for '$it'" } + serviceHub.attachments.openAttachment(it)?.open()?.readFully() + ?: throw FetchDataFlow.HashNotFound(it) + } + FetchDataFlow.DataType.PARAMETERS -> dataRequest.hashes.map { + logger.trace { "Sending: Parameters for '$it'" } + (serviceHub.networkParametersService as NetworkParametersStorage).lookupSigned(it) + ?: throw FetchDataFlow.MissingNetworkParameters(it) + } + FetchDataFlow.DataType.UNKNOWN -> dataRequest.hashes.map { + logger.warn("Message from from a future version of Corda with UNKNOWN enum value for FetchDataFlow.DataType: ID='$it'") + } } + logger.trace { "Block total size = $totalByteCount: Num Items = ($numSent of ${dataRequest.hashes.size} total)" } } - logger.trace { "Block total size = $totalByteCount: Num Items = ($numSent of ${dataRequest.hashes.size} total)" } } + return null } @Suspendable @@ -240,3 +324,10 @@ open class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any) } } } + +@CordaSerializable +data class SignedTransactionWithDistributionList( + val stx: SignedTransaction, + val distributionList: ByteArray, + val isFinality: Boolean +) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/flows/StateMachineRunId.kt b/core/src/main/kotlin/net/corda/core/flows/StateMachineRunId.kt index 47de376947..30f0d9599a 100644 --- a/core/src/main/kotlin/net/corda/core/flows/StateMachineRunId.kt +++ b/core/src/main/kotlin/net/corda/core/flows/StateMachineRunId.kt @@ -1,6 +1,5 @@ package net.corda.core.flows -import net.corda.core.DeleteForDJVM import net.corda.core.serialization.CordaSerializable import java.util.* @@ -8,7 +7,6 @@ import java.util.* * A unique identifier for a single state machine run, valid across node restarts. Note that a single run always * has at least one flow, but that flow may also invoke sub-flows: they all share the same run id. */ -@DeleteForDJVM @CordaSerializable data class StateMachineRunId(val uuid: UUID) { companion object { diff --git a/core/src/main/kotlin/net/corda/core/identity/AnonymousParty.kt b/core/src/main/kotlin/net/corda/core/identity/AnonymousParty.kt index 8797f9e5a6..e61fe4ce81 100644 --- a/core/src/main/kotlin/net/corda/core/identity/AnonymousParty.kt +++ b/core/src/main/kotlin/net/corda/core/identity/AnonymousParty.kt @@ -1,6 +1,5 @@ package net.corda.core.identity -import net.corda.core.KeepForDJVM import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.toStringShort import net.corda.core.flows.Destination @@ -17,7 +16,6 @@ import java.security.PublicKey * Anonymous parties can be used to communicate using the [FlowLogic.initiateFlow] method. Message routing is simply routing to the well-known * [Party] the anonymous party belongs to. This mechanism assumes the party initiating the communication knows who the anonymous party is. */ -@KeepForDJVM class AnonymousParty(owningKey: PublicKey) : Destination, AbstractParty(owningKey) { override fun nameOrNull(): CordaX500Name? = null override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes) 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 f61ba04b58..a2cc82d1e3 100644 --- a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt +++ b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt @@ -1,7 +1,6 @@ package net.corda.core.identity import net.corda.core.CordaInternal -import net.corda.core.KeepForDJVM import net.corda.core.internal.LegalNameValidator import net.corda.core.internal.toAttributesMap import net.corda.core.internal.toX500Name @@ -30,7 +29,6 @@ import javax.security.auth.x500.X500Principal * attribute type. */ @CordaSerializable -@KeepForDJVM data class CordaX500Name(val commonName: String?, val organisationUnit: String?, val organisation: String, diff --git a/core/src/main/kotlin/net/corda/core/identity/Party.kt b/core/src/main/kotlin/net/corda/core/identity/Party.kt index a01caadf8b..75aceaa233 100644 --- a/core/src/main/kotlin/net/corda/core/identity/Party.kt +++ b/core/src/main/kotlin/net/corda/core/identity/Party.kt @@ -1,6 +1,5 @@ package net.corda.core.identity -import net.corda.core.KeepForDJVM import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.Crypto @@ -35,7 +34,6 @@ import java.security.cert.X509Certificate * * @see CompositeKey */ -@KeepForDJVM class Party(val name: CordaX500Name, owningKey: PublicKey) : Destination, AbstractParty(owningKey) { constructor(certificate: X509Certificate) : this(CordaX500Name.build(certificate.subjectX500Principal), Crypto.toSupportedPublicKey(certificate.publicKey)) diff --git a/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt b/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt index d1adcc73c2..4fdea7cda2 100644 --- a/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt +++ b/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt @@ -1,6 +1,5 @@ package net.corda.core.identity -import net.corda.core.KeepForDJVM import net.corda.core.internal.CertRole import net.corda.core.internal.uncheckedCast import net.corda.core.internal.validate @@ -18,7 +17,6 @@ import java.security.cert.X509Certificate * not part of the identifier themselves. */ @CordaSerializable -@KeepForDJVM class PartyAndCertificate(val certPath: CertPath) { @Transient val certificate: X509Certificate diff --git a/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt b/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt index 198416260f..d5d25bc6cc 100644 --- a/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt +++ b/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt @@ -1,9 +1,5 @@ -@file:KeepForDJVM - package net.corda.core.internal -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.contracts.Attachment import net.corda.core.contracts.ContractAttachment import net.corda.core.crypto.SecureHash @@ -23,7 +19,7 @@ const val P2P_UPLOADER = "p2p" const val TESTDSL_UPLOADER = "TestDSL" const val UNKNOWN_UPLOADER = "unknown" -// We whitelist sources of transaction JARs for now as a temporary state until the DJVM and other security sandboxes +// We whitelist sources of transaction JARs for now as a temporary state until security sandboxes // have been integrated, at which point we'll be able to run untrusted code downloaded over the network and this mechanism // can be removed. Because we ARE downloading attachments over the P2P network in anticipation of this upgrade, we // track the source of each attachment in our store. TestDSL is used by LedgerDSLInterpreter when custom attachments @@ -38,7 +34,6 @@ fun Attachment.isUploaderTrusted(): Boolean = when (this) { else -> false } -@KeepForDJVM abstract class AbstractAttachment(dataLoader: () -> ByteArray, val uploader: String?) : Attachment { companion object { /** @@ -46,7 +41,6 @@ abstract class AbstractAttachment(dataLoader: () -> ByteArray, val uploader: Str * * TODO - this code together with the rest of the Attachment handling (including [FetchedAttachment]) needs some refactoring as it is really hard to follow. */ - @DeleteForDJVM fun SerializeAsTokenContext.attachmentDataLoader(id: SecureHash): () -> ByteArray { return { val a = serviceHub.attachments.openAttachment(id) ?: throw MissingAttachmentsException(listOf(id)) diff --git a/core/src/main/kotlin/net/corda/core/internal/ClassGraphUtils.kt b/core/src/main/kotlin/net/corda/core/internal/ClassGraphUtils.kt index fb420e373a..94f51595d8 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ClassGraphUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ClassGraphUtils.kt @@ -1,10 +1,7 @@ -@file:DeleteForDJVM - package net.corda.core.internal import io.github.classgraph.ClassGraph import io.github.classgraph.ScanResult -import net.corda.core.DeleteForDJVM import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock diff --git a/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt b/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt index 5ead87ca59..e7834fd567 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt @@ -2,7 +2,6 @@ package net.corda.core.internal import io.github.classgraph.ClassGraph import io.github.classgraph.ClassInfo -import net.corda.core.StubOutForDJVM import net.corda.core.serialization.internal.AttachmentURLStreamHandlerFactory.attachmentScheme /** @@ -19,7 +18,6 @@ import net.corda.core.serialization.internal.AttachmentURLStreamHandlerFactory.a * - be non-abstract * - either be a Kotlin object or have a constructor with no parameters (or only optional ones) */ -@StubOutForDJVM fun createInstancesOfClassesImplementing(classloader: ClassLoader, clazz: Class, classVersionRange: IntRange? = null): Set { return getNamesOfClassesImplementing(classloader, clazz, classVersionRange) @@ -36,7 +34,6 @@ fun createInstancesOfClassesImplementing(classloader: ClassLoader, claz * @return names of the identified classes. * @throws UnsupportedClassVersionError if the class version is not within range. */ -@StubOutForDJVM fun getNamesOfClassesImplementing(classloader: ClassLoader, clazz: Class, classVersionRange: IntRange? = null): Set { return ClassGraph().overrideClassLoaders(classloader) diff --git a/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt b/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt index 149fa2ea84..d8c2f599bb 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ConstraintsUtils.kt @@ -17,7 +17,6 @@ typealias Version = Int * Attention: this value affects consensus, so it requires a minimum platform version bump in order to be changed. */ const val MAX_NUMBER_OF_KEYS_IN_SIGNATURE_CONSTRAINT = 20 -private const val DJVM_SANDBOX_PREFIX = "sandbox." private val log = loggerFor() @@ -29,15 +28,15 @@ val Attachment.contractVersion: Version get() = if (this is ContractAttachment) * one and it inherits from [Contract]. */ val ContractState.requiredContractClassName: String? get() { - val annotation = javaClass.getAnnotation(BelongsToContract::class.java) - if (annotation != null) { - return annotation.value.java.typeName.removePrefix(DJVM_SANDBOX_PREFIX) - } - val enclosingClass = javaClass.enclosingClass ?: return null - return if (Contract::class.java.isAssignableFrom(enclosingClass)) { - enclosingClass.typeName.removePrefix(DJVM_SANDBOX_PREFIX) - } else { - null + return ContractStateClassCache.contractClassName(this.javaClass) ?: let { + val annotation = javaClass.getAnnotation(BelongsToContract::class.java) + val className = if (annotation != null) { + annotation.value.java.typeName + } else { + val enclosingClass = javaClass.enclosingClass ?: return null + if (Contract::class.java.isAssignableFrom(enclosingClass)) enclosingClass.typeName else null + } + ContractStateClassCache.cacheContractClassName(this.javaClass, className) } } diff --git a/core/src/main/kotlin/net/corda/core/internal/ContractStateClassCache.kt b/core/src/main/kotlin/net/corda/core/internal/ContractStateClassCache.kt new file mode 100644 index 0000000000..73fb3a847d --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/internal/ContractStateClassCache.kt @@ -0,0 +1,18 @@ +package net.corda.core.internal + +import com.google.common.collect.MapMaker +import net.corda.core.contracts.ContractState + +object ContractStateClassCache { + private val classToString = MapMaker().weakKeys().makeMap, String>() + + fun contractClassName(key: Class): String? { + return classToString[key] + } + + fun cacheContractClassName(key: Class, contractClassName: String?): String? { + if (contractClassName == null) return null + classToString.putIfAbsent(key, contractClassName) + return contractClassName + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/ContractUpgradeUtils.kt b/core/src/main/kotlin/net/corda/core/internal/ContractUpgradeUtils.kt index 31c09c3e97..ec0589efdc 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ContractUpgradeUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ContractUpgradeUtils.kt @@ -1,12 +1,10 @@ package net.corda.core.internal -import net.corda.core.DeleteForDJVM import net.corda.core.contracts.* import net.corda.core.node.ServicesForResolution import net.corda.core.node.services.AttachmentId import net.corda.core.transactions.ContractUpgradeWireTransaction -@DeleteForDJVM object ContractUpgradeUtils { fun assembleUpgradeTx( stateAndRef: StateAndRef, diff --git a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt index 72608450cb..8461583901 100644 --- a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt @@ -1,7 +1,6 @@ @file:Suppress("TooManyFunctions") package net.corda.core.internal -import net.corda.core.DeleteForDJVM import net.corda.core.contracts.Attachment import net.corda.core.contracts.ContractClassName import net.corda.core.cordapp.CordappProvider @@ -30,7 +29,7 @@ import java.util.jar.JarInputStream // When incrementing platformVersion make sure to update PLATFORM_VERSION in constants.properties as well. -const val PLATFORM_VERSION = 12 +const val PLATFORM_VERSION = 13 fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) { checkMinimumPlatformVersion(networkParameters.minimumPlatformVersion, requiredMinPlatformVersion, feature) @@ -65,13 +64,11 @@ enum class JavaVersion(val versionString: String) { } /** Provide access to internal method for AttachmentClassLoaderTests. */ -@DeleteForDJVM fun TransactionBuilder.toWireTransaction(services: ServicesForResolution, serializationContext: SerializationContext): WireTransaction { return toWireTransactionWithContext(services, serializationContext) } /** Provide access to internal method for AttachmentClassLoaderTests. */ -@DeleteForDJVM fun TransactionBuilder.toLedgerTransaction(services: ServicesForResolution, serializationContext: SerializationContext): LedgerTransaction { return toLedgerTransactionWithContext(services, serializationContext) } diff --git a/core/src/main/kotlin/net/corda/core/internal/CordappFixupInternal.kt b/core/src/main/kotlin/net/corda/core/internal/CordappFixupInternal.kt index adbc6f5f85..e1ac4e22c6 100644 --- a/core/src/main/kotlin/net/corda/core/internal/CordappFixupInternal.kt +++ b/core/src/main/kotlin/net/corda/core/internal/CordappFixupInternal.kt @@ -1,9 +1,7 @@ package net.corda.core.internal -import net.corda.core.DeleteForDJVM import net.corda.core.node.services.AttachmentId -@DeleteForDJVM interface CordappFixupInternal { fun fixupAttachmentIds(attachmentIds: Collection): Set } diff --git a/core/src/main/kotlin/net/corda/core/internal/DjvmUtils.kt b/core/src/main/kotlin/net/corda/core/internal/DjvmUtils.kt deleted file mode 100644 index 755a7959e1..0000000000 --- a/core/src/main/kotlin/net/corda/core/internal/DjvmUtils.kt +++ /dev/null @@ -1,20 +0,0 @@ -@file:KeepForDJVM - -package net.corda.core.internal - -import net.corda.core.KeepForDJVM -import net.corda.core.contracts.Attachment -import net.corda.core.contracts.StateRef -import net.corda.core.contracts.TransactionState -import net.corda.core.crypto.SecureHash -import net.corda.core.node.NetworkParameters -import net.corda.core.transactions.LedgerTransaction -import net.corda.core.transactions.WireTransaction - -fun WireTransaction.toLtxDjvmInternal( - resolveAttachment: (SecureHash) -> Attachment?, - resolveStateRef: (StateRef) -> TransactionState<*>?, - resolveParameters: (SecureHash?) -> NetworkParameters? -): LedgerTransaction { - return toLtxDjvmInternalBridge(resolveAttachment, resolveStateRef, resolveParameters) -} diff --git a/core/src/main/kotlin/net/corda/core/internal/Emoji.kt b/core/src/main/kotlin/net/corda/core/internal/Emoji.kt index c911275126..27b780ece0 100644 --- a/core/src/main/kotlin/net/corda/core/internal/Emoji.kt +++ b/core/src/main/kotlin/net/corda/core/internal/Emoji.kt @@ -1,11 +1,8 @@ package net.corda.core.internal -import net.corda.core.DeleteForDJVM - /** * A simple wrapper class that contains icons and support for printing them only when we're connected to a terminal. */ -@DeleteForDJVM object Emoji { // Unfortunately only Apple has a terminal that can do colour emoji AND an emoji font installed by default. // However the JediTerm java terminal emulator can also do emoji on OS X when using the JetBrains JRE. diff --git a/core/src/main/kotlin/net/corda/core/internal/FetchDataFlow.kt b/core/src/main/kotlin/net/corda/core/internal/FetchDataFlow.kt index 8d69db366f..441a0e669a 100644 --- a/core/src/main/kotlin/net/corda/core/internal/FetchDataFlow.kt +++ b/core/src/main/kotlin/net/corda/core/internal/FetchDataFlow.kt @@ -12,6 +12,7 @@ import net.corda.core.flows.MaybeSerializedSignedTransaction import net.corda.core.internal.FetchDataFlow.DownloadedVsRequestedDataMismatch import net.corda.core.internal.FetchDataFlow.HashNotFound import net.corda.core.node.NetworkParameters +import net.corda.core.node.services.TransactionStatus import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializationTransformEnumDefault import net.corda.core.serialization.CordaSerializationTransformEnumDefaults @@ -22,10 +23,11 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.debug -import net.corda.core.utilities.unwrap import net.corda.core.utilities.trace +import net.corda.core.utilities.unwrap import java.nio.file.FileAlreadyExistsException -import java.util.* +import java.util.ArrayList +import java.util.LinkedHashSet /** * An abstract flow for fetching typed data from a remote peer. @@ -78,11 +80,12 @@ sealed class FetchDataFlow( // against not having unknown by using the platform version as a guard. @CordaSerializationTransformEnumDefaults( CordaSerializationTransformEnumDefault("BATCH_TRANSACTION", "TRANSACTION"), - CordaSerializationTransformEnumDefault("UNKNOWN", "TRANSACTION") + CordaSerializationTransformEnumDefault("UNKNOWN", "TRANSACTION"), + CordaSerializationTransformEnumDefault("TRANSACTION_RECOVERY", "TRANSACTION") ) @CordaSerializable enum class DataType { - TRANSACTION, ATTACHMENT, PARAMETERS, BATCH_TRANSACTION, UNKNOWN + TRANSACTION, ATTACHMENT, PARAMETERS, BATCH_TRANSACTION, UNKNOWN, TRANSACTION_RECOVERY } @Suspendable @@ -267,21 +270,23 @@ class FetchAttachmentsFlow(requests: Set, * Authorisation is accorded only on valid ancestors of the root transaction. * Note that returned transactions are not inserted into the database, because it's up to the caller to actually verify the transactions are valid. */ -class FetchTransactionsFlow(requests: Set, otherSide: FlowSession) : - FetchDataFlow(requests, otherSide, DataType.TRANSACTION) { +class FetchTransactionsFlow @JvmOverloads constructor(requests: Set, otherSide: FlowSession, dataType: DataType = DataType.TRANSACTION) : + FetchDataFlow(requests, otherSide, dataType) { override fun load(txid: SecureHash): SignedTransaction? = serviceHub.validatedTransactions.getTransaction(txid) } -class FetchBatchTransactionsFlow(requests: Set, otherSide: FlowSession) : - FetchDataFlow(requests, otherSide, DataType.BATCH_TRANSACTION) { +class FetchBatchTransactionsFlow(requests: Set, otherSide: FlowSession, private val recoveryMode: Boolean = false) : + FetchDataFlow(requests, otherSide, + if (recoveryMode) DataType.TRANSACTION_RECOVERY else DataType.BATCH_TRANSACTION) { override fun load(txid: SecureHash): MaybeSerializedSignedTransaction? { - val tran = serviceHub.validatedTransactions.getTransaction(txid) - return if (tran == null) { + val tranAndStatus = serviceHub.validatedTransactions.getTransactionWithStatus(txid) + @Suppress("ComplexCondition") + return if (tranAndStatus == null || tranAndStatus.status == TransactionStatus.UNVERIFIED || (!recoveryMode && tranAndStatus.status == TransactionStatus.IN_FLIGHT)) { null } else { - MaybeSerializedSignedTransaction(txid, null, tran) + MaybeSerializedSignedTransaction(txid, null, tranAndStatus.stx, tranAndStatus.status == TransactionStatus.IN_FLIGHT) } } } diff --git a/core/src/main/kotlin/net/corda/core/internal/FlowIORequest.kt b/core/src/main/kotlin/net/corda/core/internal/FlowIORequest.kt index 7ced0d46a0..aa8285770e 100644 --- a/core/src/main/kotlin/net/corda/core/internal/FlowIORequest.kt +++ b/core/src/main/kotlin/net/corda/core/internal/FlowIORequest.kt @@ -1,6 +1,5 @@ package net.corda.core.internal -import net.corda.core.DeleteForDJVM import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowInfo import net.corda.core.flows.FlowSession @@ -13,7 +12,6 @@ import java.time.Instant /** * A [FlowIORequest] represents an IO request of a flow when it suspends. It is persisted in checkpoints. */ -@DeleteForDJVM sealed class FlowIORequest { /** * Send messages to sessions. diff --git a/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt b/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt index bc5f0b0157..9ead75ce33 100644 --- a/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt +++ b/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt @@ -1,7 +1,6 @@ package net.corda.core.internal import co.paralleluniverse.fibers.Suspendable -import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement import net.corda.core.concurrent.CordaFuture import net.corda.core.context.InvocationContext @@ -12,7 +11,6 @@ import net.corda.core.internal.telemetry.SerializedTelemetry import net.corda.core.serialization.SerializedBytes import org.slf4j.Logger -@DeleteForDJVM @DoNotImplement interface FlowStateMachineHandle { val logic: FlowLogic? @@ -22,7 +20,6 @@ interface FlowStateMachineHandle { } /** This is an internal interface that is implemented by code in the node module. You should look at [FlowLogic]. */ -@DeleteForDJVM @DoNotImplement interface FlowStateMachine : FlowStateMachineHandle { @Suspendable 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 fb22ca085b..e3df734e91 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -1,10 +1,6 @@ @file:JvmName("InternalUtils") -@file:KeepForDJVM package net.corda.core.internal -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM -import net.corda.core.StubOutForDJVM import net.corda.core.crypto.Crypto import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.SecureHash @@ -131,7 +127,6 @@ fun List.noneOrSingle(): T? { } /** Returns a random element in the list, or `null` if empty */ -@DeleteForDJVM fun List.randomOrNull(): T? { return when (size) { 0 -> null @@ -147,7 +142,7 @@ fun List.indexOfOrThrow(item: T): Int { return i } -@DeleteForDJVM fun InputStream.copyTo(target: Path, vararg options: CopyOption): Long = Files.copy(this, target, *options) +fun InputStream.copyTo(target: Path, vararg options: CopyOption): Long = Files.copy(this, target, *options) /** Same as [InputStream.readBytes] but also closes the stream. */ fun InputStream.readFully(): ByteArray = use { it.readBytes() } @@ -185,7 +180,6 @@ fun Iterable.sum(): BigDecimal = fold(BigDecimal.ZERO) { a, b -> a + * Returns an Observable that buffers events until subscribed. * @see UnicastSubject */ -@DeleteForDJVM fun Observable.bufferUntilSubscribed(): Observable { val subject = UnicastSubject.create() val subscription = subscribe(subject) @@ -193,7 +187,6 @@ fun Observable.bufferUntilSubscribed(): Observable { } /** Copy an [Observer] to multiple other [Observer]s. */ -@DeleteForDJVM fun Observer.tee(vararg teeTo: Observer): Observer { val subject = PublishSubject.create() // use unsafe subscribe, so that the teed subscribers will not get wrapped with SafeSubscribers, @@ -205,7 +198,6 @@ fun Observer.tee(vararg teeTo: Observer): Observer { } /** Executes the given code block and returns a [Duration] of how long it took to execute in nanosecond precision. */ -@DeleteForDJVM inline fun elapsedTime(block: () -> Unit): Duration { val start = System.nanoTime() block() @@ -218,7 +210,6 @@ fun Logger.logElapsedTime(label: String, body: () -> T): T = logElapsedTime( // TODO: Add inline back when a new Kotlin version is released and check if the java.lang.VerifyError // returns in the IRSSimulationTest. If not, commit the inline back. -@DeleteForDJVM fun logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T { // Use nanoTime as it's monotonic. val now = System.nanoTime() @@ -246,7 +237,6 @@ fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash { return InputStreamAndHash(bytes.inputStream(), bytes.sha256()) } -@KeepForDJVM data class InputStreamAndHash(val inputStream: InputStream, val sha256: SecureHash.SHA256) { companion object { /** @@ -254,7 +244,6 @@ data class InputStreamAndHash(val inputStream: InputStream, val sha256: SecureHa * called "z" that contains the given content byte repeated the given number of times. * Note that a slightly bigger than numOfExpectedBytes size is expected. */ - @DeleteForDJVM fun createInMemoryTestZip(numOfExpectedBytes: Int, content: Byte, entryName: String = "z"): InputStreamAndHash { require(numOfExpectedBytes > 0){"Expected bytes must be greater than zero"} require(numOfExpectedBytes > 0) @@ -312,29 +301,24 @@ fun Stream.toSet(): Set = collect(toCollection { LinkedHashSet() }) fun Class.castIfPossible(obj: Any): T? = if (isInstance(obj)) cast(obj) else null /** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [Class]. */ -@DeleteForDJVM fun Class<*>.staticField(name: String): DeclaredField = DeclaredField(this, name, null) /** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [KClass]. */ -@DeleteForDJVM fun KClass<*>.staticField(name: String): DeclaredField = DeclaredField(java, name, null) /** Returns a [DeclaredField] wrapper around the declared (possibly non-public) instance field of the receiver object. */ -@DeleteForDJVM fun Any.declaredField(name: String): DeclaredField = DeclaredField(javaClass, name, this) /** * Returns a [DeclaredField] wrapper around the (possibly non-public) instance field of the receiver object, but declared * in its superclass [clazz]. */ -@DeleteForDJVM fun Any.declaredField(clazz: KClass<*>, name: String): DeclaredField = DeclaredField(clazz.java, name, this) /** * Returns a [DeclaredField] wrapper around the (possibly non-public) instance field of the receiver object, but declared * in its superclass [clazz]. */ -@DeleteForDJVM fun Any.declaredField(clazz: Class<*>, name: String): DeclaredField = DeclaredField(clazz, name, this) /** creates a new instance if not a Kotlin object */ @@ -363,7 +347,6 @@ val Class.kotlinObjectInstance: T? get() { * A simple wrapper around a [Field] object providing type safe read and write access using [value], ignoring the field's * visibility. */ -@DeleteForDJVM class DeclaredField(clazz: Class<*>, name: String, private val receiver: Any?) { private val javaField = findField(name, clazz) var value: T @@ -421,7 +404,6 @@ fun uncheckedCast(obj: T) = obj as U fun Iterable>.toMultiMap(): Map> = this.groupBy({ it.first }) { it.second } /** Returns the location of this class. */ -@get:StubOutForDJVM val Class<*>.location: URL get() = protectionDomain.codeSource.location /** Convenience method to get the package name of a class literal. */ @@ -449,14 +431,13 @@ inline val Member.isStatic: Boolean get() = Modifier.isStatic(modifiers) inline val Member.isFinal: Boolean get() = Modifier.isFinal(modifiers) -@DeleteForDJVM fun URI.toPath(): Path = Paths.get(this) +fun URI.toPath(): Path = Paths.get(this) -@DeleteForDJVM fun URL.toPath(): Path = toURI().toPath() +fun URL.toPath(): Path = toURI().toPath() val DEFAULT_HTTP_CONNECT_TIMEOUT = 30.seconds.toMillis() val DEFAULT_HTTP_READ_TIMEOUT = 30.seconds.toMillis() -@DeleteForDJVM fun URL.openHttpConnection(proxy: Proxy? = null): HttpURLConnection = ( if (proxy == null) openConnection() else openConnection(proxy)).also { @@ -465,7 +446,6 @@ fun URL.openHttpConnection(proxy: Proxy? = null): HttpURLConnection = ( it.readTimeout = DEFAULT_HTTP_READ_TIMEOUT.toInt() } as HttpURLConnection -@DeleteForDJVM fun URL.post(serializedData: OpaqueBytes, vararg properties: Pair, proxy: Proxy? = null): ByteArray { return openHttpConnection(proxy).run { doOutput = true @@ -478,7 +458,6 @@ fun URL.post(serializedData: OpaqueBytes, vararg properties: Pair HttpURLConnection.responseAs(): T { checkOkResponse() return inputStream.readObject() } /** Analogous to [Thread.join]. */ -@DeleteForDJVM fun ExecutorService.join() { shutdown() // Do not change to shutdownNow, tests use this method to assert the executor has no more tasks. while (!awaitTermination(1, TimeUnit.SECONDS)) { @@ -524,13 +500,11 @@ $trustAnchors""", e, this, e.index) } } -@DeleteForDJVM inline fun T.signWithCert(signer: (SerializedBytes) -> DigitalSignatureWithCert): SignedDataWithCert { val serialised = serialize() return SignedDataWithCert(serialised, signer(serialised)) } -@DeleteForDJVM fun T.signWithCert(privateKey: PrivateKey, certificate: X509Certificate): SignedDataWithCert { return signWithCert { val signature = Crypto.doSign(privateKey, it.bytes) @@ -538,24 +512,22 @@ fun T.signWithCert(privateKey: PrivateKey, certificate: X509Certificat } } -@DeleteForDJVM fun T.signWithCertPath(privateKey: PrivateKey, certPath: List): SignedDataWithCert { return signWithCert { val signature = Crypto.doSign(privateKey, it.bytes) DigitalSignatureWithCert(certPath.first(), certPath.takeLast(certPath.size - 1), signature) } } -@DeleteForDJVM + inline fun SerializedBytes.sign(signer: (SerializedBytes) -> DigitalSignature.WithKey): SignedData { return SignedData(this, signer(this)) } -@DeleteForDJVM fun SerializedBytes.sign(keyPair: KeyPair): SignedData = SignedData(this, keyPair.sign(this.bytes)) fun ByteBuffer.copyBytes(): ByteArray = ByteArray(remaining()).also { get(it) } -val PublicKey.hash: SecureHash get() = encoded.sha256() +val PublicKey.hash: SecureHash get() = Crypto.encodePublicKey(this).sha256() /** * Extension method for providing a sumBy method that processes and returns a Long @@ -573,9 +545,6 @@ fun SerializedBytes.checkPayloadIs(type: Class): Untrustworthy ?: throw IllegalArgumentException("We were expecting a ${type.name} but we instead got a ${payloadData.javaClass.name} ($payloadData)") } -/** - * Simple Map structure that can be used as a cache in the DJVM. - */ fun createSimpleCache(maxSize: Int, onEject: (MutableMap.MutableEntry) -> Unit = {}): MutableMap { return object : LinkedHashMap() { override fun removeEldestEntry(eldest: MutableMap.MutableEntry?): Boolean { diff --git a/core/src/main/kotlin/net/corda/core/internal/LazyPool.kt b/core/src/main/kotlin/net/corda/core/internal/LazyPool.kt index 2c94dea8df..72140853e9 100644 --- a/core/src/main/kotlin/net/corda/core/internal/LazyPool.kt +++ b/core/src/main/kotlin/net/corda/core/internal/LazyPool.kt @@ -1,6 +1,5 @@ package net.corda.core.internal -import net.corda.core.DeleteForDJVM import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.Semaphore @@ -15,7 +14,6 @@ import java.util.concurrent.Semaphore * instance is released. * @param newInstance The function to call to lazily newInstance a pooled resource. */ -@DeleteForDJVM class LazyPool( private val clear: ((A) -> Unit)? = null, private val shouldReturnToPool: ((A) -> Boolean)? = null, @@ -76,4 +74,18 @@ class LazyPool( release(instance) } } + + private val currentBorrowed = ThreadLocal() + fun reentrantRun(withInstance: (A) -> R): R { + return currentBorrowed.get()?.let { + withInstance(it) + } ?: run { + currentBorrowed.set(it) + try { + withInstance(it) + } finally { + currentBorrowed.set(null) + } + } + } } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/LazyStickyPool.kt b/core/src/main/kotlin/net/corda/core/internal/LazyStickyPool.kt index 868997bc56..76b5f3f17e 100644 --- a/core/src/main/kotlin/net/corda/core/internal/LazyStickyPool.kt +++ b/core/src/main/kotlin/net/corda/core/internal/LazyStickyPool.kt @@ -1,6 +1,5 @@ package net.corda.core.internal -import net.corda.core.DeleteForDJVM import java.util.* import java.util.concurrent.LinkedBlockingQueue @@ -12,7 +11,6 @@ import java.util.concurrent.LinkedBlockingQueue * @param newInstance The function to call to create a pooled resource. */ // TODO This could be implemented more efficiently. Currently the "non-sticky" use case is not optimised, it just chooses a random instance to wait on. -@DeleteForDJVM class LazyStickyPool( size: Int, private val newInstance: () -> A diff --git a/core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt b/core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt index db15d33356..9f97612852 100644 --- a/core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt +++ b/core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt @@ -1,6 +1,5 @@ package net.corda.core.internal -import net.corda.core.KeepForDJVM import net.corda.core.internal.LegalNameValidator.normalize import java.text.Normalizer import javax.security.auth.x500.X500Principal @@ -106,14 +105,12 @@ object LegalNameValidator { abstract fun validate(legalName: T) - @KeepForDJVM private class UnicodeNormalizationRule : Rule() { override fun validate(legalName: String) { require(legalName == normalize(legalName)) { "Legal name must be normalized. Please use 'normalize' to normalize the legal name before validation." } } } - @KeepForDJVM private class UnicodeRangeRule(vararg supportScripts: Character.UnicodeBlock) : Rule() { val supportScriptsSet = supportScripts.toSet() @@ -124,7 +121,6 @@ object LegalNameValidator { } } - @KeepForDJVM private class CharacterRule(vararg val bannedChars: Char) : Rule() { override fun validate(legalName: String) { bannedChars.forEach { @@ -133,7 +129,6 @@ object LegalNameValidator { } } - @KeepForDJVM private class WordRule(vararg val bannedWords: String) : Rule() { override fun validate(legalName: String) { bannedWords.forEach { @@ -142,14 +137,12 @@ object LegalNameValidator { } } - @KeepForDJVM private class LengthRule(val maxLength: Int) : Rule() { override fun validate(legalName: String) { require(legalName.length <= maxLength) { "Legal name longer then $maxLength characters." } } } - @KeepForDJVM private class CapitalLetterRule : Rule() { override fun validate(legalName: String) { val capitalizedLegalName = legalName.capitalize() @@ -157,7 +150,6 @@ object LegalNameValidator { } } - @KeepForDJVM private class X500NameRule : Rule() { override fun validate(legalName: String) { // This will throw IllegalArgumentException if the name does not comply with X500 name format. @@ -165,7 +157,6 @@ object LegalNameValidator { } } - @KeepForDJVM private class MustHaveAtLeastTwoLettersRule : Rule() { override fun validate(legalName: String) { // Try to exclude names like "/", "£", "X" etc. diff --git a/core/src/main/kotlin/net/corda/core/internal/LifeCycle.kt b/core/src/main/kotlin/net/corda/core/internal/LifeCycle.kt index d0f3118309..3b27cd746f 100644 --- a/core/src/main/kotlin/net/corda/core/internal/LifeCycle.kt +++ b/core/src/main/kotlin/net/corda/core/internal/LifeCycle.kt @@ -1,6 +1,5 @@ package net.corda.core.internal -import net.corda.core.DeleteForDJVM import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.withLock @@ -10,7 +9,6 @@ import kotlin.concurrent.withLock * * @param initial The initial state. */ -@DeleteForDJVM class LifeCycle>(initial: S) { private val lock = ReentrantReadWriteLock() private var state = initial diff --git a/core/src/main/kotlin/net/corda/core/internal/PathUtils.kt b/core/src/main/kotlin/net/corda/core/internal/PathUtils.kt index e8559f2f62..ffe686894c 100644 --- a/core/src/main/kotlin/net/corda/core/internal/PathUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/PathUtils.kt @@ -1,7 +1,5 @@ -@file:DeleteForDJVM package net.corda.core.internal -import net.corda.core.DeleteForDJVM import net.corda.core.crypto.SecureHash import net.corda.core.serialization.deserialize import java.io.* diff --git a/core/src/main/kotlin/net/corda/core/internal/PlatformVersionSwitches.kt b/core/src/main/kotlin/net/corda/core/internal/PlatformVersionSwitches.kt index c6d93f272f..f40ea96c6c 100644 --- a/core/src/main/kotlin/net/corda/core/internal/PlatformVersionSwitches.kt +++ b/core/src/main/kotlin/net/corda/core/internal/PlatformVersionSwitches.kt @@ -18,4 +18,5 @@ object PlatformVersionSwitches { const val ENABLE_P2P_COMPRESSION = 7 const val RESTRICTED_DATABASE_OPERATIONS = 7 const val CERTIFICATE_ROTATION = 9 + const val TWO_PHASE_FINALITY = 13 } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/ResolveTransactionsFlow.kt b/core/src/main/kotlin/net/corda/core/internal/ResolveTransactionsFlow.kt index 66b525692f..3896d5648c 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ResolveTransactionsFlow.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ResolveTransactionsFlow.kt @@ -1,7 +1,6 @@ package net.corda.core.internal import co.paralleluniverse.fibers.Suspendable -import net.corda.core.DeleteForDJVM import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowSession @@ -16,17 +15,24 @@ import net.corda.core.utilities.trace * Resolves transactions for the specified [txHashes] along with their full history (dependency graph) from [otherSide]. * Each retrieved transaction is validated and inserted into the local transaction storage. */ -@DeleteForDJVM class ResolveTransactionsFlow private constructor( val initialTx: SignedTransaction?, val txHashes: Set, val otherSide: FlowSession, - val statesToRecord: StatesToRecord + val statesToRecord: StatesToRecord, + val deferredAck: Boolean = false, + val recoveryMode: Boolean = false ) : FlowLogic() { constructor(txHashes: Set, otherSide: FlowSession, statesToRecord: StatesToRecord = StatesToRecord.NONE) : this(null, txHashes, otherSide, statesToRecord) + constructor(txHashes: Set, otherSide: FlowSession, statesToRecord: StatesToRecord, deferredAck: Boolean) + : this(null, txHashes, otherSide, statesToRecord, deferredAck) + + constructor(txHashes: Set, otherSide: FlowSession, statesToRecord: StatesToRecord, deferredAck: Boolean, recoveryMode: Boolean ) + : this(null, txHashes, otherSide, statesToRecord, deferredAck, recoveryMode) + /** * Resolves and validates the dependencies of the specified [SignedTransaction]. Fetches the attachments, but does * *not* validate or store the [SignedTransaction] itself. @@ -36,7 +42,12 @@ class ResolveTransactionsFlow private constructor( constructor(transaction: SignedTransaction, otherSide: FlowSession, statesToRecord: StatesToRecord = StatesToRecord.NONE) : this(transaction, transaction.dependencies, otherSide, statesToRecord) + constructor(transaction: SignedTransaction, otherSide: FlowSession, statesToRecord: StatesToRecord = StatesToRecord.NONE, deferredAck: Boolean = false) + : this(transaction, transaction.dependencies, otherSide, statesToRecord, deferredAck) + private var fetchNetParamsFromCounterpart = false + // statistics set in DbResolver (Enterprise only) after exiting recordDependencies() + var statistics: ResolveTransactionsFlowStats = ResolveTransactionsFlowStats() @Suppress("MagicNumber") @Suspendable @@ -58,10 +69,12 @@ class ResolveTransactionsFlow private constructor( } val resolver = (serviceHub as ServiceHubCoreInternal).createTransactionsResolver(this) - resolver.downloadDependencies(batchMode) + resolver.downloadDependencies(batchMode, recoveryMode) - logger.trace { "ResolveTransactionsFlow: Sending END." } - otherSide.send(FetchDataFlow.Request.End) // Finish fetching data. + if (!deferredAck) { + logger.trace { "ResolveTransactionsFlow: Sending END." } + otherSide.send(FetchDataFlow.Request.End) // Finish fetching data. + } // If transaction resolution is performed for a transaction where some states are relevant, then those should be // recorded if this has not already occurred. @@ -109,3 +122,10 @@ class ResolveTransactionsFlow private constructor( } } } + +data class ResolveTransactionsFlowStats( + val resolvableTransactionHashesCount: Long = 0, + val downloadedDependencyCount: Long = 0, + val recordedTransactionCount: Long = 0, + val skippedRecordingTransactionCount: Long = 0 +) diff --git a/core/src/main/kotlin/net/corda/core/internal/ServiceHubCoreInternal.kt b/core/src/main/kotlin/net/corda/core/internal/ServiceHubCoreInternal.kt index 5f90afa5b7..e25f04894e 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ServiceHubCoreInternal.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ServiceHubCoreInternal.kt @@ -1,16 +1,19 @@ package net.corda.core.internal import co.paralleluniverse.fibers.Suspendable -import net.corda.core.DeleteForDJVM import net.corda.core.contracts.RotatedKeys +import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.TransactionSignature +import net.corda.core.flows.TransactionMetadata +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.notary.NotaryService import net.corda.core.node.ServiceHub import net.corda.core.node.StatesToRecord import net.corda.core.serialization.internal.AttachmentsClassLoaderCache +import net.corda.core.transactions.SignedTransaction import java.util.concurrent.ExecutorService // TODO: This should really be called ServiceHubInternal but that name is already taken by net.corda.node.services.api.ServiceHubInternal. -@DeleteForDJVM interface ServiceHubCoreInternal : ServiceHub { val externalOperationExecutor: ExecutorService @@ -22,16 +25,66 @@ interface ServiceHubCoreInternal : ServiceHub { */ val notaryService: NotaryService? - fun createTransactionsResolver(flow: ResolveTransactionsFlow): TransactionsResolver + fun createTransactionsResolver(flow: ResolveTransactionsval rotatedKeys: RotatedKeys) - val attachmentsClassLoaderCache: AttachmentsClassLoaderCache + /** + * Stores [SignedTransaction] and participant signatures without the notary signature in the local transaction storage, + * inclusive of flow recovery metadata. + * This is expected to be run within a database transaction. + * + * @param txn The transaction to record. + */ + fun recordUnnotarisedTransaction(txn: SignedTransaction) - val rotatedKeys: RotatedKeys + /** + * Removes transaction from data store. + * This is expected to be run within a database transaction. + * + * @param id of transaction to remove. + */ + fun removeUnnotarisedTransaction(id: SecureHash) + + /** + * Stores [SignedTransaction] with extra signatures in the local transaction storage + * + * @param sigs The signatures to add to the transaction. + * @param txn The transactions to record. + * @param statesToRecord how the vault should treat the output states of the transaction. + */ + fun finalizeTransactionWithExtraSignatures(txn: SignedTransaction, sigs: Collection, statesToRecord: StatesToRecord) + + /** + * Records a [SignedTransaction] as VERIFIED with flow recovery metadata. + * + * @param txn The transaction to record. + * @param statesToRecord how the vault should treat the output states of the transaction. + */ + fun finalizeTransaction(txn: SignedTransaction, statesToRecord: StatesToRecord) + + /** + * Records Sender [TransactionMetadata] for a given txnId. + * + * @param txnId The SecureHash of a transaction. + * @param txnMetadata The recovery metadata associated with a transaction. + * @return encrypted distribution list (hashed peers -> StatesToRecord values). + */ + fun recordSenderTransactionRecoveryMetadata(txnId: SecureHash, txnMetadata: TransactionMetadata): ByteArray? + + /** + * Records Received [TransactionMetadata] for a given txnId. + * + * @param txnId The SecureHash of a transaction. + * @param sender The sender of the transaction. + * @param txnMetadata The recovery metadata associated with a transaction. + */ + fun recordReceiverTransactionRecoveryMetadata(txnId: SecureHash, + sender: CordaX500Name, + txnMetadata: TransactionMetadata) } interface TransactionsResolver { @Suspendable - fun downloadDependencies(batchMode: Boolean) + fun downloadDependencies(batchMode: Boolean, recoveryMode: Boolean) @Suspendable fun recordDependencies(usedStatesToRecord: StatesToRecord) diff --git a/core/src/main/kotlin/net/corda/core/internal/ThreadBox.kt b/core/src/main/kotlin/net/corda/core/internal/ThreadBox.kt index b19439710b..eedd576694 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ThreadBox.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ThreadBox.kt @@ -1,6 +1,5 @@ package net.corda.core.internal -import net.corda.core.DeleteForDJVM import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock @@ -22,7 +21,6 @@ import kotlin.concurrent.withLock * val ii = state.locked { i } * ``` */ -@DeleteForDJVM class ThreadBox(val content: T, val lock: ReentrantLock = ReentrantLock()) { inline fun locked(body: T.() -> R): R = lock.withLock { body(content) } inline fun alreadyLocked(body: T.() -> R): R { diff --git a/core/src/main/kotlin/net/corda/core/internal/TransactionUtils.kt b/core/src/main/kotlin/net/corda/core/internal/TransactionUtils.kt index 941f2db3e3..2e83aa7bad 100644 --- a/core/src/main/kotlin/net/corda/core/internal/TransactionUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/TransactionUtils.kt @@ -1,6 +1,5 @@ package net.corda.core.internal -import net.corda.core.KeepForDJVM import net.corda.core.contracts.* import net.corda.core.crypto.DigestService import net.corda.core.crypto.SecureHash @@ -195,7 +194,6 @@ fun createComponentGroups(inputs: List, * A SerializedStateAndRef is a pair (BinaryStateRepresentation, StateRef). * The [serializedState] is the actual component from the original wire transaction. */ -@KeepForDJVM data class SerializedStateAndRef(val serializedState: SerializedBytes>, val ref: StateRef) { fun toStateAndRef(factory: SerializationFactory, context: SerializationContext) = StateAndRef(serializedState.deserialize(factory, context), ref) fun toStateAndRef(): StateAndRef { diff --git a/core/src/main/kotlin/net/corda/core/internal/TransactionVerifierServiceInternal.kt b/core/src/main/kotlin/net/corda/core/internal/TransactionVerifierServiceInternal.kt index 74cb1577f5..45381599d6 100644 --- a/core/src/main/kotlin/net/corda/core/internal/TransactionVerifierServiceInternal.kt +++ b/core/src/main/kotlin/net/corda/core/internal/TransactionVerifierServiceInternal.kt @@ -1,7 +1,5 @@ package net.corda.core.internal -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.concurrent.CordaFuture import net.corda.core.contracts.Attachment import net.corda.core.contracts.AttachmentConstraint @@ -40,7 +38,6 @@ import net.corda.core.utilities.loggerFor import java.util.function.Function import java.util.function.Supplier -@DeleteForDJVM interface TransactionVerifierServiceInternal { fun reverifyWithFixups(transaction: LedgerTransaction, missingClass: String?): CordaFuture<*> } @@ -91,7 +88,7 @@ abstract class AbstractVerifier( * Because we create a separate [LedgerTransaction] onto which we need to perform verification, it becomes important we don't verify the * wrong object instance. This class helps */ -private class Validator(private val ltx: LedgerTransaction, private val transactionClassLoader: ClassLoader, private val rotatedKeys: RotatedKeys) { +private class Validator(private val ltx: LedgerTransaction, private val transactionClassLoader: ClassLoader) { private val inputStates: List> = ltx.inputs.map(StateAndRef::state) private val allStates: List> = inputStates + ltx.references.map(StateAndRef::state) + ltx.outputs @@ -453,12 +450,7 @@ private class Validator(private val ltx: LedgerTransaction, private val transact * its contents, as well as executing all of its smart contracts. */ @Suppress("TooGenericExceptionCaught") -@KeepForDJVM class TransactionVerifier(private val transactionClassLoader: ClassLoader) : Function, Unit> { - // This constructor is used inside the DJVM's sandbox. - @Suppress("unused") - constructor() : this(ClassLoader.getSystemClassLoader()) - // Loads the contract class from the transactionClassLoader. private fun createContractClass(id: SecureHash, contractClassName: ContractClassName): Class { return try { @@ -475,15 +467,7 @@ class TransactionVerifier(private val transactionClassLoader: ClassLoader) : Fun createContractClass(ltx.id, contractClassName) }.map { contractClass -> try { - /** - * This function must execute within the DJVM's sandbox, which does not - * permit user code to invoke [java.lang.reflect.Constructor.newInstance]. - * (This would be fixable now, provided the constructor is public.) - * - * [Class.newInstance] is deprecated as of Java 9. - */ - @Suppress("deprecation") - contractClass.newInstance() + contractClass.getDeclaredConstructor().newInstance() } catch (e: Exception) { throw ContractCreationError(ltx.id, contractClass.name, e) } diff --git a/core/src/main/kotlin/net/corda/core/internal/X500Utils.kt b/core/src/main/kotlin/net/corda/core/internal/X500Utils.kt index 35b7cddefa..4359139eaf 100644 --- a/core/src/main/kotlin/net/corda/core/internal/X500Utils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/X500Utils.kt @@ -1,8 +1,5 @@ -@file:KeepForDJVM - package net.corda.core.internal -import net.corda.core.KeepForDJVM import net.corda.core.identity.CordaX500Name import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.x500.AttributeTypeAndValue diff --git a/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt b/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt index 5c0d6ebe80..94c4897da8 100644 --- a/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt +++ b/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt @@ -1,9 +1,15 @@ package net.corda.core.internal -import net.corda.core.DeleteForDJVM +import net.corda.core.crypto.Crypto import net.i2p.crypto.eddsa.EdDSAEngine import net.i2p.crypto.eddsa.EdDSAPublicKey -import java.security.* +import java.security.AlgorithmParameters +import java.security.InvalidKeyException +import java.security.MessageDigest +import java.security.PrivateKey +import java.security.PublicKey +import java.security.SecureRandom +import java.security.Signature import java.security.spec.AlgorithmParameterSpec import java.security.spec.X509EncodedKeySpec @@ -25,12 +31,12 @@ class X509EdDSAEngine : Signature { } override fun engineInitSign(privateKey: PrivateKey) = engine.initSign(privateKey) - @DeleteForDJVM + override fun engineInitSign(privateKey: PrivateKey, random: SecureRandom) = engine.initSign(privateKey, random) override fun engineInitVerify(publicKey: PublicKey) { val parsedKey = try { - publicKey as? EdDSAPublicKey ?: EdDSAPublicKey(X509EncodedKeySpec(publicKey.encoded)) + publicKey as? EdDSAPublicKey ?: EdDSAPublicKey(X509EncodedKeySpec(Crypto.encodePublicKey(publicKey))) } catch (e: Exception) { throw (InvalidKeyException(e.message)) } diff --git a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt index 327723cb32..32951790c1 100644 --- a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt +++ b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt @@ -1,6 +1,5 @@ package net.corda.core.internal.cordapp -import net.corda.core.DeleteForDJVM import net.corda.core.cordapp.Cordapp import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic @@ -17,7 +16,6 @@ import net.corda.core.serialization.SerializeAsToken import java.net.URL import java.nio.file.Paths -@DeleteForDJVM data class CordappImpl( override val contractClassNames: List, override val initiatedFlows: List>>, diff --git a/core/src/main/kotlin/net/corda/core/internal/notary/NotaryService.kt b/core/src/main/kotlin/net/corda/core/internal/notary/NotaryService.kt index 3f1842e25b..479d6476a5 100644 --- a/core/src/main/kotlin/net/corda/core/internal/notary/NotaryService.kt +++ b/core/src/main/kotlin/net/corda/core/internal/notary/NotaryService.kt @@ -1,6 +1,5 @@ package net.corda.core.internal.notary -import net.corda.core.DeleteForDJVM import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowSession import net.corda.core.flows.NotaryFlow @@ -9,7 +8,6 @@ import net.corda.core.node.ServiceHub import net.corda.core.serialization.SingletonSerializeAsToken import java.security.PublicKey -@DeleteForDJVM abstract class NotaryService : SingletonSerializeAsToken() { abstract val services: ServiceHub abstract val notaryIdentityKey: PublicKey diff --git a/core/src/main/kotlin/net/corda/core/internal/rules/TargetVersionDependentRules.kt b/core/src/main/kotlin/net/corda/core/internal/rules/TargetVersionDependentRules.kt index ae1ce4ab31..1b845a49e3 100644 --- a/core/src/main/kotlin/net/corda/core/internal/rules/TargetVersionDependentRules.kt +++ b/core/src/main/kotlin/net/corda/core/internal/rules/TargetVersionDependentRules.kt @@ -10,8 +10,6 @@ import java.util.concurrent.ConcurrentHashMap import java.util.jar.JarInputStream // This file provides rules that depend on the targetVersion of the current Contract or Flow. -// Rules defined in this package are automatically removed from the DJVM in core-deterministic, -// and must be replaced by a deterministic alternative defined within that module. /** * Rule which determines whether [ContractState]s must declare the [Contract] to which they belong (e.g. via the diff --git a/core/src/main/kotlin/net/corda/core/internal/utilities/Internable.kt b/core/src/main/kotlin/net/corda/core/internal/utilities/Internable.kt index f1523ed038..4d1d578bb3 100644 --- a/core/src/main/kotlin/net/corda/core/internal/utilities/Internable.kt +++ b/core/src/main/kotlin/net/corda/core/internal/utilities/Internable.kt @@ -1,15 +1,12 @@ package net.corda.core.internal.utilities import net.corda.core.CordaInternal -import net.corda.core.KeepForDJVM -@KeepForDJVM interface Internable { @CordaInternal val interner: PrivateInterner } -@KeepForDJVM @CordaInternal interface IternabilityVerifier { // If a type being interned has a slightly dodgy equality check, the more strict rules you probably @@ -17,7 +14,6 @@ interface IternabilityVerifier { fun choose(original: T, interned: T): T } -@KeepForDJVM @CordaInternal class AlwaysInternableVerifier : IternabilityVerifier { override fun choose(original: T, interned: T): T = interned diff --git a/core/src/main/kotlin/net/corda/core/node/AppServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/AppServiceHub.kt index e1726a28ed..a6041f81f0 100644 --- a/core/src/main/kotlin/net/corda/core/node/AppServiceHub.kt +++ b/core/src/main/kotlin/net/corda/core/node/AppServiceHub.kt @@ -1,6 +1,5 @@ package net.corda.core.node -import net.corda.core.DeleteForDJVM import net.corda.core.flows.FlowLogic import net.corda.core.messaging.FlowHandle import net.corda.core.messaging.FlowProgressHandle @@ -16,7 +15,6 @@ import rx.Observable * In particular such a [net.corda.core.node.services.CordaService] can initiate and track flows marked * with [net.corda.core.flows.StartableByService]. */ -@DeleteForDJVM interface AppServiceHub : ServiceHub { companion object { diff --git a/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt b/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt index 50fe5eb258..098b45f802 100644 --- a/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt +++ b/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt @@ -1,7 +1,6 @@ package net.corda.core.node import net.corda.core.CordaRuntimeException -import net.corda.core.KeepForDJVM import net.corda.core.crypto.toStringShort import net.corda.core.identity.Party import net.corda.core.internal.noPackageOverlap @@ -34,10 +33,18 @@ import java.util.Collections.unmodifiableMap * @property packageOwnership ([AutoAcceptable]) List of the network-wide java packages that were successfully claimed by their owners. * Any CorDapp JAR that offers contracts and states in any of these packages must be signed by the owner. * @property eventHorizon Time after which nodes will be removed from the network map if they have not been seen - * during this period + * during this period. + * @property recoveryMaximumBackupInterval A default value, that will be used by the Ledger Recovery flows to set how far back in time to + * consider for recovery. The expectation is that a node will restore to a database backup that is no older than this, by default, when + * attempting a recovery. This value can be overridden by specifying an override to the flow. It can also be overridden if the same parameter + * is specified, per-node in the node configuration. An override to the flow takes priority in terms of overrides. It is optional in both + * the network parameters and the node configuration however if no values are set then it needs to be specified in the flow. + * @property confidentialIdentityMinimumBackupInterval A default value for the minimum age of a generated confidential identity key before + * it can be used. This can be overridden in the node configuration or if a more recent database backup is indicated via RPC / shell. It is + * optional in both the network parameters and the node configuration and if no value is set for either then it is assumed to be zero. */ -@KeepForDJVM @CordaSerializable +@Suppress("LongParameterList") data class NetworkParameters( val minimumPlatformVersion: Int, val notaries: List, @@ -47,10 +54,12 @@ data class NetworkParameters( @AutoAcceptable val epoch: Int, @AutoAcceptable val whitelistedContractImplementations: Map>, val eventHorizon: Duration, - @AutoAcceptable val packageOwnership: Map + @AutoAcceptable val packageOwnership: Map, + val recoveryMaximumBackupInterval: Duration? = null, + val confidentialIdentityMinimumBackupInterval: Duration? = null ) { // DOCEND 1 - @DeprecatedConstructorForDeserialization(1) + @DeprecatedConstructorForDeserialization(version = 1) constructor(minimumPlatformVersion: Int, notaries: List, maxMessageSize: Int, @@ -69,7 +78,7 @@ data class NetworkParameters( emptyMap() ) - @DeprecatedConstructorForDeserialization(2) + @DeprecatedConstructorForDeserialization(version = 2) constructor(minimumPlatformVersion: Int, notaries: List, maxMessageSize: Int, @@ -89,6 +98,29 @@ data class NetworkParameters( emptyMap() ) + @DeprecatedConstructorForDeserialization(version = 3) + constructor(minimumPlatformVersion: Int, + notaries: List, + maxMessageSize: Int, + maxTransactionSize: Int, + modifiedTime: Instant, + epoch: Int, + whitelistedContractImplementations: Map>, + eventHorizon: Duration, + packageOwnership: Map + ) : this(minimumPlatformVersion, + notaries, + maxMessageSize, + maxTransactionSize, + modifiedTime, + epoch, + whitelistedContractImplementations, + eventHorizon, + packageOwnership, + recoveryMaximumBackupInterval = null, + confidentialIdentityMinimumBackupInterval = null + ) + init { require(minimumPlatformVersion > 0) { "Minimum platform level must be at least 1" } require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" } @@ -98,6 +130,41 @@ data class NetworkParameters( require(!eventHorizon.isNegative) { "Event Horizon must be a positive value" } packageOwnership.keys.forEach(::requirePackageValid) require(noPackageOverlap(packageOwnership.keys)) { "Multiple packages added to the packageOwnership overlap." } + require(recoveryMaximumBackupInterval == null || !recoveryMaximumBackupInterval.isNegative) { + "Recovery maximum backup interval must be a positive value" + } + require(confidentialIdentityMinimumBackupInterval == null || !confidentialIdentityMinimumBackupInterval.isNegative) { + "Confidential Identities maximum backup interval must be a positive value" + } + } + + /** + * This is to address backwards compatibility of the API, invariant to package ownership + * addresses bug CORDA-2769 + */ + fun copy(minimumPlatformVersion: Int = this.minimumPlatformVersion, + notaries: List = this.notaries, + maxMessageSize: Int = this.maxMessageSize, + maxTransactionSize: Int = this.maxTransactionSize, + modifiedTime: Instant = this.modifiedTime, + epoch: Int = this.epoch, + whitelistedContractImplementations: Map> = this.whitelistedContractImplementations, + eventHorizon: Duration = this.eventHorizon, + packageOwnership: Map = this.packageOwnership + ): NetworkParameters { + return NetworkParameters( + minimumPlatformVersion = minimumPlatformVersion, + notaries = notaries, + maxMessageSize = maxMessageSize, + maxTransactionSize = maxTransactionSize, + modifiedTime = modifiedTime, + epoch = epoch, + whitelistedContractImplementations = whitelistedContractImplementations, + eventHorizon = eventHorizon, + packageOwnership = packageOwnership, + recoveryMaximumBackupInterval = recoveryMaximumBackupInterval, + confidentialIdentityMinimumBackupInterval = confidentialIdentityMinimumBackupInterval + ) } /** @@ -122,7 +189,9 @@ data class NetworkParameters( epoch = epoch, whitelistedContractImplementations = whitelistedContractImplementations, eventHorizon = eventHorizon, - packageOwnership = packageOwnership + packageOwnership = packageOwnership, + recoveryMaximumBackupInterval = recoveryMaximumBackupInterval, + confidentialIdentityMinimumBackupInterval = confidentialIdentityMinimumBackupInterval ) } @@ -147,7 +216,9 @@ data class NetworkParameters( epoch = epoch, whitelistedContractImplementations = whitelistedContractImplementations, eventHorizon = eventHorizon, - packageOwnership = packageOwnership + packageOwnership = packageOwnership, + recoveryMaximumBackupInterval = recoveryMaximumBackupInterval, + confidentialIdentityMinimumBackupInterval = confidentialIdentityMinimumBackupInterval ) } @@ -166,6 +237,8 @@ data class NetworkParameters( } modifiedTime=$modifiedTime epoch=$epoch + transactionRecoveryPeriod=$recoveryMaximumBackupInterval + confidentialIdentityPreGenerationPeriod=$confidentialIdentityMinimumBackupInterval }""" } @@ -181,7 +254,9 @@ data class NetworkParameters( unmodifiableList(entry.value) }, eventHorizon = eventHorizon, - packageOwnership = unmodifiable(packageOwnership) + packageOwnership = unmodifiable(packageOwnership), + recoveryMaximumBackupInterval = recoveryMaximumBackupInterval, + confidentialIdentityMinimumBackupInterval = confidentialIdentityMinimumBackupInterval ) } } @@ -207,7 +282,6 @@ private inline fun unmodifiable(map: Map, transform: (Map.Entry) { recordTransactions(StatesToRecord.ONLY_RELEVANT, txs) diff --git a/core/src/main/kotlin/net/corda/core/node/services/AttachmentStorage.kt b/core/src/main/kotlin/net/corda/core/node/services/AttachmentStorage.kt index 2591795f4e..e39de6f7b7 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/AttachmentStorage.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/AttachmentStorage.kt @@ -1,8 +1,7 @@ -@file:KeepForDJVM + package net.corda.core.node.services import net.corda.core.DoNotImplement -import net.corda.core.KeepForDJVM import net.corda.core.contracts.Attachment import net.corda.core.crypto.SecureHash import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION diff --git a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt index 8e46572f1c..5f9bcaeedc 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt @@ -1,6 +1,5 @@ package net.corda.core.node.services -import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement import net.corda.core.concurrent.CordaFuture import net.corda.core.identity.AbstractParty @@ -59,7 +58,7 @@ interface NetworkMapCacheBase { /** Tracks changes to the network map cache. */ val changed: Observable /** Future to track completion of the NetworkMapService registration. */ - @get:DeleteForDJVM val nodeReady: CordaFuture + val nodeReady: CordaFuture /** * Atomically get the current party nodes and a stream of updates. Note that the Observable buffers updates until the diff --git a/core/src/main/kotlin/net/corda/core/node/services/TimeWindowChecker.kt b/core/src/main/kotlin/net/corda/core/node/services/TimeWindowChecker.kt index b7bf3611fa..a4eeb0b487 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/TimeWindowChecker.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/TimeWindowChecker.kt @@ -1,6 +1,5 @@ package net.corda.core.node.services -import net.corda.core.DeleteForDJVM import net.corda.core.contracts.TimeWindow import java.time.Clock @@ -8,7 +7,6 @@ import java.time.Clock * Checks if the current instant provided by the input clock falls within the provided time-window. */ @Deprecated("This class is no longer used") -@DeleteForDJVM class TimeWindowChecker(val clock: Clock = Clock.systemUTC()) { fun isValid(timeWindow: TimeWindow): Boolean = clock.instant() in timeWindow } diff --git a/core/src/main/kotlin/net/corda/core/node/services/TransactionStorage.kt b/core/src/main/kotlin/net/corda/core/node/services/TransactionStorage.kt index 5bdb494be6..a68716a6d3 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/TransactionStorage.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/TransactionStorage.kt @@ -1,17 +1,17 @@ package net.corda.core.node.services -import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement import net.corda.core.concurrent.CordaFuture +import net.corda.core.contracts.NamedByHash import net.corda.core.crypto.SecureHash import net.corda.core.messaging.DataFeed +import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import rx.Observable /** * Thread-safe storage of transactions. */ -@DeleteForDJVM @DoNotImplement interface TransactionStorage { /** @@ -19,6 +19,11 @@ interface TransactionStorage { */ fun getTransaction(id: SecureHash): SignedTransaction? + /** + * Return the transaction with its status for the given [id], or null if no such transaction exists. + */ + fun getTransactionWithStatus(id: SecureHash): SignedTransactionWithStatus? + /** * Get a synchronous Observable of updates. When observations are pushed to the Observer, the vault will already * incorporate the update. @@ -34,4 +39,20 @@ interface TransactionStorage { * Returns a future that completes with the transaction corresponding to [id] once it has been committed */ fun trackTransaction(id: SecureHash): CordaFuture +} + +@CordaSerializable +data class SignedTransactionWithStatus( + val stx: SignedTransaction, + val status: TransactionStatus +) : NamedByHash { + override val id: SecureHash + get() = stx.id +} + +@CordaSerializable +enum class TransactionStatus { + UNVERIFIED, + VERIFIED, + IN_FLIGHT; } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/node/services/TransactionVerifierService.kt b/core/src/main/kotlin/net/corda/core/node/services/TransactionVerifierService.kt index e1c731ce86..d72eec72f2 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/TransactionVerifierService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/TransactionVerifierService.kt @@ -1,6 +1,5 @@ package net.corda.core.node.services -import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement import net.corda.core.concurrent.CordaFuture import net.corda.core.transactions.LedgerTransaction @@ -10,7 +9,6 @@ import net.corda.core.transactions.LedgerTransaction * @suppress */ @DoNotImplement -@DeleteForDJVM interface TransactionVerifierService { /** * @param transaction The transaction to be verified. diff --git a/core/src/main/kotlin/net/corda/core/node/services/VaultService.kt b/core/src/main/kotlin/net/corda/core/node/services/VaultService.kt index dfee47b2ea..e11e59cc9c 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/VaultService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/VaultService.kt @@ -3,10 +3,20 @@ package net.corda.core.node.services import co.paralleluniverse.fibers.Suspendable -import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement import net.corda.core.concurrent.CordaFuture -import net.corda.core.contracts.* +import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint +import net.corda.core.contracts.Amount +import net.corda.core.contracts.AttachmentConstraint +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.FungibleAsset +import net.corda.core.contracts.FungibleState +import net.corda.core.contracts.HashAttachmentConstraint +import net.corda.core.contracts.ReferencedStateAndRef +import net.corda.core.contracts.SignatureAttachmentConstraint +import net.corda.core.contracts.StateAndRef +import net.corda.core.contracts.StateRef +import net.corda.core.contracts.WhitelistedByZoneAttachmentConstraint import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowException @@ -15,10 +25,18 @@ import net.corda.core.identity.AbstractParty import net.corda.core.internal.MAX_NUMBER_OF_KEYS_IN_SIGNATURE_CONSTRAINT import net.corda.core.internal.concurrent.doneFuture import net.corda.core.messaging.DataFeed -import net.corda.core.node.services.Vault.RelevancyStatus.* +import net.corda.core.node.StatesToRecord +import net.corda.core.node.services.Vault.RelevancyStatus.ALL +import net.corda.core.node.services.Vault.RelevancyStatus.NOT_RELEVANT +import net.corda.core.node.services.Vault.RelevancyStatus.RELEVANT import net.corda.core.node.services.Vault.StateStatus -import net.corda.core.node.services.vault.* +import net.corda.core.node.services.vault.DEFAULT_PAGE_SIZE +import net.corda.core.node.services.vault.MAX_PAGE_SIZE +import net.corda.core.node.services.vault.PageSpecification +import net.corda.core.node.services.vault.QueryCriteria +import net.corda.core.node.services.vault.Sort import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.DeprecatedConstructorForDeserialization import net.corda.core.toFuture import net.corda.core.transactions.LedgerTransaction import net.corda.core.utilities.NonEmptySet @@ -50,7 +68,7 @@ class Vault(val states: Iterable>) { * other transactions observed, then the changes are observed "net" of those. */ @CordaSerializable - data class Update @JvmOverloads constructor( + data class Update constructor( val consumed: Set>, val produced: Set>, val flowId: UUID? = null, @@ -60,8 +78,21 @@ class Vault(val states: Iterable>) { * differently. */ val type: UpdateType = UpdateType.GENERAL, - val references: Set> = emptySet() + val references: Set> = emptySet(), + val consumingTxIds: Map = emptyMap() ) { + @DeprecatedConstructorForDeserialization(1) + @JvmOverloads constructor( consumed: Set>, + produced: Set>, + flowId: UUID? = null, + /** + * Specifies the type of update, currently supported types are general and, contract upgrade and notary change. + * Notary change transactions only modify the notary field on states, and potentially need to be handled + * differently. + */ + type: UpdateType = UpdateType.GENERAL, + references: Set> = emptySet()) : this(consumed, produced, flowId, type, references, consumingTxIds = emptyMap()) + /** Checks whether the update contains a state of the specified type. */ inline fun containsType() = consumed.any { it.state.data is T } || produced.any { it.state.data is T } || references.any { it.state.data is T } @@ -87,9 +118,9 @@ class Vault(val states: Iterable>) { val combinedConsumed = consumed + (rhs.consumed - produced) // The ordering below matters to preserve ordering of consumed/produced Sets when they are insertion order dependent implementations. val combinedProduced = produced.filter { it !in rhs.consumed }.toSet() + rhs.produced - return copy(consumed = combinedConsumed, produced = combinedProduced, references = references + rhs.references) + return copy(consumed = combinedConsumed, produced = combinedProduced, references = references + rhs.references, consumingTxIds = consumingTxIds + rhs.consumingTxIds) } - + override fun toString(): String { val sb = StringBuilder() sb.appendln("${consumed.size} consumed, ${produced.size} produced") @@ -107,6 +138,10 @@ class Vault(val states: Iterable>) { references.forEach { sb.appendln("${it.ref}: ${it.state}") } + sb.appendln("Consuming TxIds:") + consumingTxIds.forEach { + sb.appendln("${it.key}: ${it.value}") + } return sb.toString() } @@ -117,9 +152,19 @@ class Vault(val states: Iterable>) { flowId: UUID? = null, type: UpdateType = UpdateType.GENERAL ): Update { - return Update(consumed, produced, flowId, type, references) + return Update(consumed, produced, flowId, type, references, consumingTxIds) } + /** Additional copy method to maintain backwards compatibility. */ + fun copy( + consumed: Set>, + produced: Set>, + flowId: UUID? = null, + type: UpdateType = UpdateType.GENERAL, + references: Set> = emptySet() + ): Update { + return Update(consumed, produced, flowId, type, references, consumingTxIds) + } } @CordaSerializable @@ -168,7 +213,7 @@ class Vault(val states: Iterable>) { fun data(): ByteArray? { return when (type()) { Type.HASH -> (constraint as HashAttachmentConstraint).attachmentId.bytes - Type.SIGNATURE -> (constraint as SignatureAttachmentConstraint).key.encoded + Type.SIGNATURE -> Crypto.encodePublicKey((constraint as SignatureAttachmentConstraint).key) else -> null } } @@ -198,15 +243,28 @@ class Vault(val states: Iterable>) { * otherwise it defaults to -1. * 4) Status types used in this query: [StateStatus.UNCONSUMED], [StateStatus.CONSUMED], [StateStatus.ALL]. * 5) Other results as a [List] of any type (eg. aggregate function results with/without group by). + * 6) A [StateRef] pointing to the last state of the previous page. Use this to detect if the database has changed whilst loading pages + * by checking it matches your last loaded state. * * Note: currently [otherResults] is used only for aggregate functions (in which case, [states] and [statesMetadata] will be empty). */ @CordaSerializable - data class Page(val states: List>, - val statesMetadata: List, - val totalStatesAvailable: Long, - val stateTypes: StateStatus, - val otherResults: List) + data class Page @JvmOverloads constructor( + val states: List>, + val statesMetadata: List, + val totalStatesAvailable: Long, + val stateTypes: StateStatus, + val otherResults: List, + val previousPageAnchor: StateRef? = null + ) { + fun copy(states: List> = this.states, + statesMetadata: List = this.statesMetadata, + totalStatesAvailable: Long = this.totalStatesAvailable, + stateTypes: StateStatus = this.stateTypes, + otherResults: List = this.otherResults): Page { + return Page(states, statesMetadata, totalStatesAvailable, stateTypes, otherResults, null) + } + } @CordaSerializable data class StateMetadata @JvmOverloads constructor( @@ -299,7 +357,6 @@ interface VaultService { /** * Provide a [CordaFuture] for when a [StateRef] is consumed, which can be very useful in building tests. */ - @DeleteForDJVM fun whenConsumed(ref: StateRef): CordaFuture> { val query = QueryCriteria.VaultQueryCriteria( stateRefs = listOf(ref), 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 4a7a10b21e..5295b4a46a 100644 --- a/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt +++ b/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt @@ -1,6 +1,5 @@ package net.corda.core.schemas -import net.corda.core.KeepForDJVM import net.corda.core.contracts.ContractState import net.corda.core.contracts.StateRef import net.corda.core.serialization.CordaSerializable @@ -16,7 +15,6 @@ import javax.persistence.MappedSuperclass * A contract state that may be mapped to database schemas configured for this node to support querying for, * or filtering of, states. */ -@KeepForDJVM interface QueryableState : ContractState { /** * Enumerate the schemas this state can export representations of itself as. @@ -39,7 +37,6 @@ interface QueryableState : ContractState { * @param version The version number of this instance within the family. * @param mappedTypes The JPA entity classes that the ORM layer needs to be configure with for this schema. */ -@KeepForDJVM open class MappedSchema(schemaFamily: Class<*>, val version: Int, val mappedTypes: Iterable>) { @@ -78,7 +75,6 @@ open class MappedSchema(schemaFamily: Class<*>, * A super class for all mapped states exported to a schema that ensures the [StateRef] appears on the database row. The * [StateRef] will be set to the correct value by the framework (there's no need to set during mapping generation by the state itself). */ -@KeepForDJVM @MappedSuperclass @CordaSerializable class PersistentState(@EmbeddedId override var stateRef: PersistentStateRef? = null) : DirectStatePersistable @@ -86,7 +82,6 @@ class PersistentState(@EmbeddedId override var stateRef: PersistentStateRef? = n /** * Embedded [StateRef] representation used in state mapping. */ -@KeepForDJVM @Embeddable @Immutable @@ -104,7 +99,6 @@ data class PersistentStateRef( /** * Marker interface to denote a persistable Corda state entity that will always have a transaction id and index */ -@KeepForDJVM interface StatePersistable /** diff --git a/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsException.kt b/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsException.kt index b3c73786a1..f353a6ee27 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsException.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsException.kt @@ -1,11 +1,9 @@ package net.corda.core.serialization import net.corda.core.CordaException -import net.corda.core.KeepForDJVM import net.corda.core.crypto.SecureHash /** Thrown during deserialization to indicate that an attachment needed to construct the [WireTransaction] is not found. */ -@KeepForDJVM @CordaSerializable class MissingAttachmentsException(val ids: List, message: String?) : CordaException(message) { diff --git a/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsRuntimeException.kt b/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsRuntimeException.kt index ddeeacc618..d09b700e25 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsRuntimeException.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsRuntimeException.kt @@ -1,10 +1,8 @@ package net.corda.core.serialization import net.corda.core.CordaRuntimeException -import net.corda.core.KeepForDJVM import net.corda.core.node.services.AttachmentId -@KeepForDJVM @CordaSerializable class MissingAttachmentsRuntimeException(val ids: List, message: String?, cause: Throwable?) : CordaRuntimeException(message, cause) { diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt index 77289d8c8e..f4c543a25d 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt @@ -1,9 +1,7 @@ -@file:KeepForDJVM + package net.corda.core.serialization -import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement -import net.corda.core.KeepForDJVM import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 import net.corda.core.serialization.internal.effectiveSerializationEnv @@ -118,7 +116,6 @@ interface SerializationEncoding /** * Parameters to serialization and deserialization. */ -@KeepForDJVM @DoNotImplement interface SerializationContext { /** @@ -254,7 +251,6 @@ interface SerializationContext { /** * The use case that we are serializing for, since it influences the implementations chosen. */ - @KeepForDJVM enum class UseCase { P2P, RPCServer, @@ -269,7 +265,6 @@ interface SerializationContext { * others being set that aren't keyed on this enumeration, but for general use properties adding a * well known key here is preferred. */ -@KeepForDJVM enum class ContextPropertyKeys { SERIALIZERS } @@ -277,13 +272,12 @@ enum class ContextPropertyKeys { /** * Global singletons to be used as defaults that are injected elsewhere (generally, in the node or in RPC client). */ -@KeepForDJVM object SerializationDefaults { val SERIALIZATION_FACTORY get() = effectiveSerializationEnv.serializationFactory val P2P_CONTEXT get() = effectiveSerializationEnv.p2pContext - @DeleteForDJVM val RPC_SERVER_CONTEXT get() = effectiveSerializationEnv.rpcServerContext - @DeleteForDJVM val RPC_CLIENT_CONTEXT get() = effectiveSerializationEnv.rpcClientContext - @DeleteForDJVM val STORAGE_CONTEXT get() = effectiveSerializationEnv.storageContext + val RPC_SERVER_CONTEXT get() = effectiveSerializationEnv.rpcServerContext + val RPC_CLIENT_CONTEXT get() = effectiveSerializationEnv.rpcClientContext + val STORAGE_CONTEXT get() = effectiveSerializationEnv.storageContext } /** @@ -323,7 +317,6 @@ inline fun ByteArray.deserialize(serializationFactory: Seriali /** * Convenience extension method for deserializing a JDBC Blob, utilising the defaults. */ -@DeleteForDJVM inline fun Blob.deserialize(serializationFactory: SerializationFactory = SerializationFactory.defaultFactory, context: SerializationContext = serializationFactory.defaultContext): T { return this.getBytes(1, this.length().toInt()).deserialize(serializationFactory, context) @@ -342,7 +335,6 @@ fun T.serialize(serializationFactory: SerializationFactory = Serializa * to get the original object back. */ @Suppress("unused") -@KeepForDJVM @CordaSerializable class SerializedBytes(bytes: ByteArray) : OpaqueBytes(bytes) { companion object { @@ -362,12 +354,10 @@ class SerializedBytes(bytes: ByteArray) : OpaqueBytes(bytes) { val hash: SecureHash by lazy { bytes.sha256() } } -@KeepForDJVM interface ClassWhitelist { fun hasListed(type: Class<*>): Boolean } -@KeepForDJVM @DoNotImplement interface EncodingWhitelist { fun acceptEncoding(encoding: SerializationEncoding): Boolean diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationCustomSerializer.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationCustomSerializer.kt index ed387f8f94..979be233e4 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/SerializationCustomSerializer.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationCustomSerializer.kt @@ -1,7 +1,5 @@ package net.corda.core.serialization -import net.corda.core.KeepForDJVM - /** * Allows CorDapps to provide custom serializers for third party libraries where those libraries cannot * be recompiled with the -parameters flag rendering their classes natively serializable by Corda. In this case @@ -11,7 +9,6 @@ import net.corda.core.KeepForDJVM * NOTE: The proxy object should be specified as a separate class. However, this can be defined within the * scope of the custom serializer. */ -@KeepForDJVM interface SerializationCustomSerializer { /** * Should facilitate the conversion of the third party object into the serializable @@ -34,7 +31,6 @@ interface SerializationCustomSerializer { * NOTE: Only implement this interface if you have a class that triggers an error during normal checkpoint * serialization/deserialization. */ -@KeepForDJVM interface CheckpointCustomSerializer { /** * Should facilitate the conversion of the third party object into the serializable diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationToken.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationToken.kt index 1bc11091f3..9ec9fb75c0 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/SerializationToken.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationToken.kt @@ -1,6 +1,5 @@ package net.corda.core.serialization -import net.corda.core.DeleteForDJVM import net.corda.core.node.ServiceHub import net.corda.core.serialization.SingletonSerializationToken.Companion.singletonSerializationToken @@ -19,7 +18,6 @@ import net.corda.core.serialization.SingletonSerializationToken.Companion.single * * This models a similar pattern to the readReplace/writeReplace methods in Java serialization. */ -@DeleteForDJVM @CordaSerializable interface SerializeAsToken { fun toToken(context: SerializeAsTokenContext): SerializationToken @@ -28,7 +26,6 @@ interface SerializeAsToken { /** * This represents a token in the serialized stream for an instance of a type that implements [SerializeAsToken]. */ -@DeleteForDJVM interface SerializationToken { fun fromToken(context: SerializeAsTokenContext): Any } @@ -36,7 +33,6 @@ interface SerializationToken { /** * A context for mapping SerializationTokens to/from SerializeAsTokens. */ -@DeleteForDJVM interface SerializeAsTokenContext { val serviceHub: ServiceHub fun putSingleton(toBeTokenized: SerializeAsToken) @@ -47,7 +43,6 @@ interface SerializeAsTokenContext { * A class representing a [SerializationToken] for some object that is not serializable but can be looked up * (when deserialized) via just the class name. */ -@DeleteForDJVM class SingletonSerializationToken private constructor(private val className: String) : SerializationToken { override fun fromToken(context: SerializeAsTokenContext) = context.getSingleton(className) @@ -63,7 +58,6 @@ class SingletonSerializationToken private constructor(private val className: Str * A base class for implementing large objects / components / services that need to serialize themselves to a string token * to indicate which instance the token is a serialized form of. */ -@DeleteForDJVM abstract class SingletonSerializeAsToken : SerializeAsToken { private val token = singletonSerializationToken(javaClass) diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationWhitelist.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationWhitelist.kt index cc0b56e32e..0c2be0c16b 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/SerializationWhitelist.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationWhitelist.kt @@ -1,13 +1,10 @@ package net.corda.core.serialization -import net.corda.core.KeepForDJVM - /** * Provide a subclass of this via the [java.util.ServiceLoader] mechanism to be able to whitelist types for * serialisation that you cannot otherwise annotate. The name of the class must appear in a text file on the * classpath under the path META-INF/services/net.corda.core.serialization.SerializationWhitelist */ -@KeepForDJVM interface SerializationWhitelist { /** * Optionally whitelist types for use in object serialization, as we lock down the types that can be serialized. diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt index b8083c6727..d1cb05f8dd 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt @@ -480,7 +480,12 @@ interface AttachmentsClassLoaderCache { fun computeIfAbsent(key: AttachmentsClassLoaderKey, mappingFunction: Function): SerializationContext } +<<<<<<< HEAD class AttachmentsClassLoaderCacheImpl(cacheFactory: NamedCacheFactory, override val rotatedKeys: RotatedKeys = CordaRotatedKeys.keys) : SingletonSerializeAsToken(), AttachmentsClassLoaderCache { +======= +class AttachmentsClassLoaderCacheImpl(cacheFactory: NamedCacheFactory) : SingletonSerializeAsToken(), AttachmentsClassLoaderCache { + +>>>>>>> release/os/4.11 private class ToBeClosed( serializationContext: SerializationContext, val classLoaderToClose: AutoCloseable, diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/CheckpointSerializationAPI.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/CheckpointSerializationAPI.kt index 510986141c..5e49813957 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/CheckpointSerializationAPI.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/CheckpointSerializationAPI.kt @@ -1,21 +1,16 @@ package net.corda.core.serialization.internal -import net.corda.core.DeleteForDJVM import net.corda.core.DoNotImplement -import net.corda.core.KeepForDJVM import net.corda.core.serialization.* import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.sequence import java.io.NotSerializableException - object CheckpointSerializationDefaults { - @DeleteForDJVM val CHECKPOINT_CONTEXT get() = effectiveSerializationEnv.checkpointContext val CHECKPOINT_SERIALIZER get() = effectiveSerializationEnv.checkpointSerializer } -@KeepForDJVM @DoNotImplement interface CheckpointSerializer { @Throws(NotSerializableException::class) @@ -28,7 +23,6 @@ interface CheckpointSerializer { /** * Parameters to checkpoint serialization and deserialization. */ -@KeepForDJVM @DoNotImplement interface CheckpointSerializationContext { /** diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/CustomSerializationSchemeUtils.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/CustomSerializationSchemeUtils.kt index b0588755aa..39e4f15e8c 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/CustomSerializationSchemeUtils.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/CustomSerializationSchemeUtils.kt @@ -1,13 +1,11 @@ package net.corda.core.serialization.internal -import net.corda.core.KeepForDJVM import net.corda.core.serialization.SerializationMagic import net.corda.core.utilities.ByteSequence import java.nio.ByteBuffer class CustomSerializationSchemeUtils { - @KeepForDJVM companion object { private const val SERIALIZATION_SCHEME_ID_SIZE = 4 diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/MissingSerializerException.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/MissingSerializerException.kt index 600f4d77cc..7d9a1c8512 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/MissingSerializerException.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/MissingSerializerException.kt @@ -1,13 +1,11 @@ package net.corda.core.serialization.internal -import net.corda.core.KeepForDJVM import java.io.NotSerializableException /** * Thrown by the serialization framework, probably indicating that a custom serializer * needs to be included in a transaction. */ -@KeepForDJVM open class MissingSerializerException private constructor( message: String, val typeDescriptor: String?, diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/SerializationEnvironment.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/SerializationEnvironment.kt index f306e84a08..540a21c3b2 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/SerializationEnvironment.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/SerializationEnvironment.kt @@ -1,14 +1,11 @@ -@file:KeepForDJVM package net.corda.core.serialization.internal -import net.corda.core.KeepForDJVM import net.corda.core.internal.InheritableThreadLocalToggleField import net.corda.core.internal.SimpleToggleField import net.corda.core.internal.ThreadLocalToggleField import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationFactory -@KeepForDJVM interface SerializationEnvironment { companion object { @@ -43,7 +40,6 @@ interface SerializationEnvironment { val checkpointContext: CheckpointSerializationContext } -@KeepForDJVM private class SerializationEnvironmentImpl( override val serializationFactory: SerializationFactory, override val p2pContext: SerializationContext, diff --git a/core/src/main/kotlin/net/corda/core/transactions/BaseTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/BaseTransaction.kt index 80494a8706..23b986b721 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/BaseTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/BaseTransaction.kt @@ -1,7 +1,6 @@ package net.corda.core.transactions import net.corda.core.DoNotImplement -import net.corda.core.KeepForDJVM import net.corda.core.contracts.* import net.corda.core.identity.Party import net.corda.core.internal.castIfPossible @@ -12,7 +11,6 @@ import java.util.function.Predicate /** * An abstract class defining fields shared by all transaction types in the system. */ -@KeepForDJVM @DoNotImplement abstract class BaseTransaction : NamedByHash { /** A list of reusable reference data states which can be referred to by other contracts in this transaction. */ diff --git a/core/src/main/kotlin/net/corda/core/transactions/ContractUpgradeTransactions.kt b/core/src/main/kotlin/net/corda/core/transactions/ContractUpgradeTransactions.kt index 911c67d0da..145da6a07c 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/ContractUpgradeTransactions.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/ContractUpgradeTransactions.kt @@ -1,7 +1,6 @@ package net.corda.core.transactions import net.corda.core.CordaInternal -import net.corda.core.KeepForDJVM import net.corda.core.contracts.* import net.corda.core.crypto.DigestService import net.corda.core.crypto.SecureHash @@ -32,7 +31,6 @@ import java.security.PublicKey // TODO: check transaction size is within limits /** A special transaction for upgrading the contract of a state. */ -@KeepForDJVM @CordaSerializable data class ContractUpgradeWireTransaction( /** @@ -200,7 +198,6 @@ data class ContractUpgradeWireTransaction( * is no flexibility on what parts of the transaction to reveal – the inputs, notary and network parameters hash fields are always visible and the * rest of the transaction is always hidden. Its only purpose is to hide transaction data when using a non-validating notary. */ -@KeepForDJVM @CordaSerializable data class ContractUpgradeFilteredTransaction( /** Transaction components that are exposed. */ @@ -269,7 +266,6 @@ data class ContractUpgradeFilteredTransaction( * In contrast with a regular transaction, signatures are checked against the signers specified by input states' * *participants* fields, so full resolution is needed for signature verification. */ -@KeepForDJVM class ContractUpgradeLedgerTransaction private constructor( override val inputs: List>, diff --git a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt index 097aa4cf96..51cfcb4867 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt @@ -1,8 +1,6 @@ package net.corda.core.transactions import net.corda.core.CordaInternal -import net.corda.core.KeepForDJVM -import net.corda.core.StubOutForDJVM import net.corda.core.contracts.Attachment import net.corda.core.contracts.Command import net.corda.core.contracts.CommandData @@ -64,7 +62,6 @@ import java.util.function.Supplier * [LedgerTransaction]s should never be instantiated directly from client code, but rather via WireTransaction.toLedgerTransaction */ @Suppress("LongParameterList") -@KeepForDJVM class LedgerTransaction private constructor( // DOCSTART 1 @@ -130,7 +127,6 @@ private constructor( networkParameters, references, componentGroups, serializedInputs, serializedReferences, isAttachmentTrusted, verifierFactory, attachmentsClassLoaderCache, DigestService.sha2_256) - @KeepForDJVM companion object { private val logger = contextLogger() @@ -188,8 +184,8 @@ private constructor( /** * This factory function will create an instance of [LedgerTransaction] - * that will be used for contract verification. See [BasicVerifier] and - * [DeterministicVerifier][net.corda.node.internal.djvm.DeterministicVerifier]. + * that will be used for contract verification. + * @see BasicVerifier */ @CordaInternal fun createForContractVerify( @@ -329,18 +325,13 @@ private constructor( logger.warn("Network parameters on the LedgerTransaction with id: $id are null. Please don't use deprecated constructors of the LedgerTransaction. " + "Use WireTransaction.toLedgerTransaction instead. The result of the verify method would not be accurate.") // Roll the dice - we're probably in flow context if we got here at all, which means we can fish the current params out. - try { - params = getParamsFromFlowLogic() - } catch (e: UnsupportedOperationException) { - // Inside DJVM, ignore. - } + params = getParamsFromFlowLogic() if (params == null) throw UnsupportedOperationException("Cannot verify a LedgerTransaction created using deprecated constructors outside of flow context.") } return params } - @StubOutForDJVM private fun getParamsFromFlowLogic(): NetworkParameters? { return FlowLogic.currentTopLevel?.serviceHub?.networkParameters } @@ -404,7 +395,6 @@ private constructor( * be used to simplify this logic. */ // DOCSTART 3 - @KeepForDJVM data class InOutGroup(val inputs: List, val outputs: List, val groupingKey: K) // DOCEND 3 diff --git a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt index e5076d4d26..897598335b 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt @@ -1,7 +1,6 @@ package net.corda.core.transactions import net.corda.core.CordaException -import net.corda.core.KeepForDJVM import net.corda.core.contracts.* import net.corda.core.contracts.ComponentGroupEnum.* import net.corda.core.crypto.* @@ -90,7 +89,6 @@ abstract class TraversableTransaction(open val componentGroups: List, @@ -361,7 +358,6 @@ data class FilteredComponentGroup(override val groupIndex: Int, * @param id transaction's id. * @param reason information about the exception. */ -@KeepForDJVM @CordaSerializable class ComponentVisibilityException(val id: SecureHash, val reason: String) : CordaException("Component visibility error for transaction with id:$id. Reason: $reason") @@ -369,16 +365,13 @@ class ComponentVisibilityException(val id: SecureHash, val reason: String) : Cor * @param id transaction's id. * @param reason information about the exception. */ -@KeepForDJVM @CordaSerializable class FilteredTransactionVerificationException(val id: SecureHash, val reason: String) : CordaException("Transaction with id:$id cannot be verified. Reason: $reason") /** Wrapper over [StateRef] to be used when filtering reference states. */ -@KeepForDJVM @CordaSerializable data class ReferenceStateRef(val stateRef: StateRef) /** Wrapper over [SecureHash] to be used when filtering network parameters hash. */ -@KeepForDJVM @CordaSerializable -data class NetworkParametersHash(val hash: SecureHash) \ No newline at end of file +data class NetworkParametersHash(val hash: SecureHash) diff --git a/core/src/main/kotlin/net/corda/core/transactions/MissingContractAttachments.kt b/core/src/main/kotlin/net/corda/core/transactions/MissingContractAttachments.kt index ddcd01728f..e2debdcd1a 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/MissingContractAttachments.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/MissingContractAttachments.kt @@ -1,6 +1,5 @@ package net.corda.core.transactions -import net.corda.core.KeepForDJVM import net.corda.core.contracts.ContractState import net.corda.core.contracts.TransactionState import net.corda.core.flows.FlowException @@ -13,7 +12,6 @@ import net.corda.core.serialization.CordaSerializable * @property states States which have contracts that do not have corresponding attachments in the attachment store. */ @CordaSerializable -@KeepForDJVM class MissingContractAttachments @JvmOverloads constructor(val states: List>, contractsClassName: String? = null, minimumRequiredContractClassVersion: Version? = null) : FlowException( diff --git a/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt b/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt index d9fdee9173..ab42260f37 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt @@ -1,8 +1,6 @@ package net.corda.core.transactions import net.corda.core.CordaInternal -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.contracts.* import net.corda.core.crypto.DigestService import net.corda.core.crypto.SecureHash @@ -27,7 +25,6 @@ import java.security.PublicKey * on the fly. */ @CordaSerializable -@KeepForDJVM data class NotaryChangeWireTransaction( /** * Contains all of the transaction components in serialized form. @@ -90,7 +87,6 @@ data class NotaryChangeWireTransaction( } /** Resolves input states and network parameters and builds a [NotaryChangeLedgerTransaction]. */ - @DeleteForDJVM fun resolve(services: ServicesForResolution, sigs: List): NotaryChangeLedgerTransaction { val resolvedInputs = services.loadStates(inputs.toSet()).toList() val hashToResolve = networkParametersHash ?: services.networkParametersService.defaultHash @@ -100,7 +96,6 @@ data class NotaryChangeWireTransaction( } /** Resolves input states and builds a [NotaryChangeLedgerTransaction]. */ - @DeleteForDJVM fun resolve(services: ServiceHub, sigs: List) = resolve(services as ServicesForResolution, sigs) /** @@ -134,7 +129,6 @@ data class NotaryChangeWireTransaction( * signatures are checked against the signers specified by input states' *participants* fields, so full resolution is * needed for signature verification. */ -@KeepForDJVM class NotaryChangeLedgerTransaction private constructor( override val inputs: List>, diff --git a/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt index ec1067d815..94e2079967 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt @@ -2,8 +2,6 @@ package net.corda.core.transactions import net.corda.core.CordaException import net.corda.core.CordaThrowable -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.contracts.* import net.corda.core.crypto.* import net.corda.core.identity.Party @@ -40,7 +38,6 @@ import java.util.function.Predicate * sign. */ // DOCSTART 1 -@KeepForDJVM @CordaSerializable data class SignedTransaction(val txBits: SerializedBytes, override val sigs: List @@ -98,7 +95,6 @@ data class SignedTransaction(val txBits: SerializedBytes, return descriptions } - @DeleteForDJVM @VisibleForTesting fun withAdditionalSignature(keyPair: KeyPair, signatureMetadata: SignatureMetadata): SignedTransaction { val signableData = SignableData(tx.id, signatureMetadata) @@ -144,7 +140,6 @@ data class SignedTransaction(val txBits: SerializedBytes, * @throws SignaturesMissingException if any signatures that should have been present are missing. */ @JvmOverloads - @DeleteForDJVM @Throws(SignatureException::class, AttachmentResolutionException::class, TransactionResolutionException::class) fun toLedgerTransaction(services: ServiceHub, checkSufficientSignatures: Boolean = true): LedgerTransaction { // TODO: We could probably optimise the below by @@ -175,7 +170,6 @@ data class SignedTransaction(val txBits: SerializedBytes, * @throws SignaturesMissingException if any signatures that should have been present are missing. */ @JvmOverloads - @DeleteForDJVM @Throws(SignatureException::class, AttachmentResolutionException::class, TransactionResolutionException::class, TransactionVerificationException::class) fun verify(services: ServiceHub, checkSufficientSignatures: Boolean = true) { resolveAndCheckNetworkParameters(services) @@ -186,7 +180,7 @@ data class SignedTransaction(val txBits: SerializedBytes, } } - @DeleteForDJVM + @Suppress("ThrowsCount") private fun resolveAndCheckNetworkParameters(services: ServiceHub) { val hashOrDefault = networkParametersHash ?: services.networkParametersService.defaultHash val txNetworkParameters = services.networkParametersService.lookup(hashOrDefault) @@ -203,7 +197,6 @@ data class SignedTransaction(val txBits: SerializedBytes, } /** No contract code is run when verifying notary change transactions, it is sufficient to check invariants during initialisation. */ - @DeleteForDJVM private fun verifyNotaryChangeTransaction(services: ServiceHub, checkSufficientSignatures: Boolean) { val ntx = resolveNotaryChangeTransaction(services) if (checkSufficientSignatures) ntx.verifyRequiredSignatures() @@ -211,7 +204,6 @@ data class SignedTransaction(val txBits: SerializedBytes, } /** No contract code is run when verifying contract upgrade transactions, it is sufficient to check invariants during initialisation. */ - @DeleteForDJVM private fun verifyContractUpgradeTransaction(services: ServicesForResolution, checkSufficientSignatures: Boolean) { val ctx = resolveContractUpgradeTransaction(services) if (checkSufficientSignatures) ctx.verifyRequiredSignatures() @@ -221,7 +213,6 @@ data class SignedTransaction(val txBits: SerializedBytes, // TODO: Verify contract constraints here as well as in LedgerTransaction to ensure that anything being deserialised // from the attachment is trusted. This will require some partial serialisation work to not load the ContractState // objects from the TransactionState. - @DeleteForDJVM private fun verifyRegularTransaction(services: ServiceHub, checkSufficientSignatures: Boolean) { val ltx = toLedgerTransaction(services, checkSufficientSignatures) try { @@ -251,7 +242,6 @@ data class SignedTransaction(val txBits: SerializedBytes, } } - @DeleteForDJVM @Suppress("ThrowsCount") private fun retryVerification(cause: Throwable?, ex: Throwable, ltx: LedgerTransaction, services: ServiceHub) { when (cause) { @@ -276,7 +266,6 @@ data class SignedTransaction(val txBits: SerializedBytes, // Transactions created before Corda 4 can be missing dependencies on other CorDapps. // This code has detected a missing custom serializer - probably located inside a workflow CorDapp. // We need to extract this CorDapp from AttachmentStorage and try verifying this transaction again. - @DeleteForDJVM private fun reverifyWithFixups(ltx: LedgerTransaction, services: ServiceHub, missingClass: String?) { log.warn("""Detected that transaction $id does not contain all cordapp dependencies. |This may be the result of a bug in a previous version of Corda. @@ -292,7 +281,6 @@ data class SignedTransaction(val txBits: SerializedBytes, * Resolves the underlying base transaction and then returns it, handling any special case transactions such as * [NotaryChangeWireTransaction]. */ - @DeleteForDJVM fun resolveBaseTransaction(servicesForResolution: ServicesForResolution): BaseTransaction { return when (coreTransaction) { is NotaryChangeWireTransaction -> resolveNotaryChangeTransaction(servicesForResolution) @@ -307,7 +295,6 @@ data class SignedTransaction(val txBits: SerializedBytes, * Resolves the underlying transaction with signatures and then returns it, handling any special case transactions * such as [NotaryChangeWireTransaction]. */ - @DeleteForDJVM fun resolveTransactionWithSignatures(services: ServicesForResolution): TransactionWithSignatures { return when (coreTransaction) { is NotaryChangeWireTransaction -> resolveNotaryChangeTransaction(services) @@ -322,7 +309,6 @@ data class SignedTransaction(val txBits: SerializedBytes, * If [transaction] is a [NotaryChangeWireTransaction], loads the input states and resolves it to a * [NotaryChangeLedgerTransaction] so the signatures can be verified. */ - @DeleteForDJVM fun resolveNotaryChangeTransaction(services: ServicesForResolution): NotaryChangeLedgerTransaction { val ntx = coreTransaction as? NotaryChangeWireTransaction ?: throw IllegalStateException("Expected a ${NotaryChangeWireTransaction::class.simpleName} but found ${coreTransaction::class.simpleName}") @@ -333,14 +319,12 @@ data class SignedTransaction(val txBits: SerializedBytes, * If [transaction] is a [NotaryChangeWireTransaction], loads the input states and resolves it to a * [NotaryChangeLedgerTransaction] so the signatures can be verified. */ - @DeleteForDJVM fun resolveNotaryChangeTransaction(services: ServiceHub) = resolveNotaryChangeTransaction(services as ServicesForResolution) /** * If [coreTransaction] is a [ContractUpgradeWireTransaction], loads the input states and resolves it to a * [ContractUpgradeLedgerTransaction] so the signatures can be verified. */ - @DeleteForDJVM fun resolveContractUpgradeTransaction(services: ServicesForResolution): ContractUpgradeLedgerTransaction { val ctx = coreTransaction as? ContractUpgradeWireTransaction ?: throw IllegalStateException("Expected a ${ContractUpgradeWireTransaction::class.simpleName} but found ${coreTransaction::class.simpleName}") @@ -359,7 +343,6 @@ data class SignedTransaction(val txBits: SerializedBytes, private val log = contextLogger() } - @KeepForDJVM class SignaturesMissingException(val missing: Set, val descriptions: List, override val id: SecureHash) : NamedByHash, SignatureException(missingSignatureMsg(missing, descriptions, id)), CordaThrowable by CordaException(missingSignatureMsg(missing, descriptions, id)) diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index daeb6afa0d..75a09efbe0 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -3,7 +3,6 @@ package net.corda.core.transactions import co.paralleluniverse.strands.Strand import net.corda.core.CordaInternal -import net.corda.core.DeleteForDJVM import net.corda.core.contracts.* import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.SignableData @@ -48,7 +47,6 @@ import kotlin.reflect.KClass * When this is set to a non-null value, an output state can be added by just passing in a [ContractState] – a * [TransactionState] with this notary specified will be generated automatically. */ -@DeleteForDJVM open class TransactionBuilder( var notary: Party? = null, var lockId: UUID = defaultLockId(), diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionWithSignatures.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionWithSignatures.kt index 74b2a2760a..72c027b9ff 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionWithSignatures.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionWithSignatures.kt @@ -1,7 +1,6 @@ package net.corda.core.transactions import net.corda.core.DoNotImplement -import net.corda.core.KeepForDJVM import net.corda.core.contracts.NamedByHash import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.isFulfilledBy @@ -13,7 +12,6 @@ import java.security.SignatureException import java.util.* /** An interface for transactions containing signatures, with logic for signature verification. */ -@KeepForDJVM @DoNotImplement interface TransactionWithSignatures : NamedByHash { /** diff --git a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt index 6c33af9cc1..c6eb410ad5 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt @@ -1,8 +1,6 @@ package net.corda.core.transactions import net.corda.core.CordaInternal -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.contracts.* import net.corda.core.contracts.ComponentGroupEnum.COMMANDS_GROUP import net.corda.core.contracts.ComponentGroupEnum.OUTPUTS_GROUP @@ -60,9 +58,7 @@ import java.util.function.Predicate *

    */ @CordaSerializable -@KeepForDJVM class WireTransaction(componentGroups: List, val privacySalt: PrivacySalt, digestService: DigestService) : TraversableTransaction(componentGroups, digestService) { - @DeleteForDJVM constructor(componentGroups: List) : this(componentGroups, PrivacySalt()) /** @@ -73,7 +69,6 @@ class WireTransaction(componentGroups: List, val privacySalt: Pr @Deprecated("Required only in some unit-tests and for backwards compatibility purposes.", ReplaceWith("WireTransaction(val componentGroups: List, override val privacySalt: PrivacySalt)"), DeprecationLevel.WARNING) - @DeleteForDJVM @JvmOverloads constructor( inputs: List, @@ -117,7 +112,6 @@ class WireTransaction(componentGroups: List, val privacySalt: Pr * @throws TransactionResolutionException if an input points to a transaction not found in storage. */ @Throws(AttachmentResolutionException::class, TransactionResolutionException::class) - @DeleteForDJVM fun toLedgerTransaction(services: ServicesForResolution): LedgerTransaction { return services.specialise( toLedgerTransactionInternal( @@ -136,7 +130,6 @@ class WireTransaction(componentGroups: List, val privacySalt: Pr } // Helper for deprecated toLedgerTransaction - // TODO: revisit once Deterministic JVM code updated @Suppress("UNUSED") // not sure if this field can be removed safely?? private val missingAttachment: Attachment by lazy { object : AbstractAttachment({ byteArrayOf() }, DEPLOYED_CORDAPP_UPLOADER ) { @@ -171,23 +164,6 @@ class WireTransaction(componentGroups: List, val privacySalt: Pr ) } - // Especially crafted for TransactionVerificationRequest - @CordaInternal - internal fun toLtxDjvmInternalBridge( - resolveAttachment: (SecureHash) -> Attachment?, - resolveStateRef: (StateRef) -> TransactionState<*>?, - resolveParameters: (SecureHash?) -> NetworkParameters? - ): LedgerTransaction { - return toLedgerTransactionInternal( - { null }, - resolveAttachment, - { stateRef -> resolveStateRef(stateRef)?.serialize() }, - resolveParameters, - { true }, // Any attachment loaded through the DJVM should be trusted - null - ) - } - @Suppress("LongParameterList", "ThrowsCount") private fun toLedgerTransactionInternal( resolveIdentity: (PublicKey) -> Party?, @@ -408,7 +384,6 @@ class WireTransaction(componentGroups: List, val privacySalt: Pr } } - @DeleteForDJVM override fun toString(): String { val buf = StringBuilder() buf.appendln("Transaction:") diff --git a/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt b/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt index 4d4083b0ec..67643c5998 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt @@ -1,9 +1,6 @@ @file:JvmName("ByteArrays") -@file:KeepForDJVM - package net.corda.core.utilities -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializable import java.io.ByteArrayInputStream import java.io.OutputStream @@ -19,7 +16,6 @@ import java.nio.ByteBuffer * @property offset The start position of the sequence within the byte array. * @property size The number of bytes this sequence represents. */ -@KeepForDJVM sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val size: Int) : Comparable { /** * The underlying bytes. Some implementations may choose to make a copy of the underlying [ByteArray] for @@ -112,6 +108,7 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si if (this === other) return true if (other !is ByteSequence) return false if (this.size != other.size) return false + if (this.hashCode() != other.hashCode()) return false return subArraysEqual(this._bytes, this.offset, this.size, other._bytes, other.offset) } @@ -125,13 +122,18 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si return true } + private var _hashCode: Int = 0; + override fun hashCode(): Int { - val thisBytes = _bytes - var result = 1 - for (index in offset until (offset + size)) { - result = 31 * result + thisBytes[index] - } - return result + return if (_hashCode == 0) { + val thisBytes = _bytes + var result = 1 + for (index in offset until (offset + size)) { + result = 31 * result + thisBytes[index] + } + _hashCode = result + result + } else _hashCode } override fun toString(): String = "[${copyBytes().toHexString()}]" @@ -142,7 +144,6 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si * In an ideal JVM this would be a value type and be completely overhead free. Project Valhalla is adding such * functionality to Java, but it won't arrive for a few years yet! */ -@KeepForDJVM @CordaSerializable open class OpaqueBytes(bytes: ByteArray) : ByteSequence(bytes, 0, bytes.size) { companion object { @@ -236,7 +237,6 @@ private fun parseHexBinary(s: String): ByteArray { /** * Class is public for serialization purposes. */ -@KeepForDJVM class OpaqueBytesSubSequence(override val bytes: ByteArray, offset: Int, size: Int) : ByteSequence(bytes, offset, size) { init { require(offset >= 0 && offset < bytes.size) { "Offset must be greater than or equal to 0, and less than the size of the backing array" } diff --git a/core/src/main/kotlin/net/corda/core/utilities/EncodingUtils.kt b/core/src/main/kotlin/net/corda/core/utilities/EncodingUtils.kt index dc47a1c22f..65aa987704 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/EncodingUtils.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/EncodingUtils.kt @@ -1,15 +1,13 @@ @file:JvmName("EncodingUtils") -@file:KeepForDJVM package net.corda.core.utilities -import net.corda.core.KeepForDJVM import net.corda.core.crypto.Base58 import net.corda.core.crypto.Crypto import net.corda.core.internal.hash import java.nio.charset.Charset import java.security.PublicKey -import java.util.* +import java.util.Base64 // This file includes useful encoding methods and extension functions for the most common encoding/decoding operations. @@ -81,7 +79,7 @@ fun String.hexToBase64(): String = hexToByteArray().toBase64() fun parsePublicKeyBase58(base58String: String): PublicKey = Crypto.decodePublicKey(base58String.base58ToByteArray()) /** Return the Base58 representation of the serialised public key. */ -fun PublicKey.toBase58String(): String = this.encoded.toBase58() +fun PublicKey.toBase58String(): String = Crypto.encodePublicKey(this).toBase58() /** Return the bytes of the SHA-256 output for this public key. */ fun PublicKey.toSHA256Bytes(): ByteArray = this.hash.bytes diff --git a/core/src/main/kotlin/net/corda/core/utilities/Id.kt b/core/src/main/kotlin/net/corda/core/utilities/Id.kt index a68bfb2d49..5020a08700 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/Id.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/Id.kt @@ -1,6 +1,5 @@ package net.corda.core.utilities -import net.corda.core.DeleteForDJVM import java.time.Instant import java.time.Instant.now @@ -16,7 +15,6 @@ open class Id(val value: VALUE, val entityType: String?, val ti /** * Creates an id using [Instant.now] as timestamp. */ - @DeleteForDJVM @JvmStatic fun newInstance(value: V, entityType: String? = null, timestamp: Instant = now()) = Id(value, entityType, timestamp) } diff --git a/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt b/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt index a274085e63..f1299b61e5 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt @@ -1,9 +1,5 @@ -@file:KeepForDJVM - package net.corda.core.utilities -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.internal.concurrent.get import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.CordaSerializable @@ -132,7 +128,6 @@ private class TransientProperty internal constructor(private val initiali fun Collection.toNonEmptySet(): NonEmptySet = NonEmptySet.copyOf(this) /** Same as [Future.get] except that the [ExecutionException] is unwrapped. */ -@DeleteForDJVM fun Future.getOrThrow(timeout: Duration? = null): V = try { get(timeout) } catch (e: ExecutionException) { diff --git a/core/src/main/kotlin/net/corda/core/utilities/NonEmptySet.kt b/core/src/main/kotlin/net/corda/core/utilities/NonEmptySet.kt index 06fb7834d6..ab5f0b86d7 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/NonEmptySet.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/NonEmptySet.kt @@ -1,6 +1,5 @@ package net.corda.core.utilities -import net.corda.core.KeepForDJVM import java.util.* import java.util.function.Consumer import java.util.stream.Stream @@ -8,7 +7,6 @@ import java.util.stream.Stream /** * An immutable ordered non-empty set. */ -@KeepForDJVM class NonEmptySet private constructor(private val elements: Set) : Set by elements { companion object { /** diff --git a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt index 96a43e80e7..5532ba1f61 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt @@ -1,6 +1,5 @@ package net.corda.core.utilities -import net.corda.core.DeleteForDJVM import net.corda.core.internal.STRUCTURAL_STEP_PREFIX import net.corda.core.internal.warnOnce import net.corda.core.serialization.CordaSerializable @@ -32,7 +31,6 @@ import java.util.* * using the [Observable] subscribeOn call. */ @CordaSerializable -@DeleteForDJVM class ProgressTracker(vararg inputSteps: Step) { private companion object { @@ -40,7 +38,6 @@ class ProgressTracker(vararg inputSteps: Step) { } @CordaSerializable - @DeleteForDJVM sealed class Change(val progressTracker: ProgressTracker) { data class Position(val tracker: ProgressTracker, val newStep: Step) : Change(tracker) { override fun toString() = newStep.label @@ -87,17 +84,14 @@ class ProgressTracker(vararg inputSteps: Step) { } // Sentinel objects. Overrides equals() to survive process restarts and serialization. - @DeleteForDJVM object UNSTARTED : Step("Unstarted") { override fun equals(other: Any?) = other === UNSTARTED } - @DeleteForDJVM object STARTING : Step("Starting") { override fun equals(other: Any?) = other === STARTING } - @DeleteForDJVM object DONE : Step("Done") { override fun equals(other: Any?) = other === DONE } diff --git a/core/src/main/kotlin/net/corda/core/utilities/SgxSupport.kt b/core/src/main/kotlin/net/corda/core/utilities/SgxSupport.kt index 82f805defc..b4691cb1e0 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/SgxSupport.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/SgxSupport.kt @@ -1,8 +1,5 @@ package net.corda.core.utilities -import net.corda.core.DeleteForDJVM - -@DeleteForDJVM object SgxSupport { @JvmStatic val isInsideEnclave: Boolean by lazy { diff --git a/core/src/main/kotlin/net/corda/core/utilities/Try.kt b/core/src/main/kotlin/net/corda/core/utilities/Try.kt index 98fc5c1764..f492f9af8f 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/Try.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/Try.kt @@ -1,6 +1,5 @@ package net.corda.core.utilities -import net.corda.core.KeepForDJVM import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.CordaSerializable import java.util.function.Consumer @@ -85,7 +84,6 @@ sealed class Try { return this } - @KeepForDJVM data class Success(val value: A) : Try
    () { override val isSuccess: Boolean get() = true override val isFailure: Boolean get() = false @@ -93,7 +91,6 @@ sealed class Try { override fun toString(): String = "Success($value)" } - @KeepForDJVM data class Failure(val exception: Throwable) : Try() { override val isSuccess: Boolean get() = false override val isFailure: Boolean get() = true diff --git a/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt b/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt index 94fcb2a4cf..272b5ec200 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt @@ -1,8 +1,6 @@ -@file:KeepForDJVM package net.corda.core.utilities import co.paralleluniverse.fibers.Suspendable -import net.corda.core.KeepForDJVM import net.corda.core.flows.FlowException import java.io.Serializable @@ -17,13 +15,11 @@ import java.io.Serializable * - Are any objects *reachable* from this object mismatched or not what you expected? * - Is it suspiciously large or small? */ -@KeepForDJVM class UntrustworthyData(@PublishedApi internal val fromUntrustedWorld: T) { @Suspendable @Throws(FlowException::class) fun unwrap(validator: Validator) = validator.validate(fromUntrustedWorld) - @KeepForDJVM @FunctionalInterface interface Validator : Serializable { @Suspendable diff --git a/core/src/main/kotlin/net/corda/core/utilities/UuidGenerator.kt b/core/src/main/kotlin/net/corda/core/utilities/UuidGenerator.kt index 72beb0d624..e851dae583 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/UuidGenerator.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/UuidGenerator.kt @@ -1,9 +1,7 @@ package net.corda.core.utilities -import net.corda.core.DeleteForDJVM import java.util.* -@DeleteForDJVM class UuidGenerator { companion object { diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 046c21e008..a3bfde2f82 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -467,7 +467,6 @@ ForbiddenComment:WebServerConfig.kt$WebServerConfig$// TODO: remove this once config format is updated ForbiddenComment:WebServerConfig.kt$WebServerConfig$// TODO: replace with credentials supplied by a user ForbiddenComment:WireTransaction.kt$WireTransaction$// TODO: prevent notary field from being set if there are no inputs and no time-window. - ForbiddenComment:WireTransaction.kt$WireTransaction$// TODO: revisit once Deterministic JVM code updated ForbiddenComment:X509Utilities.kt$CertificateType.LEGAL_IDENTITY$// TODO: Identity certs should have tight name constraints on child certificates FunctionNaming:ArtemisRpcTests.kt$ArtemisRpcTests$@Test(timeout=300_000) fun rpc_client_certificate_untrusted_to_server() FunctionNaming:ArtemisRpcTests.kt$ArtemisRpcTests$@Test(timeout=300_000) fun rpc_with_no_ssl_on_client_side_and_ssl_on_server_side() @@ -1170,7 +1169,6 @@ MatchingDeclarationName:ReceiveAllFlowTests.kt$net.corda.coretests.flows.ReceiveAllFlowTests.kt MatchingDeclarationName:ReferenceInputStateTests.kt$net.corda.coretests.transactions.ReferenceInputStateTests.kt MatchingDeclarationName:SSLHelper.kt$net.corda.nodeapi.internal.protonwrapper.netty.SSLHelper.kt - MatchingDeclarationName:SampleData.kt$net.corda.deterministic.verifier.SampleData.kt MatchingDeclarationName:SerializationHelper.kt$net.corda.networkbuilder.serialization.SerializationHelper.kt MatchingDeclarationName:SerializationHelper.kt$net.corda.serialization.internal.amqp.SerializationHelper.kt MatchingDeclarationName:Specification.kt$net.corda.common.configuration.parsing.internal.Specification.kt @@ -1388,7 +1386,6 @@ ThrowsCount:RPCServer.kt$RPCServer$private fun invokeRpc(context: RpcAuthContext, inMethodName: String, arguments: List<Any?>): Try<Any> ThrowsCount:ServicesForResolutionImpl.kt$ServicesForResolutionImpl$// We may need to recursively chase transactions if there are notary changes. fun inner(stateRef: StateRef, forContractClassName: String?): Attachment ThrowsCount:SignedNodeInfo.kt$SignedNodeInfo$// TODO Add root cert param (or TrustAnchor) to make sure all the identities belong to the same root fun verified(): NodeInfo - ThrowsCount:SignedTransaction.kt$SignedTransaction$@DeleteForDJVM private fun resolveAndCheckNetworkParameters(services: ServiceHub) ThrowsCount:SingleThreadedStateMachineManager.kt$SingleThreadedStateMachineManager$private fun getInitiatedFlowFactory(message: InitialSessionMessage): InitiatedFlowFactory<*> ThrowsCount:StringToMethodCallParser.kt$StringToMethodCallParser$ @Throws(UnparseableCallException::class) fun parse(target: T?, command: String): ParsedMethodCall ThrowsCount:StringToMethodCallParser.kt$StringToMethodCallParser$ @Throws(UnparseableCallException::class) fun parseArguments(methodNameHint: String, parameters: List<Pair<String, Type>>, args: String): Array<Any?> @@ -1968,7 +1965,6 @@ WildcardImport:DBTransactionStorage.kt$import javax.persistence.* WildcardImport:DBTransactionStorage.kt$import net.corda.core.serialization.* WildcardImport:DBTransactionStorage.kt$import net.corda.nodeapi.internal.persistence.* - WildcardImport:DeleteForDJVM.kt$import kotlin.annotation.AnnotationTarget.* WildcardImport:DemoBench.kt$import tornadofx.* WildcardImport:DemoBenchNodeInfoFilesCopier.kt$import tornadofx.* WildcardImport:DemoBenchView.kt$import tornadofx.* @@ -2325,7 +2321,6 @@ WildcardImport:SpringDriver.kt$import net.corda.testing.node.internal.* WildcardImport:StartedFlowTransition.kt$import net.corda.node.services.statemachine.* WildcardImport:StatePointerSearchTests.kt$import net.corda.core.contracts.* - WildcardImport:StubOutForDJVM.kt$import kotlin.annotation.AnnotationTarget.* WildcardImport:SubFlow.kt$import net.corda.core.flows.* WildcardImport:SwapIdentitiesFlowTests.kt$import net.corda.testing.core.* WildcardImport:TLSAuthenticationTests.kt$import javax.net.ssl.* diff --git a/detekt-plugins/src/test/kotlin/net/corda/detekt/plugins/rules/TestWithMissingTimeoutTest.kt b/detekt-plugins/src/test/kotlin/net/corda/detekt/plugins/rules/TestWithMissingTimeoutTest.kt index bfeb1ab0ca..34b767b72a 100644 --- a/detekt-plugins/src/test/kotlin/net/corda/detekt/plugins/rules/TestWithMissingTimeoutTest.kt +++ b/detekt-plugins/src/test/kotlin/net/corda/detekt/plugins/rules/TestWithMissingTimeoutTest.kt @@ -20,11 +20,8 @@ class JUnit4Test { """.trimIndent() private val junit5Code = """ - package net.corda.serialization.djvm - import net.corda.core.serialization.internal._contextSerializationEnv import net.corda.core.serialization.serialize - import net.corda.serialization.djvm.SandboxType.KOTLIN import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -33,7 +30,7 @@ class JUnit4Test { import java.util.function.Function @ExtendWith(LocalSerialization::class) - class DeserializeInstantTest : TestBase(KOTLIN) { + class DeserializeInstantTest { @Test fun `test deserializing instant`() { val instant = Instant.now() diff --git a/deterministic.gradle b/deterministic.gradle deleted file mode 100644 index 751af8bfb2..0000000000 --- a/deterministic.gradle +++ /dev/null @@ -1,36 +0,0 @@ -import static org.gradle.api.JavaVersion.VERSION_1_8 - -/* - * Gradle script plugin: Configure a module such that the Java and Kotlin - * compilers use the deterministic rt.jar instead of the full JDK rt.jar. - */ -apply plugin: 'kotlin' - -evaluationDependsOn(':jdk8u-deterministic') - -def jdk8uDeterministic = project(':jdk8u-deterministic') - -ext { - jdkTask = jdk8uDeterministic.tasks.named('assemble') - deterministic_jdk_home = jdk8uDeterministic.jdk_home - deterministic_rt_jar = jdk8uDeterministic.rt_jar -} - -tasks.withType(AbstractCompile).configureEach { - dependsOn jdkTask - - // This is a bit ugly, but Gradle isn't recognising the KotlinCompile task - // as it does the built-in JavaCompile task. - if (it.class.name.startsWith('org.jetbrains.kotlin.gradle.tasks.KotlinCompile')) { - kotlinOptions { - jdkHome = deterministic_jdk_home - jvmTarget = VERSION_1_8 - } - } -} - -tasks.withType(JavaCompile).configureEach { - options.compilerArgs << '-bootclasspath' << deterministic_rt_jar - sourceCompatibility = VERSION_1_8 - targetCompatibility = VERSION_1_8 -} diff --git a/docker/src/bash/example-mini-network.sh b/docker/src/bash/example-mini-network.sh index 85a2b450e6..f8e628a7e1 100755 --- a/docker/src/bash/example-mini-network.sh +++ b/docker/src/bash/example-mini-network.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash NODE_LIST=("dockerNode1" "dockerNode2" "dockerNode3") NETWORK_NAME=mininet -CORDAPP_VERSION="4.10-SNAPSHOT" -DOCKER_IMAGE_VERSION="corda-zulu-4.10-snapshot" +CORDAPP_VERSION="4.11-SNAPSHOT" +DOCKER_IMAGE_VERSION="corda-zulu-4.11-snapshot" mkdir cordapps rm -f cordapps/* diff --git a/docker/src/docker/DockerfileAL b/docker/src/docker/DockerfileAL index df671c8262..11f3db261b 100644 --- a/docker/src/docker/DockerfileAL +++ b/docker/src/docker/DockerfileAL @@ -1,4 +1,4 @@ -FROM amazoncorretto:8u422-al2023 +FROM amazoncorretto:8u422-al2 ## Add packages, clean cache, create dirs, create corda user and change ownership RUN yum -y install bash && \ diff --git a/experimental/quasar-hook/build.gradle b/experimental/quasar-hook/build.gradle index dfe49fa970..df09b426d7 100644 --- a/experimental/quasar-hook/build.gradle +++ b/experimental/quasar-hook/build.gradle @@ -1,7 +1,3 @@ -ext { - javaassist_version = "3.12.1.GA" -} - apply plugin: 'kotlin' apply plugin: 'idea' @@ -10,7 +6,7 @@ description 'A javaagent to allow hooking into the instrumentation by Quasar' dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - compile "javassist:javassist:$javaassist_version" + compile "org.javassist:javassist:$javaassist_version" } jar { diff --git a/finance/workflows/src/test/kotlin/net/corda/finance/flows/CompatibilityTest.kt b/finance/workflows/src/test/kotlin/net/corda/finance/flows/CompatibilityTest.kt index 27a6f4324a..626089cace 100644 --- a/finance/workflows/src/test/kotlin/net/corda/finance/flows/CompatibilityTest.kt +++ b/finance/workflows/src/test/kotlin/net/corda/finance/flows/CompatibilityTest.kt @@ -57,17 +57,50 @@ class CompatibilityTest { assertTrue(inByteArray.contentEquals(serializedBytes.bytes)) } + @Test(timeout = 300_000) + fun performanceTest() { + val inputStream = javaClass.classLoader.getResourceAsStream("compatibilityData/v3/node_transaction.dat") + assertNotNull(inputStream) + + val inByteArray: ByteArray = inputStream.readBytes() + val deserializationInput = DeserializationInput(serializerFactory) + + val bytes = SerializedBytes(inByteArray) + val transaction = deserializationInput.deserialize(bytes, SignedTransaction::class.java, SerializationDefaults.STORAGE_CONTEXT) + assertNotNull(transaction) + + val counts = 1000 + val loops = 200 + for (loop in 0 until loops) { + val start = System.nanoTime() + for (count in 0 until counts) { + val stx = deserializationInput.deserialize(bytes, SignedTransaction::class.java, SerializationDefaults.STORAGE_CONTEXT) + for (input in stx.inputs) { + assertNotNull(input) + } + for (output in stx.tx.outputs) { + assertNotNull(output) + } + for (command in stx.tx.commands) { + assertNotNull(command) + } + } + val end = System.nanoTime() + println("Time per transaction deserialize on loop $loop = ${(end - start) / counts} nanoseconds") + } + } + private fun assertSchemasMatch(original: Schema, reserialized: Schema) { if (original.toString() == reserialized.toString()) return original.types.forEach { originalType -> - val reserializedType = reserialized.types.firstOrNull { it.name == originalType.name } ?: - fail("""Schema mismatch between original and re-serialized data. Could not find reserialized schema matching: + val reserializedType = reserialized.types.firstOrNull { it.name == originalType.name } + ?: fail("""Schema mismatch between original and re-serialized data. Could not find reserialized schema matching: $originalType """) - if (originalType.toString() != reserializedType.toString()) - fail("""Schema mismatch between original and re-serialized data. Expected: + if (originalType.toString() != reserializedType.toString()) + fail("""Schema mismatch between original and re-serialized data. Expected: $originalType diff --git a/jdk8u-deterministic/.gitignore b/jdk8u-deterministic/.gitignore deleted file mode 100644 index 3216b4dc3d..0000000000 --- a/jdk8u-deterministic/.gitignore +++ /dev/null @@ -1 +0,0 @@ -jdk/ diff --git a/jdk8u-deterministic/build.gradle b/jdk8u-deterministic/build.gradle deleted file mode 100644 index 80804d15a8..0000000000 --- a/jdk8u-deterministic/build.gradle +++ /dev/null @@ -1,49 +0,0 @@ -repositories { - maven { - url "$publicArtifactURL/corda-dependencies" - } -} - -ext { - jdk_home = "$projectDir/jdk".toString() - rt_jar = "$jdk_home/jre/lib/rt.jar".toString() -} - -configurations { - jdk -} - -dependencies { - jdk "net.corda:deterministic-rt:$deterministic_rt_version:api" -} - -def copyJdk = tasks.register('copyJdk', Copy) { - outputs.dir jdk_home - - from(configurations.jdk) { - rename 'deterministic-rt-(.*).jar', 'rt.jar' - } - into "$jdk_home/jre/lib" - - doLast { - def eol = System.lineSeparator() - file("$jdk_home/release").write "JAVA_VERSION=\"1.8.0_172\"$eol" - mkdir "$jdk_home/bin" - file("$jdk_home/bin/javac").with { - write "#!/bin/sh\necho \"javac 1.8.0_172\"\n" - setExecutable true, false - return - } - } -} - -tasks.named('assemble') { - dependsOn copyJdk -} -tasks.named('jar', Jar) { - enabled = false -} - -artifacts { - jdk file: file(rt_jar), type: 'jar', builtBy: copyJdk -} diff --git a/lib/quasar.jar b/lib/quasar.jar deleted file mode 100644 index 6f1e8c2fca..0000000000 Binary files a/lib/quasar.jar and /dev/null differ diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/AesEncryption.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/AesEncryption.kt new file mode 100644 index 0000000000..f9b36ffd07 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/AesEncryption.kt @@ -0,0 +1,65 @@ +package net.corda.nodeapi.internal.crypto + +import net.corda.core.crypto.secureRandomBytes +import java.nio.ByteBuffer +import javax.crypto.Cipher +import javax.crypto.SecretKey +import javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.SecretKeySpec + +object AesEncryption { + const val KEY_SIZE_BYTES = 16 + internal const val IV_SIZE_BYTES = 12 + private const val TAG_SIZE_BYTES = 16 + private const val TAG_SIZE_BITS = TAG_SIZE_BYTES * 8 + + /** + * Generates a random 128-bit AES key. + */ + fun randomKey(): SecretKey { + return SecretKeySpec(secureRandomBytes(KEY_SIZE_BYTES), "AES") + } + + /** + * Encrypt the given [plaintext] with AES using the given [aesKey]. + * + * An optional public [additionalData] bytes can also be provided which will be authenticated alongside the ciphertext but not encrypted. + * This may be metadata for example. The same authenticated data bytes must be provided to [decrypt] to be able to decrypt the + * ciphertext. Typically these bytes are serialised alongside the ciphertext. Since it's authenticated in the ciphertext, it cannot be + * modified undetected. + */ + fun encrypt(aesKey: SecretKey, plaintext: ByteArray, additionalData: ByteArray? = null): ByteArray { + val cipher = Cipher.getInstance("AES/GCM/NoPadding") + val iv = secureRandomBytes(IV_SIZE_BYTES) // Never use the same IV with the same key! + cipher.init(Cipher.ENCRYPT_MODE, aesKey, GCMParameterSpec(TAG_SIZE_BITS, iv)) + val buffer = ByteBuffer.allocate(IV_SIZE_BYTES + plaintext.size + TAG_SIZE_BYTES) + buffer.put(iv) + if (additionalData != null) { + cipher.updateAAD(additionalData) + } + cipher.doFinal(ByteBuffer.wrap(plaintext), buffer) + return buffer.array() + } + + fun encrypt(aesKey: ByteArray, plaintext: ByteArray, additionalData: ByteArray? = null): ByteArray { + return encrypt(SecretKeySpec(aesKey, "AES"), plaintext, additionalData) + } + + /** + * Decrypt ciphertext that was encrypted with the same key using [encrypt]. + * + * If additional data was used for the encryption then it must also be provided. If doesn't match then the decryption will fail. + */ + fun decrypt(aesKey: SecretKey, ciphertext: ByteArray, additionalData: ByteArray? = null): ByteArray { + val cipher = Cipher.getInstance("AES/GCM/NoPadding") + cipher.init(Cipher.DECRYPT_MODE, aesKey, GCMParameterSpec(TAG_SIZE_BITS, ciphertext, 0, IV_SIZE_BYTES)) + if (additionalData != null) { + cipher.updateAAD(additionalData) + } + return cipher.doFinal(ciphertext, IV_SIZE_BYTES, ciphertext.size - IV_SIZE_BYTES) + } + + fun decrypt(aesKey: ByteArray, ciphertext: ByteArray, additionalData: ByteArray? = null): ByteArray { + return decrypt(SecretKeySpec(aesKey, "AES"), ciphertext, additionalData) + } +} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt index 79ae834a16..d617b7fb0f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt @@ -63,7 +63,8 @@ import java.security.cert.X509Certificate import java.time.Duration import java.time.Instant import java.time.temporal.ChronoUnit -import java.util.* +import java.util.ArrayList +import java.util.Date import javax.security.auth.x500.X500Principal import kotlin.experimental.and import kotlin.experimental.or @@ -219,7 +220,7 @@ object X509Utilities { crlIssuer: X500Name? = null): X509v3CertificateBuilder { val serial = generateCertificateSerialNumber() val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) - val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded)) + val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(Crypto.encodePublicKey(subjectPublicKey))) val role = certificateType.role val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/DatabaseTransaction.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/DatabaseTransaction.kt index 0d69706eb1..f4482c702c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/DatabaseTransaction.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/DatabaseTransaction.kt @@ -29,7 +29,10 @@ class DatabaseTransaction( val connection: Connection by lazy(LazyThreadSafetyMode.NONE) { database.dataSource.connection.apply { autoCommit = false - transactionIsolation = isolation + // only set the transaction isolation level if it's actually changed - setting isn't free. + if (transactionIsolation != isolation) { + transactionIsolation = isolation + } } } 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 e7b36efa94..11f370c0aa 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 @@ -4,9 +4,13 @@ import com.fasterxml.jackson.databind.ObjectMapper import liquibase.Contexts import liquibase.LabelExpression import liquibase.Liquibase +import liquibase.Scope +import liquibase.ThreadLocalScopeManager import liquibase.database.jvm.JdbcConnection import liquibase.exception.LiquibaseException import liquibase.resource.ClassLoaderResourceAccessor +import liquibase.resource.Resource +import liquibase.resource.URIResource import net.corda.core.identity.CordaX500Name import net.corda.core.schemas.MappedSchema import net.corda.core.utilities.contextLogger @@ -14,8 +18,10 @@ import net.corda.nodeapi.internal.MigrationHelpers.getMigrationResource import net.corda.nodeapi.internal.cordapp.CordappLoader import java.io.ByteArrayInputStream import java.io.InputStream +import java.net.URI import java.nio.file.Path import java.sql.Connection +import java.util.Collections import java.util.concurrent.locks.ReentrantLock import javax.sql.DataSource import kotlin.concurrent.withLock @@ -36,6 +42,10 @@ open class SchemaMigration( const val NODE_BASE_DIR_KEY = "liquibase.nodeDaseDir" const val NODE_X500_NAME = "liquibase.nodeName" val loader = ThreadLocal() + init { + Scope.setScopeManager(ThreadLocalScopeManager()) + } + @JvmStatic protected val mutex = ReentrantLock() } @@ -46,31 +56,31 @@ open class SchemaMigration( private val classLoader = cordappLoader?.appClassLoader ?: Thread.currentThread().contextClassLoader - /** + /** * Will run the Liquibase migration on the actual database. - * @param existingCheckpoints Whether checkpoints exist that would prohibit running a migration - * @param schemas The set of MappedSchemas to check - * @param forceThrowOnMissingMigration throws an exception if a mapped schema is missing the migration resource. Can be set to false - * when allowing hibernate to create missing schemas in dev or tests. + * @param existingCheckpoints Whether checkpoints exist that would prohibit running a migration + * @param schemas The set of MappedSchemas to check + * @param forceThrowOnMissingMigration throws an exception if a mapped schema is missing the migration resource. Can be set to false + * when allowing hibernate to create missing schemas in dev or tests. */ - fun runMigration(existingCheckpoints: Boolean, schemas: Set, forceThrowOnMissingMigration: Boolean) { - val resourcesAndSourceInfo = prepareResources(schemas, forceThrowOnMissingMigration) - - // current version of Liquibase appears to be non-threadsafe - // this is apparent when multiple in-process nodes are all running migrations simultaneously - mutex.withLock { - dataSource.connection.use { connection -> - val (runner, _, shouldBlockOnCheckpoints) = prepareRunner(connection, resourcesAndSourceInfo) - if (shouldBlockOnCheckpoints && existingCheckpoints) - throw CheckpointsException() - try { - runner.update(Contexts().toString()) - } catch (exp: LiquibaseException) { - throw DatabaseMigrationException(exp.message, exp) - } - } - } - } + fun runMigration(existingCheckpoints: Boolean, schemas: Set, forceThrowOnMissingMigration: Boolean) { + val resourcesAndSourceInfo = prepareResources(schemas, forceThrowOnMissingMigration) + Scope.enter(mapOf(Scope.Attr.classLoader.name to classLoader)) + // current version of Liquibase appears to be non-threadsafe + // this is apparent when multiple in-process nodes are all running migrations simultaneously + mutex.withLock { + dataSource.connection.use { connection -> + val (runner, _, shouldBlockOnCheckpoints) = prepareRunner(connection, resourcesAndSourceInfo) + if (shouldBlockOnCheckpoints && existingCheckpoints) + throw CheckpointsException() + try { + runner.update(Contexts().toString()) + } catch (exp: LiquibaseException) { + throw DatabaseMigrationException(exp.message, exp) + } + } + } + } /** * Ensures that the database is up to date with the latest migration changes. @@ -98,7 +108,7 @@ open class SchemaMigration( * @param forceThrowOnMissingMigration throws an exception if a mapped schema is missing the migration resource. Can be set to false * when allowing hibernate to create missing schemas in dev or tests. */ - fun getPendingChangesCount(schemas: Set, forceThrowOnMissingMigration: Boolean) : Int { + fun getPendingChangesCount(schemas: Set, forceThrowOnMissingMigration: Boolean): Int { val resourcesAndSourceInfo = prepareResources(schemas, forceThrowOnMissingMigration) // current version of Liquibase appears to be non-threadsafe @@ -140,19 +150,40 @@ open class SchemaMigration( /** Create a resource accessor that aggregates the changelogs included in the schemas into one dynamic stream. */ protected class CustomResourceAccessor(val dynamicInclude: String, val changelogList: List, classLoader: ClassLoader) : ClassLoaderResourceAccessor(classLoader) { - override fun getResourcesAsStream(path: String): Set { + override fun getAll(path: String?): List { + if (path == dynamicInclude) { - // Create a map in Liquibase format including all migration files. - val includeAllFiles = mapOf("databaseChangeLog" - to changelogList.filterNotNull().map { file -> mapOf("include" to mapOf("file" to file)) }) - - // Transform it to json. - val includeAllFilesJson = ObjectMapper().writeValueAsBytes(includeAllFiles) - // Return the json as a stream. - return setOf(ByteArrayInputStream(includeAllFilesJson)) + val resource = object : URIResource(path, URI(path)) { + override fun openInputStream(): InputStream { + return getPathAsStream() + } + } + return Collections.singletonList(resource) } - return super.getResourcesAsStream(path)?.take(1)?.toSet() ?: emptySet() + // Take 1 resource due to Liquibase find duplicate files which throws an error + return super.getAll(path).take(1) + } + + override fun get(path: String?): Resource { + if (path == dynamicInclude) { + // Return the json as a stream. + return object : URIResource(path, URI(path)) { + override fun openInputStream(): InputStream { + return getPathAsStream() + } + } + } + return super.get(path) + } + + private fun getPathAsStream(): InputStream { + // Create a map in Liquibase format including all migration files. + val includeAllFiles = mapOf("databaseChangeLog" + to changelogList.filterNotNull().map { file -> mapOf("include" to mapOf("file" to file)) }) + val includeAllFilesJson = ObjectMapper().writeValueAsBytes(includeAllFiles) + + return ByteArrayInputStream(includeAllFilesJson) } } @@ -184,6 +215,7 @@ open class SchemaMigration( if (ourName != null) { System.setProperty(NODE_X500_NAME, ourName.toString()) } + Scope.enter(mapOf(Scope.Attr.classLoader.name to classLoader)) val customResourceAccessor = CustomResourceAccessor(dynamicInclude, changelogList, classLoader) checkResourcesInClassPath(changelogList) return listOf(Pair(customResourceAccessor, "")) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/Slf4jLiquibaseLogService.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/Slf4jLiquibaseLogService.kt new file mode 100644 index 0000000000..1ec830d0e5 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/Slf4jLiquibaseLogService.kt @@ -0,0 +1,13 @@ +package net.corda.nodeapi.internal.persistence + +import liquibase.logging.core.AbstractLogService +import org.slf4j.LoggerFactory + +class Slf4jLiquibaseLogService : AbstractLogService() { + + override fun getPriority() = Integer.MAX_VALUE + override fun getLog(clazz: Class<*>?) : liquibase.logging.Logger { + return Slf4jLiquibaseLogger(LoggerFactory.getLogger(clazz)) + } + +} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/Slf4jLiquibaseLogger.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/Slf4jLiquibaseLogger.kt new file mode 100644 index 0000000000..d9afda1e38 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/Slf4jLiquibaseLogger.kt @@ -0,0 +1,74 @@ +package net.corda.nodeapi.internal.persistence + +import org.slf4j.Logger +import java.util.logging.Level + +/** + * Slf4j logger for Liquibase + * Captures logging from Liquibase that would otherwise go to stderr. + */ +class Slf4jLiquibaseLogger(private val logger: Logger) : liquibase.logging.core.AbstractLogger() { + + companion object { + private val TRACE_THRESHOLD = Level.FINEST.intValue() + private val DEBUG_THRESHOLD = Level.FINE.intValue() + private val INFO_THRESHOLD = Level.INFO.intValue() + private val WARN_THRESHOLD = Level.WARNING.intValue() + } + + override fun log(level: Level, message: String?, e: Throwable?) { + val levelValue = level.intValue() + if (levelValue <= TRACE_THRESHOLD) { + logger.trace(message, e) + } else if (levelValue <= DEBUG_THRESHOLD) { + logger.debug(message, e) + } else if (levelValue <= INFO_THRESHOLD) { + logger.info(message, e) + } else if (levelValue <= WARN_THRESHOLD) { + logger.warn(message, e) + } else { + logger.error(message, e) + } + } + + override fun severe(message: String?) { + logger.error(message) + } + + override fun severe(message: String?, e: Throwable?) { + logger.error(message, e) + } + + override fun warning(message: String?) { + logger.warn(message) + } + + override fun warning(message: String?, e: Throwable?) { + logger.warn(message, e) + } + + override fun info(message: String?) { + logger.info(message) + } + + override fun info(message: String?, e: Throwable?) { + logger.info(message, e) + } + + override fun config(message: String?) { + logger.info(message) + } + + override fun config(message: String?, e: Throwable?) { + logger.info(message, e) + } + + override fun fine(message: String?) { + logger.debug(message) + } + + override fun fine(message: String?, e: Throwable?) { + logger.debug(message, e) + } + +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt index b594954ef5..d747c23b97 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt @@ -1,13 +1,16 @@ package net.corda.nodeapi.internal.serialization.kryo -import com.esotericsoftware.kryo.* +import com.esotericsoftware.kryo.ClassResolver +import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.KryoException +import com.esotericsoftware.kryo.Registration +import com.esotericsoftware.kryo.Serializer import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer import com.esotericsoftware.kryo.serializers.FieldSerializer import com.esotericsoftware.kryo.util.MapReferenceResolver -import net.corda.core.DeleteForDJVM import net.corda.core.contracts.PrivacySalt import net.corda.core.crypto.Crypto import net.corda.core.crypto.DigestService @@ -17,7 +20,13 @@ import net.corda.core.internal.LazyMappedList import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.SerializeAsTokenContext import net.corda.core.serialization.SerializedBytes -import net.corda.core.transactions.* +import net.corda.core.transactions.ComponentGroup +import net.corda.core.transactions.ContractUpgradeFilteredTransaction +import net.corda.core.transactions.ContractUpgradeWireTransaction +import net.corda.core.transactions.CoreTransaction +import net.corda.core.transactions.NotaryChangeWireTransaction +import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.SgxSupport import net.corda.serialization.internal.serializationContextKey @@ -302,7 +311,7 @@ object PrivateKeySerializer : Serializer() { object PublicKeySerializer : Serializer() { override fun write(kryo: Kryo, output: Output, obj: PublicKey) { // TODO: Instead of encoding to the default X509 format, we could have a custom per key type (space-efficient) serialiser. - output.writeBytesWithLength(obj.encoded) + output.writeBytesWithLength(Crypto.encodePublicKey(obj)) } override fun read(kryo: Kryo, input: Input, type: Class): PublicKey { @@ -423,7 +432,7 @@ object LoggerSerializer : Serializer() { object ClassSerializer : Serializer>() { override fun read(kryo: Kryo, input: Input, type: Class>): Class<*> { val className = input.readString() - return Class.forName(className, true, kryo.classLoader) + return if (className == "void") Void.TYPE else Class.forName(className, true, kryo.classLoader) } override fun write(kryo: Kryo, output: Output, clazz: Class<*>) { @@ -464,7 +473,6 @@ fun Kryo.serializationContext(): SerializeAsTokenContext? = context.get(serializ * unmodifiable collection to [java.lang.Throwable.suppressedExceptions] which will fail some sentinel identity checks * e.g. in [java.lang.Throwable.addSuppressed] */ -@DeleteForDJVM @ThreadSafe class ThrowableSerializer(kryo: Kryo, type: Class) : Serializer(false, true) { diff --git a/node-api/src/main/resources/META-INF/services/liquibase.logging.LogService b/node-api/src/main/resources/META-INF/services/liquibase.logging.LogService new file mode 100644 index 0000000000..74002d445f --- /dev/null +++ b/node-api/src/main/resources/META-INF/services/liquibase.logging.LogService @@ -0,0 +1 @@ +net.corda.nodeapi.internal.persistence.Slf4jLiquibaseLogService \ No newline at end of file diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/AesEncryptionTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/AesEncryptionTest.kt new file mode 100644 index 0000000000..d3b1ded638 --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/AesEncryptionTest.kt @@ -0,0 +1,73 @@ +package net.corda.nodeapi.internal.crypto + +import net.corda.core.crypto.secureRandomBytes +import net.corda.nodeapi.internal.crypto.AesEncryption.IV_SIZE_BYTES +import net.corda.nodeapi.internal.crypto.AesEncryption.KEY_SIZE_BYTES +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.Test +import java.security.GeneralSecurityException + +class AesEncryptionTest { + private val aesKey = secureRandomBytes(KEY_SIZE_BYTES) + private val plaintext = secureRandomBytes(257) // Intentionally not a power of 2 + + @Test(timeout = 300_000) + fun `ciphertext can be decrypted using the same key`() { + val ciphertext = AesEncryption.encrypt(aesKey, plaintext) + assertThat(String(ciphertext)).doesNotContain(String(plaintext)) + val decrypted = AesEncryption.decrypt(aesKey, ciphertext) + assertThat(decrypted).isEqualTo(plaintext) + } + + @Test(timeout = 300_000) + fun `ciphertext with authenticated data can be decrypted using the same key`() { + val ciphertext = AesEncryption.encrypt(aesKey, plaintext, "Extra public data".toByteArray()) + assertThat(String(ciphertext)).doesNotContain(String(plaintext)) + val decrypted = AesEncryption.decrypt(aesKey, ciphertext, "Extra public data".toByteArray()) + assertThat(decrypted).isEqualTo(plaintext) + } + + @Test(timeout = 300_000) + fun `ciphertext cannot be decrypted with different authenticated data`() { + val ciphertext = AesEncryption.encrypt(aesKey, plaintext, "Extra public data".toByteArray()) + assertThat(String(ciphertext)).doesNotContain(String(plaintext)) + assertThatExceptionOfType(GeneralSecurityException::class.java).isThrownBy { + AesEncryption.decrypt(aesKey, ciphertext, "Different public data".toByteArray()) + } + } + + @Test(timeout = 300_000) + fun `ciphertext cannot be decrypted with different key`() { + val ciphertext = AesEncryption.encrypt(aesKey, plaintext) + for (index in aesKey.indices) { + aesKey[index]-- + assertThatExceptionOfType(GeneralSecurityException::class.java).isThrownBy { + AesEncryption.decrypt(aesKey, ciphertext) + } + aesKey[index]++ + } + } + + @Test(timeout = 300_000) + fun `corrupted ciphertext cannot be decrypted`() { + val ciphertext = AesEncryption.encrypt(aesKey, plaintext) + for (index in ciphertext.indices) { + ciphertext[index]-- + assertThatExceptionOfType(GeneralSecurityException::class.java).isThrownBy { + AesEncryption.decrypt(aesKey, ciphertext) + } + ciphertext[index]++ + } + } + + @Test(timeout = 300_000) + fun `encrypting same plainttext twice with same key does not produce same ciphertext`() { + val first = AesEncryption.encrypt(aesKey, plaintext) + val second = AesEncryption.encrypt(aesKey, plaintext) + // The IV should be different + assertThat(first.take(IV_SIZE_BYTES)).isNotEqualTo(second.take(IV_SIZE_BYTES)) + // Which should cause the encrypted bytes to be different as well + assertThat(first.drop(IV_SIZE_BYTES)).isNotEqualTo(second.drop(IV_SIZE_BYTES)) + } +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt index 15a98d1552..2f070a4d24 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt @@ -351,6 +351,20 @@ class KryoTests(private val compression: CordaSerializationEncoding?) { assertEquals(randomHash, exception2.requested) } + @Test(timeout=300_000) + fun `serialize - deserialize primative void`() { + val original = JavaVoidHolder() + val roundtrip = original.checkpointSerialize(context).checkpointDeserialize(context) + assertThat(roundtrip.voidClass).isEqualTo(original.voidClass) + } + + class JavaVoidHolder { + val voidClass: Class = Void.TYPE + init { + check(voidClass.name == "void") // Sanity check to make sure we're dealing with the primitive void + } + } + @Test(timeout=300_000) fun `compression has the desired effect`() { compression ?: return @@ -373,6 +387,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) { @Test(timeout=300_000) fun `compression reduces number of bytes significantly`() { + @Suppress("unused") class Holder(val holder: ByteArray) val obj = Holder(ByteArray(20000)) diff --git a/node/build.gradle b/node/build.gradle index c788acb66c..81418360f7 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -20,11 +20,6 @@ ext { jolokia_version = constants.getProperty('jolokiaAgentVersion') } -evaluationDependsOn(':core-deterministic') -evaluationDependsOn(':serialization-deterministic') -evaluationDependsOn(':serialization-djvm:deserializers') -evaluationDependsOn(':node:djvm') - //noinspection GroovyAssignabilityCheck configurations { integrationTestCompile.extendsFrom testCompile @@ -32,9 +27,6 @@ configurations { slowIntegrationTestCompile.extendsFrom testCompile slowIntegrationTestRuntimeOnly.extendsFrom testRuntimeOnly - - jdkRt - deterministic } sourceSets { @@ -150,19 +142,6 @@ dependencies { // TypeSafe Config: for simple and human friendly config files. compile "com.typesafe:config:$typesafe_config_version" - // Sandbox for deterministic contract verification - compile "net.corda.djvm:corda-djvm:$djvm_version" - compile project(':serialization-djvm') - compile(project(':node:djvm')) { - transitive = false - } - jdkRt "net.corda:deterministic-rt:$deterministic_rt_version" - deterministic project(path: ':core-deterministic', configuration: 'deterministicArtifacts') - deterministic project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts') - deterministic project(':serialization-djvm:deserializers') - deterministic project(':node:djvm') - deterministic "org.slf4j:slf4j-nop:$slf4j_version" - testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" testImplementation "junit:junit:$junit_version" @@ -251,6 +230,9 @@ dependencies { integrationTestCompile(project(":testing:cordapps:missingmigration")) testCompile project(':testing:cordapps:dbfailure:dbfworkflows') + + // used by FinalityFlowErrorHandlingTest + slowIntegrationTestCompile project(':testing:cordapps:cashobservers') } tasks.withType(JavaCompile).configureEach { @@ -262,8 +244,6 @@ tasks.withType(Test).configureEach { if (JavaVersion.current() == JavaVersion.VERSION_11) { jvmArgs '-Djdk.attach.allowAttachSelf=true' } - systemProperty 'deterministic-rt.path', configurations.jdkRt.asPath - systemProperty 'deterministic-sources.path', configurations.deterministic.asPath } tasks.register('integrationTest', Test) { @@ -281,7 +261,6 @@ tasks.register('slowIntegrationTest', Test) { // quasar exclusions upon agent code instrumentation at run-time quasar { excludeClassLoaders.addAll( - 'net.corda.djvm.**', 'net.corda.core.serialization.internal.**' ) excludePackages.addAll( @@ -292,12 +271,10 @@ quasar { "com.google.**", "com.lmax.**", "com.zaxxer.**", - "djvm**", "net.bytebuddy**", "io.github.classgraph**", "io.netty*", "liquibase**", - "net.corda.djvm**", "net.i2p.crypto.**", "nonapi.io.github.classgraph.**", "org.apiguardian.**", @@ -316,15 +293,6 @@ quasar { jar { baseName 'corda-node' - exclude 'sandbox/java/**' - exclude 'sandbox/org/**' - exclude 'sandbox/net/corda/core/crypto/SecureHash.class' - exclude 'sandbox/net/corda/core/crypto/SignatureScheme.class' - exclude 'sandbox/net/corda/core/crypto/TransactionSignature.class' - manifest { - attributes('Corda-Deterministic-Runtime': configurations.jdkRt.singleFile.name) - attributes('Corda-Deterministic-Classpath': configurations.deterministic.collect { it.name }.join(' ')) - } } publish { diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle index 2eb546be0d..8026e23ab9 100644 --- a/node/capsule/build.gradle +++ b/node/capsule/build.gradle @@ -40,8 +40,6 @@ def nodeProject = project(':node') task buildCordaJAR(type: FatCapsule, dependsOn: [ nodeProject.tasks.named('jar'), - project(':core-deterministic').tasks.named('assemble'), - project(':serialization-deterministic').tasks.named('assemble') ]) { applicationClass 'net.corda.node.Corda' archiveBaseName = 'corda' @@ -58,46 +56,18 @@ task buildCordaJAR(type: FatCapsule, dependsOn: [ from configurations.capsuleRuntime.files.collect { zipTree(it) } with jar - // The DJVM will share most of its dependencies with the node, but any extra ones that it needs - // are listed in the node's "deterministic" configuration and copied into a djvm subdirectory. - // - // Gradle may not resolve exactly the same transitive dependencies for both the runtimeClasspath - // and deterministic configurations - specifically, the artifacts' version numbers may differ slightly. - // And so we map the files by the resolved ModuleIdentifier objects instead, which just contain an - // artifact's group and name. - def cordaResolved = nodeProject.configurations['runtimeClasspath'].resolvedConfiguration.resolvedArtifacts.collectEntries { - [ (it.moduleVersion.id.module):it.file ] - } - def deterministicResolved = nodeProject.configurations['deterministic'].resolvedConfiguration.resolvedArtifacts.collectEntries { - [ (it.moduleVersion.id.module):it.file ] - } - def resolvedDifferences = deterministicResolved.keySet() - cordaResolved.keySet() - - cordaResolved.keySet().retainAll(deterministicResolved.keySet() - resolvedDifferences) - deterministicResolved.keySet().retainAll(resolvedDifferences) - manifest { - // These are the dependencies that the deterministic Corda libraries share with Corda. - attributes('Corda-DJVM-Dependencies': cordaResolved.values().collect { it.name }.join(' ')) - if (JavaVersion.current() == JavaVersion.VERSION_11) { attributes('Add-Opens': 'java.management/com.sun.jmx.mbeanserver java.base/java.lang') } - - } - - into('djvm') { - from nodeProject.configurations['jdkRt'].singleFile - from deterministicResolved.values() - fileMode = 0444 } capsuleManifest { applicationVersion = corda_release_version applicationId = "net.corda.node.Corda" // See experimental/quasar-hook/README.md for how to generate. - def quasarExcludeExpression = "x(antlr**;bftsmart**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;kotlin**;net.corda.djvm**;djvm**;net.bytebuddy**;net.i2p**;org.apache**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**;io.opentelemetry**)" - def quasarClassLoaderExclusion = "l(net.corda.djvm.**;net.corda.core.serialization.internal.**)" + def quasarExcludeExpression = "x(antlr**;bftsmart**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**;io.opentelemetry**)" + def quasarClassLoaderExclusion = "l(net.corda.core.serialization.internal.**)" javaAgents = quasar_classifier ? ["quasar-core-${quasar_version}-${quasar_classifier}.jar=${quasarExcludeExpression}${quasarClassLoaderExclusion}"] : ["quasar-core-${quasar_version}.jar=${quasarExcludeExpression}${quasarClassLoaderExclusion}"] systemProperties['visualvm.display.name'] = 'Corda' if (JavaVersion.current() == JavaVersion.VERSION_1_8) { diff --git a/node/capsule/src/main/java/CordaCaplet.java b/node/capsule/src/main/java/CordaCaplet.java index a2c73e0dd3..25e1b26d58 100644 --- a/node/capsule/src/main/java/CordaCaplet.java +++ b/node/capsule/src/main/java/CordaCaplet.java @@ -22,8 +22,6 @@ import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.util.stream.Collectors.toMap; public class CordaCaplet extends Capsule { - private static final String DJVM_DIR ="djvm"; - private Config nodeConfig = null; private String baseDir = null; @@ -90,76 +88,10 @@ public class CordaCaplet extends Capsule { return null; } - private void installDJVM() { - Path djvmDir = Paths.get(baseDir, DJVM_DIR); - if (!djvmDir.toFile().mkdir() && !Files.isDirectory(djvmDir)) { - log(LOG_VERBOSE, "DJVM directory could not be created"); - } else { - try { - Path sourceDir = appDir().resolve(DJVM_DIR); - if (Files.isDirectory(sourceDir)) { - installCordaDependenciesForDJVM(sourceDir, djvmDir); - installTransitiveDependenciesForDJVM(appDir(), djvmDir); - } - } catch (IOException e) { - log(LOG_VERBOSE, "Failed to populate directory " + djvmDir.toAbsolutePath()); - log(LOG_VERBOSE, e); - } - } - } - - private void installCordaDependenciesForDJVM(Path sourceDir, Path targetDir) throws IOException { - try (DirectoryStream directory = Files.newDirectoryStream(sourceDir, file -> Files.isRegularFile(file))) { - for (Path sourceFile : directory) { - Path targetFile = targetDir.resolve(sourceFile.getFileName()); - installFile(sourceFile, targetFile); - } - } - } - - private void installTransitiveDependenciesForDJVM(Path sourceDir, Path targetDir) throws IOException { - Manifest manifest = getManifest(); - String[] transitives = manifest.getMainAttributes().getValue("Corda-DJVM-Dependencies").split("\\s++", 0); - for (String transitive : transitives) { - Path source = sourceDir.resolve(transitive); - if (Files.isRegularFile(source)) { - installFile(source, targetDir.resolve(transitive)); - } - } - } - - private Manifest getManifest() throws IOException { - URL capsule = getClass().getProtectionDomain().getCodeSource().getLocation(); - try (JarInputStream jar = new JarInputStream(capsule.openStream())) { - return jar.getManifest(); - } - } - - private void installFile(Path source, Path target) { - try { - // Forcibly reinstall this dependency. - Files.deleteIfExists(target); - Files.createSymbolicLink(target, source); - } catch (UnsupportedOperationException | IOException e) { - copyFile(source, target); - } - } - - private void copyFile(Path source, Path target) { - try { - Files.copy(source, target, REPLACE_EXISTING); - } catch (IOException e) { - //noinspection ResultOfMethodCallIgnored - target.toFile().delete(); - log(LOG_VERBOSE, e); - } - } - @Override protected ProcessBuilder prelaunch(List jvmArgs, List args) { checkJavaVersion(); nodeConfig = parseConfigFile(args); - installDJVM(); return super.prelaunch(jvmArgs, args); } diff --git a/node/djvm/build.gradle b/node/djvm/build.gradle deleted file mode 100644 index 9c39b404d6..0000000000 --- a/node/djvm/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -apply from: '../../deterministic.gradle' -apply plugin: 'com.jfrog.artifactory' -apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'java-library' - -description 'Internal Corda Node modules for deterministic contract verification.' - -dependencies { - api 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' - api project(path: ':core-deterministic', configuration: 'deterministicArtifacts') - api project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts') -} - -jar { - archiveBaseName = 'corda-node-djvm' - archiveClassifier = '' - manifest { - attributes('Automatic-Module-Name': 'net.corda.node.djvm') - attributes('Sealed': true) - } -} - -publish { - name jar.archiveBaseName.get() -} diff --git a/node/djvm/src/main/kotlin/net/corda/node/djvm/AttachmentBuilder.kt b/node/djvm/src/main/kotlin/net/corda/node/djvm/AttachmentBuilder.kt deleted file mode 100644 index 561b5bcd76..0000000000 --- a/node/djvm/src/main/kotlin/net/corda/node/djvm/AttachmentBuilder.kt +++ /dev/null @@ -1,88 +0,0 @@ -@file:JvmName("AttachmentConstants") -package net.corda.node.djvm - -import net.corda.core.contracts.Attachment -import net.corda.core.contracts.BrokenAttachmentException -import net.corda.core.contracts.ContractAttachment -import net.corda.core.crypto.SecureHash -import net.corda.core.identity.Party -import java.io.InputStream -import java.security.PublicKey -import java.util.Collections.unmodifiableList -import java.util.function.Function - -private const val SIGNER_KEYS_IDX = 0 -private const val SIZE_IDX = 1 -private const val ID_IDX = 2 -private const val ATTACHMENT_IDX = 3 -private const val STREAMER_IDX = 4 - -private const val CONTRACT_IDX = 5 -private const val ADDITIONAL_CONTRACT_IDX = 6 -private const val UPLOADER_IDX = 7 -private const val CONTRACT_SIGNER_KEYS_IDX = 8 -private const val VERSION_IDX = 9 - -class AttachmentBuilder : Function?, List?> { - private val attachments = mutableListOf() - - private fun unmodifiable(list: List): List { - return if (list.isEmpty()) { - emptyList() - } else { - unmodifiableList(list) - } - } - - override fun apply(inputs: Array?): List? { - @Suppress("unchecked_cast") - return if (inputs == null) { - unmodifiable(attachments) - } else { - var attachment: Attachment = SandboxAttachment( - signerKeys = inputs[SIGNER_KEYS_IDX] as List, - size = inputs[SIZE_IDX] as Int, - id = inputs[ID_IDX] as SecureHash, - attachment = inputs[ATTACHMENT_IDX], - streamer = inputs[STREAMER_IDX] as Function - ) - - if (inputs.size > VERSION_IDX) { - attachment = ContractAttachment.create( - attachment = attachment, - contract = inputs[CONTRACT_IDX] as String, - additionalContracts = (inputs[ADDITIONAL_CONTRACT_IDX] as Array).toSet(), - uploader = inputs[UPLOADER_IDX] as? String, - signerKeys = inputs[CONTRACT_SIGNER_KEYS_IDX] as List, - version = inputs[VERSION_IDX] as Int - ) - } - - attachments.add(attachment) - null - } - } -} - -/** - * This represents an [Attachment] from within the sandbox. - */ -private class SandboxAttachment( - override val signerKeys: List, - override val size: Int, - override val id: SecureHash, - private val attachment: Any, - private val streamer: Function -) : Attachment { - @Suppress("OverridingDeprecatedMember") - override val signers: List = emptyList() - - @Suppress("TooGenericExceptionCaught") - override fun open(): InputStream { - return try { - streamer.apply(attachment) - } catch (e: Exception) { - throw BrokenAttachmentException(id, e.message, e) - } - } -} diff --git a/node/djvm/src/main/kotlin/net/corda/node/djvm/CommandBuilder.kt b/node/djvm/src/main/kotlin/net/corda/node/djvm/CommandBuilder.kt deleted file mode 100644 index 247fef3ec6..0000000000 --- a/node/djvm/src/main/kotlin/net/corda/node/djvm/CommandBuilder.kt +++ /dev/null @@ -1,48 +0,0 @@ -package net.corda.node.djvm - -import net.corda.core.contracts.CommandData -import net.corda.core.contracts.CommandWithParties -import net.corda.core.internal.lazyMapped -import java.security.PublicKey -import java.util.function.Function -import java.util.function.Supplier - -class CommandBuilder : Function, Supplier>>> { - @Suppress("unchecked_cast") - override fun apply(inputs: Array): Supplier>> { - val signersProvider = inputs[0] as? Supplier>> ?: Supplier(::emptyList) - val commandsDataProvider = inputs[1] as? Supplier> ?: Supplier(::emptyList) - val partialMerkleLeafIndices = inputs[2] as? IntArray - - /** - * This logic has been lovingly reproduced from [net.corda.core.internal.deserialiseCommands]. - */ - return Supplier { - val signers = signersProvider.get() - val commandsData = commandsDataProvider.get() - - if (partialMerkleLeafIndices != null) { - check(commandsData.size <= signers.size) { - "Invalid Transaction. Fewer Signers (${signers.size}) than CommandData (${commandsData.size}) objects" - } - if (partialMerkleLeafIndices.isNotEmpty()) { - check(partialMerkleLeafIndices.max()!! < signers.size) { - "Invalid Transaction. A command with no corresponding signer detected" - } - } - commandsData.lazyMapped { commandData, index -> - // Deprecated signingParties property not supported. - CommandWithParties(signers[partialMerkleLeafIndices[index]], emptyList(), commandData) - } - } else { - check(commandsData.size == signers.size) { - "Invalid Transaction. Sizes of CommandData (${commandsData.size}) and Signers (${signers.size}) do not match" - } - commandsData.lazyMapped { commandData, index -> - // Deprecated signingParties property not supported. - CommandWithParties(signers[index], emptyList(), commandData) - } - } - } - } -} diff --git a/node/djvm/src/main/kotlin/net/corda/node/djvm/ComponentBuilder.kt b/node/djvm/src/main/kotlin/net/corda/node/djvm/ComponentBuilder.kt deleted file mode 100644 index f0e2e476aa..0000000000 --- a/node/djvm/src/main/kotlin/net/corda/node/djvm/ComponentBuilder.kt +++ /dev/null @@ -1,27 +0,0 @@ -package net.corda.node.djvm - -import net.corda.core.contracts.ComponentGroupEnum -import net.corda.core.internal.TransactionDeserialisationException -import net.corda.core.internal.lazyMapped -import net.corda.core.utilities.OpaqueBytes -import java.util.function.Function -import java.util.function.Supplier - -class ComponentBuilder : Function, Supplier>> { - @Suppress("unchecked_cast", "TooGenericExceptionCaught") - override fun apply(inputs: Array): Supplier> { - val deserializer = inputs[0] as Function - val groupType = inputs[1] as ComponentGroupEnum - val components = (inputs[2] as Array).map(::OpaqueBytes) - - return Supplier { - components.lazyMapped { component, index -> - try { - deserializer.apply(component.bytes) - } catch (e: Exception) { - throw TransactionDeserialisationException(groupType, index, e) - } - } - } - } -} diff --git a/node/djvm/src/main/kotlin/net/corda/node/djvm/LtxSupplierFactory.kt b/node/djvm/src/main/kotlin/net/corda/node/djvm/LtxSupplierFactory.kt deleted file mode 100644 index 69899508c5..0000000000 --- a/node/djvm/src/main/kotlin/net/corda/node/djvm/LtxSupplierFactory.kt +++ /dev/null @@ -1,75 +0,0 @@ -@file:JvmName("LtxTools") -package net.corda.node.djvm - -import net.corda.core.contracts.Attachment -import net.corda.core.contracts.CommandData -import net.corda.core.contracts.CommandWithParties -import net.corda.core.contracts.ContractState -import net.corda.core.contracts.CordaRotatedKeys -import net.corda.core.contracts.PrivacySalt -import net.corda.core.contracts.StateAndRef -import net.corda.core.contracts.StateRef -import net.corda.core.contracts.TimeWindow -import net.corda.core.contracts.TransactionState -import net.corda.core.crypto.DigestService -import net.corda.core.crypto.SecureHash -import net.corda.core.identity.Party -import net.corda.core.node.NetworkParameters -import net.corda.core.transactions.LedgerTransaction -import java.util.function.Function -import java.util.function.Supplier - -private const val TX_INPUTS = 0 -private const val TX_OUTPUTS = 1 -private const val TX_COMMANDS = 2 -private const val TX_ATTACHMENTS = 3 -private const val TX_ID = 4 -private const val TX_NOTARY = 5 -private const val TX_TIME_WINDOW = 6 -private const val TX_PRIVACY_SALT = 7 -private const val TX_NETWORK_PARAMETERS = 8 -private const val TX_REFERENCES = 9 -private const val TX_DIGEST_SERVICE = 10 - -class LtxSupplierFactory : Function, Supplier> { - @Suppress("unchecked_cast") - override fun apply(txArgs: Array): Supplier { - val inputProvider = (txArgs[TX_INPUTS] as Function>>) - .andThen(Function(Array>::toContractStatesAndRef)) - .toSupplier() - val outputProvider = txArgs[TX_OUTPUTS] as? Supplier>> ?: Supplier(::emptyList) - val commandsProvider = txArgs[TX_COMMANDS] as Supplier>> - val referencesProvider = (txArgs[TX_REFERENCES] as Function>>) - .andThen(Function(Array>::toContractStatesAndRef)) - .toSupplier() - val networkParameters = (txArgs[TX_NETWORK_PARAMETERS] as? NetworkParameters)?.toImmutable() - return Supplier { - LedgerTransaction.createForContractVerify( - inputs = inputProvider.get(), - outputs = outputProvider.get(), - commands = commandsProvider.get(), - attachments = txArgs[TX_ATTACHMENTS] as? List ?: emptyList(), - id = txArgs[TX_ID] as SecureHash, - notary = txArgs[TX_NOTARY] as? Party, - timeWindow = txArgs[TX_TIME_WINDOW] as? TimeWindow, - privacySalt = txArgs[TX_PRIVACY_SALT] as PrivacySalt, - networkParameters = networkParameters, - references = referencesProvider.get(), - digestService = txArgs[TX_DIGEST_SERVICE] as DigestService, - rotatedKeys = CordaRotatedKeys.keys - ) - } - } -} - -private fun Function.toSupplier(): Supplier { - return Supplier { apply(null) } -} - -private fun Array>.toContractStatesAndRef(): List> { - return map(Array::toStateAndRef) -} - -private fun Array<*>.toStateAndRef(): StateAndRef { - return StateAndRef(this[0] as TransactionState<*>, this[1] as StateRef) -} diff --git a/node/djvm/src/main/resources/META-INF/DJVM-preload b/node/djvm/src/main/resources/META-INF/DJVM-preload deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/node/src/integration-test-slow/kotlin/net/corda/node/flows/FinalityFlowErrorHandlingTest.kt b/node/src/integration-test-slow/kotlin/net/corda/node/flows/FinalityFlowErrorHandlingTest.kt new file mode 100644 index 0000000000..dc0133f575 --- /dev/null +++ b/node/src/integration-test-slow/kotlin/net/corda/node/flows/FinalityFlowErrorHandlingTest.kt @@ -0,0 +1,107 @@ +package net.corda.node.flows + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.CordaRuntimeException +import net.corda.core.crypto.SecureHash +import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.StartableByRPC +import net.corda.core.messaging.startFlow +import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.getOrThrow +import net.corda.core.utilities.seconds +import net.corda.finance.DOLLARS +import net.corda.finance.test.flows.CashIssueWithObserversFlow +import net.corda.node.services.statemachine.StateMachineErrorHandlingTest +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.CHARLIE_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.singleIdentity +import net.corda.testing.flows.waitForAllFlowsToComplete +import net.corda.testing.node.NotarySpec +import net.corda.testing.node.internal.FINANCE_CORDAPPS +import net.corda.testing.node.internal.enclosedCordapp +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.fail + +class FinalityFlowErrorHandlingTest : StateMachineErrorHandlingTest() { + + /** + * Throws an exception after recording an issuance transaction but before broadcasting the transaction to observer sessions. + * + */ + @Test(timeout = 300_000) + fun `error after recording an issuance transaction inside of FinalityFlow generates recovery metadata`() { + startDriver(notarySpec = NotarySpec(DUMMY_NOTARY_NAME, validating = false), + extraCordappPackagesToScan = listOf("net.corda.node.flows", "net.corda.finance.test.flows")) { + val (charlie, alice, port) = createNodeAndBytemanNode(CHARLIE_NAME, ALICE_NAME, FINANCE_CORDAPPS + enclosedCordapp()) + + val rules = """ + RULE Set flag when entering receive finality flow + CLASS ${FinalityFlow::class.java.name} + METHOD call + AT ENTRY + IF !flagged("finality_flag") + DO flag("finality_flag"); traceln("Setting finality flag") + ENDRULE + + RULE Throw exception when recording transaction + CLASS ${FinalityFlow::class.java.name} + METHOD finaliseLocallyAndBroadcast + AT EXIT + IF flagged("finality_flag") + DO traceln("Throwing exception"); + throw new java.lang.RuntimeException("die dammit die") + ENDRULE + """.trimIndent() + + submitBytemanRules(rules, port) + + try { + alice.rpc.startFlow( + ::CashIssueWithObserversFlow, + 500.DOLLARS, + OpaqueBytes.of(0x01), + defaultNotaryIdentity, + setOf(charlie.nodeInfo.singleIdentity()) + ).returnValue.getOrThrow(30.seconds) + fail() + } + catch (e: CordaRuntimeException) { + waitForAllFlowsToComplete(alice) + val txId = alice.rpc.stateMachineRecordedTransactionMappingSnapshot().single().transactionId + + alice.rpc.startFlow(::GetFlowTransaction, txId).returnValue.getOrThrow().apply { + assertEquals("V", this.first) // "V" -> VERIFIED + assertEquals(SecureHash.sha256(CHARLIE_NAME.toString()).toString(), this.second) // peer + } + } + } + } +} + +// Internal use for testing only!! +@StartableByRPC +class GetFlowTransaction(private val txId: SecureHash) : FlowLogic>() { + @Suspendable + override fun call(): Pair { + val transactionStatus = serviceHub.jdbcSession().prepareStatement("select * from node_transactions where tx_id = ?") + .apply { setString(1, txId.toString()) } + .use { ps -> + ps.executeQuery().use { rs -> + rs.next() + rs.getString(4) // TransactionStatus + } + } + val receiverPartyId = serviceHub.jdbcSession().prepareStatement("select * from node_sender_distr_recs where transaction_id = ?") + .apply { setString(1, txId.toString()) } + .use { ps -> + ps.executeQuery().use { rs -> + rs.next() + rs.getString(4) // receiverPartyId + } + } + return Pair(transactionStatus, receiverPartyId) + } +} \ No newline at end of file diff --git a/node/src/integration-test-slow/kotlin/net/corda/node/services/statemachine/StateMachineErrorHandlingTest.kt b/node/src/integration-test-slow/kotlin/net/corda/node/services/statemachine/StateMachineErrorHandlingTest.kt index 3e0882e62e..8885b936ee 100644 --- a/node/src/integration-test-slow/kotlin/net/corda/node/services/statemachine/StateMachineErrorHandlingTest.kt +++ b/node/src/integration-test-slow/kotlin/net/corda/node/services/statemachine/StateMachineErrorHandlingTest.kt @@ -49,13 +49,16 @@ abstract class StateMachineErrorHandlingTest { counter = 0 } - internal fun startDriver(notarySpec: NotarySpec = NotarySpec(DUMMY_NOTARY_NAME), dsl: DriverDSL.() -> Unit) { + internal fun startDriver(notarySpec: NotarySpec = NotarySpec(DUMMY_NOTARY_NAME), + extraCordappPackagesToScan: List = emptyList(), + dsl: DriverDSL.() -> Unit) { driver( DriverParameters( notarySpecs = listOf(notarySpec), startNodesInProcess = false, inMemoryDB = false, - systemProperties = mapOf("co.paralleluniverse.fibers.verifyInstrumentation" to "true") + systemProperties = mapOf("co.paralleluniverse.fibers.verifyInstrumentation" to "true"), + extraCordappPackagesToScan = extraCordappPackagesToScan ) ) { dsl() diff --git a/node/src/integration-test-slow/kotlin/net/corda/node/services/statemachine/StateMachineFinalityErrorHandlingTest.kt b/node/src/integration-test-slow/kotlin/net/corda/node/services/statemachine/StateMachineFinalityErrorHandlingTest.kt index d1db7445fb..86b1781442 100644 --- a/node/src/integration-test-slow/kotlin/net/corda/node/services/statemachine/StateMachineFinalityErrorHandlingTest.kt +++ b/node/src/integration-test-slow/kotlin/net/corda/node/services/statemachine/StateMachineFinalityErrorHandlingTest.kt @@ -57,7 +57,7 @@ class StateMachineFinalityErrorHandlingTest : StateMachineErrorHandlingTest() { RULE Throw exception when recording transaction INTERFACE ${ServiceHubInternal::class.java.name} - METHOD recordTransactions + METHOD finalizeTransactionWithExtraSignatures AT ENTRY IF flagged("finality_flag") && flagged("resolve_tx_flag") DO traceln("Throwing exception"); @@ -118,7 +118,7 @@ class StateMachineFinalityErrorHandlingTest : StateMachineErrorHandlingTest() { RULE Throw exception when recording transaction INTERFACE ${ServiceHubInternal::class.java.name} - METHOD recordTransactions + METHOD finalizeTransactionWithExtraSignatures AT ENTRY IF flagged("finality_flag") && flagged("resolve_tx_flag") DO traceln("Throwing exception"); diff --git a/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintMigrationFromHashConstraintsTests.kt b/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintMigrationFromHashConstraintsTests.kt index f430a6fa70..a69723cdc3 100644 --- a/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintMigrationFromHashConstraintsTests.kt +++ b/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintMigrationFromHashConstraintsTests.kt @@ -8,7 +8,6 @@ import net.corda.core.internal.deleteRecursively import net.corda.core.internal.div import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow -import net.corda.node.flows.isQuasarAgentSpecified import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.singleIdentity import net.corda.testing.driver.NodeParameters @@ -28,8 +27,8 @@ open class SignatureConstraintMigrationFromHashConstraintsTests : SignatureConst val stateAndRef: StateAndRef? = internalDriver( inMemoryDB = false, - startNodesInProcess = isQuasarAgentSpecified(), - networkParameters = testNetworkParameters(notaries = emptyList(), minimumPlatformVersion = 4) + networkParameters = testNetworkParameters(notaries = emptyList(), minimumPlatformVersion = 4), + systemProperties = mapOf("net.corda.recordtransaction.signature.verification.disabled" to true.toString()) ) { val nodeName = { val nodeHandle = startNode(NodeParameters(rpcUsers = listOf(user), additionalCordapps = listOf(oldCordapp))).getOrThrow() diff --git a/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintMigrationFromWhitelistConstraintTests.kt b/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintMigrationFromWhitelistConstraintTests.kt index c3e0688548..8c194ac2a9 100644 --- a/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintMigrationFromWhitelistConstraintTests.kt +++ b/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintMigrationFromWhitelistConstraintTests.kt @@ -9,7 +9,6 @@ import net.corda.core.internal.deleteRecursively import net.corda.core.internal.div import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow -import net.corda.node.flows.isQuasarAgentSpecified import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.singleIdentity import net.corda.testing.driver.NodeParameters @@ -30,8 +29,8 @@ open class SignatureConstraintMigrationFromWhitelistConstraintTests : Signature val stateAndRef: StateAndRef? = internalDriver( inMemoryDB = false, - startNodesInProcess = isQuasarAgentSpecified(), - networkParameters = testNetworkParameters(notaries = emptyList(), minimumPlatformVersion = 4) + networkParameters = testNetworkParameters(notaries = emptyList(), minimumPlatformVersion = 4), + systemProperties = mapOf("net.corda.recordtransaction.signature.verification.disabled" to true.toString()) ) { val nodeName = { val nodeHandle = startNode(NodeParameters(rpcUsers = listOf(user), additionalCordapps = listOf(oldCordapp))).getOrThrow() @@ -142,7 +141,7 @@ open class SignatureConstraintMigrationFromWhitelistConstraintTests : Signature ) ), systemProperties = emptyMap(), - startNodesInProcess = true, + startNodesInProcess = false, specifyExistingConstraint = true, addAnotherAutomaticConstraintState = true ) diff --git a/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintVersioningTests.kt b/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintVersioningTests.kt index a574d1d2d6..bd243e7127 100644 --- a/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintVersioningTests.kt +++ b/node/src/integration-test/kotlin/net/corda/contracts/SignatureConstraintVersioningTests.kt @@ -74,7 +74,8 @@ open class SignatureConstraintVersioningTests { minimumPlatformVersion = minimumPlatformVersion, whitelistedContractImplementations = whitelistedAttachmentHashes ), - systemProperties = systemProperties + systemProperties = systemProperties + + ("net.corda.recordtransaction.signature.verification.disabled" to true.toString()) ) { // create transaction using first Cordapp val (nodeName, baseDirectory, issuanceTransaction) = createIssuanceTransaction(cordapp) diff --git a/node/src/integration-test/kotlin/net/corda/contracts/djvm/attachment/SandboxAttachmentContract.kt b/node/src/integration-test/kotlin/net/corda/contracts/djvm/attachment/SandboxAttachmentContract.kt deleted file mode 100644 index 4e96264a3d..0000000000 --- a/node/src/integration-test/kotlin/net/corda/contracts/djvm/attachment/SandboxAttachmentContract.kt +++ /dev/null @@ -1,38 +0,0 @@ -package net.corda.contracts.djvm.attachment - -import net.corda.core.contracts.CommandData -import net.corda.core.contracts.Contract -import net.corda.core.contracts.ContractState -import net.corda.core.identity.AbstractParty -import net.corda.core.transactions.LedgerTransaction -import java.io.ByteArrayOutputStream - -class SandboxAttachmentContract : Contract { - override fun verify(tx: LedgerTransaction) { - val attachments = tx.attachments - require(attachments.isNotEmpty()) { "Attachments are missing for TX=${tx.id}" } - - require(attachments.size == 1) { "Did not expect to find ${attachments.size} attachments for TX=${tx.id}" } - val attachment = attachments[0] - require(attachment.size > 0) { "Attachment ${attachment.id} has no contents for TX=${tx.id}" } - - val keyCount = attachment.signerKeys.size - require(keyCount == 1) { "Did not expect to find $keyCount signing keys for attachment ${attachment.id}, TX=${tx.id}" } - - tx.commandsOfType().forEach { extract -> - val fileName = extract.value.fileName - val contents = ByteArrayOutputStream().use { - attachment.extractFile(fileName, it) - it - }.toByteArray() - require(contents.isNotEmpty()) { "File $fileName has no contents for TX=${tx.id}" } - } - } - - @Suppress("CanBeParameter", "MemberVisibilityCanBePrivate") - class State(val issuer: AbstractParty) : ContractState { - override val participants: List = listOf(issuer) - } - - class ExtractFile(val fileName: String) : CommandData -} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/contracts/djvm/broken/NonDeterministicContract.kt b/node/src/integration-test/kotlin/net/corda/contracts/djvm/broken/NonDeterministicContract.kt deleted file mode 100644 index 9d7ffacf41..0000000000 --- a/node/src/integration-test/kotlin/net/corda/contracts/djvm/broken/NonDeterministicContract.kt +++ /dev/null @@ -1,50 +0,0 @@ -package net.corda.contracts.djvm.broken - -import net.corda.core.contracts.Contract -import net.corda.core.contracts.ContractState -import net.corda.core.contracts.TypeOnlyCommandData -import net.corda.core.identity.AbstractParty -import net.corda.core.transactions.LedgerTransaction -import java.time.Instant -import java.util.* - -class NonDeterministicContract : Contract { - override fun verify(tx: LedgerTransaction) { - when { - tx.commandsOfType().isNotEmpty() -> verifyInstantNow() - tx.commandsOfType().isNotEmpty() -> verifyCurrentTimeMillis() - tx.commandsOfType().isNotEmpty() -> verifyNanoTime() - tx.commandsOfType().isNotEmpty() -> UUID.randomUUID() - tx.commandsOfType().isNotEmpty() -> verifyNoReflection() - else -> {} - } - } - - private fun verifyInstantNow() { - Instant.now() - } - - private fun verifyCurrentTimeMillis() { - System.currentTimeMillis() - } - - private fun verifyNanoTime() { - System.nanoTime() - } - - private fun verifyNoReflection() { - Date::class.java.getDeclaredConstructor().newInstance() - } - - @Suppress("CanBeParameter", "MemberVisibilityCanBePrivate") - class State(val issuer: AbstractParty) : ContractState { - override val participants: List = listOf(issuer) - } - - class InstantNow : TypeOnlyCommandData() - class CurrentTimeMillis : TypeOnlyCommandData() - class NanoTime : TypeOnlyCommandData() - class RandomUUID : TypeOnlyCommandData() - class WithReflection : TypeOnlyCommandData() - class NoOperation : TypeOnlyCommandData() -} diff --git a/node/src/integration-test/kotlin/net/corda/contracts/djvm/crypto/DeterministicCryptoContract.kt b/node/src/integration-test/kotlin/net/corda/contracts/djvm/crypto/DeterministicCryptoContract.kt deleted file mode 100644 index d861a74d28..0000000000 --- a/node/src/integration-test/kotlin/net/corda/contracts/djvm/crypto/DeterministicCryptoContract.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.contracts.djvm.crypto - -import net.corda.core.contracts.CommandData -import net.corda.core.contracts.Contract -import net.corda.core.contracts.ContractState -import net.corda.core.crypto.Crypto -import net.corda.core.identity.AbstractParty -import net.corda.core.transactions.LedgerTransaction -import net.corda.core.utilities.OpaqueBytes -import java.security.PublicKey - -class DeterministicCryptoContract : Contract { - override fun verify(tx: LedgerTransaction) { - val cryptoData = tx.outputsOfType() - val validators = tx.commandsOfType() - - val isValid = validators.all { validate -> - with (validate.value) { - cryptoData.all { crypto -> - Crypto.doVerify(schemeCodeName, publicKey, crypto.signature.bytes, crypto.original.bytes) - } - } - } - - require(cryptoData.isNotEmpty() && validators.isNotEmpty() && isValid) { - "Failed to validate signatures in command data" - } - } - - @Suppress("CanBeParameter", "MemberVisibilityCanBePrivate") - class CryptoState(val owner: AbstractParty, val original: OpaqueBytes, val signature: OpaqueBytes) : ContractState { - override val participants: List = listOf(owner) - } - - class Validate( - val schemeCodeName: String, - val publicKey: PublicKey - ) : CommandData -} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/contracts/djvm/whitelist/DeterministicWhitelistContract.kt b/node/src/integration-test/kotlin/net/corda/contracts/djvm/whitelist/DeterministicWhitelistContract.kt deleted file mode 100644 index 433165e986..0000000000 --- a/node/src/integration-test/kotlin/net/corda/contracts/djvm/whitelist/DeterministicWhitelistContract.kt +++ /dev/null @@ -1,38 +0,0 @@ -package net.corda.contracts.djvm.whitelist - -import net.corda.core.contracts.CommandData -import net.corda.core.contracts.Contract -import net.corda.core.contracts.ContractState -import net.corda.core.identity.AbstractParty -import net.corda.core.transactions.LedgerTransaction - -class DeterministicWhitelistContract : Contract { - companion object { - const val MAX_VALUE = 2000L - } - - override fun verify(tx: LedgerTransaction) { - val states = tx.outputsOfType() - require(states.isNotEmpty()) { - "Requires at least one custom data state" - } - - states.forEach { - require(it.whitelistData in WhitelistData(0)..WhitelistData(MAX_VALUE)) { - "WhitelistData $it exceeds maximum value!" - } - } - } - - @Suppress("CanBeParameter", "MemberVisibilityCanBePrivate") - class State(val owner: AbstractParty, val whitelistData: WhitelistData) : ContractState { - override val participants: List = listOf(owner) - - @Override - override fun toString(): String { - return whitelistData.toString() - } - } - - class Operate : CommandData -} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/contracts/djvm/whitelist/WhitelistData.kt b/node/src/integration-test/kotlin/net/corda/contracts/djvm/whitelist/WhitelistData.kt deleted file mode 100644 index c0d5f00f89..0000000000 --- a/node/src/integration-test/kotlin/net/corda/contracts/djvm/whitelist/WhitelistData.kt +++ /dev/null @@ -1,15 +0,0 @@ -package net.corda.contracts.djvm.whitelist - -import net.corda.core.serialization.SerializationWhitelist - -data class WhitelistData(val value: Long) : Comparable { - override fun compareTo(other: WhitelistData): Int { - return value.compareTo(other.value) - } - - override fun toString(): String = "$value things" -} - -class Whitelist : SerializationWhitelist { - override val whitelist = listOf(WhitelistData::class.java) -} diff --git a/node/src/integration-test/kotlin/net/corda/contracts/incompatible/version1/AttachmentContract.kt b/node/src/integration-test/kotlin/net/corda/contracts/incompatible/version1/AttachmentContract.kt new file mode 100644 index 0000000000..f4c3fa965b --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/contracts/incompatible/version1/AttachmentContract.kt @@ -0,0 +1,33 @@ +package net.corda.contracts.incompatible.version1 + +import net.corda.core.contracts.Contract +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.TransactionVerificationException +import net.corda.core.contracts.TypeOnlyCommandData +import net.corda.core.crypto.SecureHash +import net.corda.core.identity.AbstractParty +import net.corda.core.serialization.internal.AttachmentsClassLoader +import net.corda.core.transactions.LedgerTransaction + +class AttachmentContract : Contract { + + private val FAIL_CONTRACT_VERIFY = java.lang.Boolean.getBoolean("net.corda.contracts.incompatible.AttachmentContract.fail.verify") + override fun verify(tx: LedgerTransaction) { + if (FAIL_CONTRACT_VERIFY) throw object:TransactionVerificationException(tx.id, "AttachmentContract verify failed.", null) {} + val state = tx.outputsOfType().single() + // we check that at least one has the matching hash, the other will be the contract + require(tx.attachments.any { it.id == state.hash }) {"At least one attachment in transaction must match hash ${state.hash}"} + } + + object Command : TypeOnlyCommandData() + + data class State(val hash: SecureHash.SHA256) : ContractState { + private val FAIL_CONTRACT_STATE = java.lang.Boolean.getBoolean("net.corda.contracts.incompatible.AttachmentContract.fail.state") && (this.javaClass.classLoader !is AttachmentsClassLoader) + init { + if (FAIL_CONTRACT_STATE) throw TransactionVerificationException.TransactionRequiredContractUnspecifiedException(hash,"AttachmentContract state initialisation failed.") + } + override val participants: List = emptyList() + } +} + +const val ATTACHMENT_PROGRAM_ID = "net.corda.contracts.incompatible.version1.AttachmentContract" diff --git a/node/src/integration-test/kotlin/net/corda/contracts/incompatible/version2/AttachmentContract.kt b/node/src/integration-test/kotlin/net/corda/contracts/incompatible/version2/AttachmentContract.kt new file mode 100644 index 0000000000..00bb7e2371 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/contracts/incompatible/version2/AttachmentContract.kt @@ -0,0 +1,25 @@ +package net.corda.contracts.incompatible.version2 + +import net.corda.core.contracts.Contract +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.TypeOnlyCommandData +import net.corda.core.crypto.SecureHash +import net.corda.core.identity.AbstractParty +import net.corda.core.transactions.LedgerTransaction +import net.corda.core.utilities.OpaqueBytes + +class AttachmentContract : Contract { + override fun verify(tx: LedgerTransaction) { + val state = tx.outputsOfType().single() + // we check that at least one has the matching hash, the other will be the contract + require(tx.attachments.any { it.id == SecureHash.SHA256(state.opaqueBytes.bytes) }) {"At least one attachment in transaction must match hash ${state.opaqueBytes}"} + } + + object Command : TypeOnlyCommandData() + + data class State(val opaqueBytes: OpaqueBytes) : ContractState { + override val participants: List = emptyList() + } +} + +const val ATTACHMENT_PROGRAM_ID = "net.corda.contracts.incompatible.version2.AttachmentContract" diff --git a/node/src/integration-test/kotlin/net/corda/flows/djvm/attachment/SandboxAttachmentFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/djvm/attachment/SandboxAttachmentFlow.kt deleted file mode 100644 index ae277dfda2..0000000000 --- a/node/src/integration-test/kotlin/net/corda/flows/djvm/attachment/SandboxAttachmentFlow.kt +++ /dev/null @@ -1,25 +0,0 @@ -package net.corda.flows.djvm.attachment - -import co.paralleluniverse.fibers.Suspendable -import net.corda.contracts.djvm.attachment.SandboxAttachmentContract -import net.corda.core.contracts.Command -import net.corda.core.contracts.CommandData -import net.corda.core.crypto.SecureHash -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.StartableByRPC -import net.corda.core.transactions.TransactionBuilder - -@StartableByRPC -class SandboxAttachmentFlow(private val command: CommandData) : FlowLogic() { - @Suspendable - override fun call(): SecureHash { - val notary = serviceHub.networkMapCache.notaryIdentities[0] - val stx = serviceHub.signInitialTransaction( - TransactionBuilder(notary) - .addOutputState(SandboxAttachmentContract.State(ourIdentity)) - .addCommand(Command(command, ourIdentity.owningKey)) - ) - stx.verify(serviceHub, checkSufficientSignatures = false) - return stx.id - } -} diff --git a/node/src/integration-test/kotlin/net/corda/flows/djvm/broken/NonDeterministicFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/djvm/broken/NonDeterministicFlow.kt deleted file mode 100644 index 076f17bdff..0000000000 --- a/node/src/integration-test/kotlin/net/corda/flows/djvm/broken/NonDeterministicFlow.kt +++ /dev/null @@ -1,25 +0,0 @@ -package net.corda.flows.djvm.broken - -import co.paralleluniverse.fibers.Suspendable -import net.corda.contracts.djvm.broken.NonDeterministicContract -import net.corda.core.contracts.Command -import net.corda.core.contracts.TypeOnlyCommandData -import net.corda.core.crypto.SecureHash -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.StartableByRPC -import net.corda.core.transactions.TransactionBuilder - -@StartableByRPC -class NonDeterministicFlow(private val trouble: TypeOnlyCommandData) : FlowLogic() { - @Suspendable - override fun call(): SecureHash { - val notary = serviceHub.networkMapCache.notaryIdentities[0] - val stx = serviceHub.signInitialTransaction( - TransactionBuilder(notary) - .addOutputState(NonDeterministicContract.State(ourIdentity)) - .addCommand(Command(trouble, ourIdentity.owningKey)) - ) - stx.verify(serviceHub, checkSufficientSignatures = false) - return stx.id - } -} diff --git a/node/src/integration-test/kotlin/net/corda/flows/djvm/crypto/DeterministicCryptoFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/djvm/crypto/DeterministicCryptoFlow.kt deleted file mode 100644 index 65e0065eb1..0000000000 --- a/node/src/integration-test/kotlin/net/corda/flows/djvm/crypto/DeterministicCryptoFlow.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.flows.djvm.crypto - -import co.paralleluniverse.fibers.Suspendable -import net.corda.contracts.djvm.crypto.DeterministicCryptoContract -import net.corda.core.contracts.Command -import net.corda.core.contracts.CommandData -import net.corda.core.crypto.SecureHash -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.StartableByRPC -import net.corda.core.transactions.TransactionBuilder -import net.corda.core.utilities.OpaqueBytes - -@StartableByRPC -class DeterministicCryptoFlow( - private val command: CommandData, - private val original: OpaqueBytes, - private val signature: OpaqueBytes -) : FlowLogic() { - @Suspendable - override fun call(): SecureHash { - val notary = serviceHub.networkMapCache.notaryIdentities[0] - val stx = serviceHub.signInitialTransaction( - TransactionBuilder(notary) - .addOutputState(DeterministicCryptoContract.CryptoState(ourIdentity, original, signature)) - .addCommand(Command(command, ourIdentity.owningKey)) - ) - stx.verify(serviceHub, checkSufficientSignatures = false) - return stx.id - } -} diff --git a/node/src/integration-test/kotlin/net/corda/flows/djvm/whitelist/DeterministicWhitelistFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/djvm/whitelist/DeterministicWhitelistFlow.kt deleted file mode 100644 index fe8824e03c..0000000000 --- a/node/src/integration-test/kotlin/net/corda/flows/djvm/whitelist/DeterministicWhitelistFlow.kt +++ /dev/null @@ -1,26 +0,0 @@ -package net.corda.flows.djvm.whitelist - -import co.paralleluniverse.fibers.Suspendable -import net.corda.contracts.djvm.whitelist.DeterministicWhitelistContract.Operate -import net.corda.contracts.djvm.whitelist.DeterministicWhitelistContract.State -import net.corda.contracts.djvm.whitelist.WhitelistData -import net.corda.core.contracts.Command -import net.corda.core.crypto.SecureHash -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.StartableByRPC -import net.corda.core.transactions.TransactionBuilder - -@StartableByRPC -class DeterministicWhitelistFlow(private val data: WhitelistData) : FlowLogic() { - @Suspendable - override fun call(): SecureHash { - val notary = serviceHub.networkMapCache.notaryIdentities[0] - val stx = serviceHub.signInitialTransaction( - TransactionBuilder(notary) - .addOutputState(State(ourIdentity, data)) - .addCommand(Command(Operate(), ourIdentity.owningKey)) - ) - stx.verify(serviceHub, checkSufficientSignatures = false) - return stx.id - } -} diff --git a/node/src/integration-test/kotlin/net/corda/flows/incompatible/version1/AttachmentFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/incompatible/version1/AttachmentFlow.kt new file mode 100644 index 0000000000..6a4ccd9d04 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/flows/incompatible/version1/AttachmentFlow.kt @@ -0,0 +1,94 @@ +package net.corda.flows.incompatible.version1 + +import co.paralleluniverse.fibers.Suspendable +import net.corda.contracts.incompatible.version1.ATTACHMENT_PROGRAM_ID +import net.corda.contracts.incompatible.version1.AttachmentContract +import net.corda.core.contracts.Command +import net.corda.core.contracts.StateAndRef +import net.corda.core.contracts.TransactionState +import net.corda.core.crypto.SecureHash +import net.corda.core.flows.CollectSignaturesFlow +import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.ReceiveFinalityFlow +import net.corda.core.flows.SignTransactionFlow +import net.corda.core.flows.StartableByRPC +import net.corda.core.identity.Party +import net.corda.core.node.StatesToRecord +import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.ProgressTracker +import net.corda.core.utilities.unwrap + +@InitiatingFlow +@StartableByRPC +class AttachmentFlow(private val otherSide: Party, + private val notary: Party, + private val attachId: SecureHash.SHA256, + private val notariseInputState: StateAndRef? = null) : FlowLogic() { + + object SIGNING : ProgressTracker.Step("Signing transaction") + + override val progressTracker: ProgressTracker = ProgressTracker(SIGNING) + + @Suspendable + override fun call(): SignedTransaction { + val session = initiateFlow(otherSide) + val notarise = notariseInputState != null + session.send(notarise) // inform peer whether to sign for notarisation + + // Create a trivial transaction with an output that describes the attachment, and the attachment itself + val ptx = TransactionBuilder(notary) + .addOutputState(AttachmentContract.State(attachId), ATTACHMENT_PROGRAM_ID) + .addAttachment(attachId) + if (notarise) { + ptx.addInputState(notariseInputState!!) + ptx.addCommand(AttachmentContract.Command, ourIdentity.owningKey, otherSide.owningKey) + } + else + ptx.addCommand(AttachmentContract.Command, ourIdentity.owningKey) + + progressTracker.currentStep = SIGNING + + val stx = serviceHub.signInitialTransaction(ptx) + val ftx = if (notarise) { + subFlow(CollectSignaturesFlow(stx, listOf(session))) + } else stx + + return subFlow(FinalityFlow(ftx, setOf(session), statesToRecord = StatesToRecord.ALL_VISIBLE)) + } +} + +@InitiatedBy(AttachmentFlow::class) +class StoreAttachmentFlow(private val otherSide: FlowSession) : FlowLogic() { + @Suspendable + override fun call() { + val notarise = otherSide.receive().unwrap { it } + if (notarise) { + val stx = subFlow(object : SignTransactionFlow(otherSide) { + override fun checkTransaction(stx: SignedTransaction) { + } + }) + subFlow(ReceiveFinalityFlow(otherSide, stx.id, statesToRecord = StatesToRecord.ALL_VISIBLE)) + } else { + subFlow(ReceiveFinalityFlow(otherSide, statesToRecord = StatesToRecord.ALL_VISIBLE)) + } + } +} + +@StartableByRPC +class AttachmentIssueFlow(private val attachId: SecureHash.SHA256, + private val notary: Party): FlowLogic() { + @Suspendable + override fun call(): SignedTransaction { + val builder = TransactionBuilder(notary) + builder.addAttachment(attachId) + builder.addOutputState(TransactionState(AttachmentContract.State(attachId), ATTACHMENT_PROGRAM_ID, notary)) + builder.addCommand(Command(AttachmentContract.Command, listOf(ourIdentity.owningKey))) + val tx = serviceHub.signInitialTransaction(builder, ourIdentity.owningKey) + return subFlow(FinalityFlow(tx, emptySet(), statesToRecord = StatesToRecord.ALL_VISIBLE)) + } +} diff --git a/node/src/integration-test/kotlin/net/corda/flows/incompatible/version2/AttachmentFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/incompatible/version2/AttachmentFlow.kt new file mode 100644 index 0000000000..2a1faadef7 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/flows/incompatible/version2/AttachmentFlow.kt @@ -0,0 +1,58 @@ +package net.corda.flows.incompatible.version2 + +import co.paralleluniverse.fibers.Suspendable +import net.corda.contracts.incompatible.version2.ATTACHMENT_PROGRAM_ID +import net.corda.contracts.incompatible.version2.AttachmentContract +import net.corda.core.crypto.SecureHash +import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.ReceiveTransactionFlow +import net.corda.core.flows.StartableByRPC +import net.corda.core.identity.Party +import net.corda.core.node.StatesToRecord +import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.ProgressTracker + +@InitiatingFlow +@StartableByRPC +class AttachmentFlow(private val otherSide: Party, + private val notary: Party, + private val attachId: SecureHash.SHA256) : FlowLogic() { + + object SIGNING : ProgressTracker.Step("Signing transaction") + + override val progressTracker: ProgressTracker = ProgressTracker(SIGNING) + + @Suspendable + override fun call(): SignedTransaction { + // Create a trivial transaction with an output that describes the attachment, and the attachment itself + val ptx = TransactionBuilder(notary) + .addOutputState(AttachmentContract.State(attachId), ATTACHMENT_PROGRAM_ID) + .addCommand(AttachmentContract.Command, ourIdentity.owningKey) + .addAttachment(attachId) + + progressTracker.currentStep = SIGNING + + val stx = serviceHub.signInitialTransaction(ptx) + + // Send the transaction to the other recipient + return subFlow(FinalityFlow(stx, initiateFlow(otherSide))) + } +} + +@InitiatedBy(AttachmentFlow::class) +class StoreAttachmentFlow(private val otherSide: FlowSession) : FlowLogic() { + @Suspendable + override fun call() { + // purposely prevent transaction verification and recording in ReceiveTransactionFlow + val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false, statesToRecord = StatesToRecord.ALL_VISIBLE)) + logger.info("StoreAttachmentFlow: successfully received fully signed tx. Sending it to the vault for processing.") + + serviceHub.recordTransactions(StatesToRecord.ALL_VISIBLE, setOf(stx)) + logger.info("StoreAttachmentFlow: successfully recorded received transaction locally.") + } +} diff --git a/node/src/integration-test/kotlin/net/corda/flows/incompatible/version3/AttachmentFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/incompatible/version3/AttachmentFlow.kt new file mode 100644 index 0000000000..082f4da8aa --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/flows/incompatible/version3/AttachmentFlow.kt @@ -0,0 +1,56 @@ +package net.corda.flows.incompatible.version3 + +import co.paralleluniverse.fibers.Suspendable +import net.corda.contracts.incompatible.version1.ATTACHMENT_PROGRAM_ID +import net.corda.contracts.incompatible.version1.AttachmentContract +import net.corda.core.crypto.SecureHash +import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.ReceiveTransactionFlow +import net.corda.core.flows.StartableByRPC +import net.corda.core.identity.Party +import net.corda.core.node.StatesToRecord +import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.ProgressTracker + +@InitiatingFlow +@StartableByRPC +class AttachmentFlow(private val otherSide: Party, + private val notary: Party, + private val attachId: SecureHash.SHA256) : FlowLogic() { + + object SIGNING : ProgressTracker.Step("Signing transaction") + + override val progressTracker: ProgressTracker = ProgressTracker(SIGNING) + + @Suspendable + override fun call(): SignedTransaction { + // Create a trivial transaction with an output that describes the attachment, and the attachment itself + val ptx = TransactionBuilder(notary) + .addOutputState(AttachmentContract.State(attachId), ATTACHMENT_PROGRAM_ID) + .addCommand(AttachmentContract.Command, ourIdentity.owningKey) + .addAttachment(attachId) + + progressTracker.currentStep = SIGNING + + val stx = serviceHub.signInitialTransaction(ptx) + + // Send the transaction to the other recipient + return subFlow(FinalityFlow(stx, initiateFlow(otherSide))) + } +} + +@InitiatedBy(AttachmentFlow::class) +class StoreAttachmentFlow(private val otherSide: FlowSession) : FlowLogic() { + @Suspendable + override fun call() { + // purposely enable transaction verification and recording in ReceiveTransactionFlow + subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = true, statesToRecord = StatesToRecord.ALL_VISIBLE)) + logger.info("StoreAttachmentFlow: successfully received fully signed tx. Sending it to the vault for processing.") + } +} + diff --git a/node/src/integration-test/kotlin/net/corda/node/CashIssueAndPaymentTest.kt b/node/src/integration-test/kotlin/net/corda/node/CashIssueAndPaymentTest.kt index 2da38e1509..93b08b7842 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CashIssueAndPaymentTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CashIssueAndPaymentTest.kt @@ -1,10 +1,16 @@ package net.corda.node import net.corda.core.messaging.startFlow +import net.corda.core.messaging.vaultTrackBy +import net.corda.core.node.services.Vault +import net.corda.core.node.services.vault.DEFAULT_PAGE_NUM +import net.corda.core.node.services.vault.PageSpecification +import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.loggerFor import net.corda.finance.DOLLARS +import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.node.services.config.NodeConfiguration import net.corda.testing.core.ALICE_NAME @@ -17,6 +23,8 @@ import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.findCordapp import org.junit.Test import org.junit.jupiter.api.assertDoesNotThrow +import java.util.concurrent.CountDownLatch +import kotlin.test.assertEquals /** * Execute a flow with sub-flows, including the finality flow. @@ -65,4 +73,36 @@ class CashIssueAndPaymentTest { logger.info("TXN={}, recipient={}", result.stx, result.recipient) } } + + @Test(timeout = 300_000) + fun `test can issue cash and see consumming transaction id in rpc client`() { + driver(parametersFor()) { + val alice = startNode(providedName = ALICE_NAME, customOverrides = configOverrides).getOrThrow() + val aliceParty = alice.nodeInfo.singleIdentity() + val notaryParty = notaryHandles.single().identity + val result = assertDoesNotThrow { + + val criteria = QueryCriteria.VaultQueryCriteria(Vault.StateStatus.CONSUMED) + val (_, vaultUpdates) = alice.rpc.vaultTrackBy(criteria = criteria, paging = PageSpecification(DEFAULT_PAGE_NUM)) + val updateLatch = CountDownLatch(1) + vaultUpdates.subscribe { update -> + val consumedRef = update.consumed.single().ref + assertEquals( update.produced.single().ref.txhash, update.consumingTxIds[consumedRef] ) + updateLatch.countDown() + } + val flowRet = alice.rpc.startFlow(::CashIssueAndPaymentFlow, + CASH_AMOUNT, + OpaqueBytes.of(0x01), + aliceParty, + false, + notaryParty + ).use { flowHandle -> + flowHandle.returnValue.getOrThrow() + } + updateLatch.await() + flowRet + } + logger.info("TXN={}, recipient={}", result.stx, result.recipient) + } + } } diff --git a/node/src/integration-test/kotlin/net/corda/node/DeterministicSourcesRule.kt b/node/src/integration-test/kotlin/net/corda/node/DeterministicSourcesRule.kt deleted file mode 100644 index 4171f40fda..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/DeterministicSourcesRule.kt +++ /dev/null @@ -1,31 +0,0 @@ -package net.corda.node - -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement -import java.io.File -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import kotlin.test.fail - -class DeterministicSourcesRule : TestRule { - private var deterministicRt: Path? = null - private var deterministicSources: List? = null - - val bootstrap: Path get() = deterministicRt ?: fail("deterministic-rt.path property not set") - val corda: List get() = deterministicSources ?: fail("deterministic-sources.path property not set") - - override fun apply(statement: Statement, description: Description?): Statement { - deterministicRt = System.getProperty("deterministic-rt.path")?.run { Paths.get(this) } - deterministicSources = System.getProperty("deterministic-sources.path")?.split(File.pathSeparator) - ?.map { Paths.get(it) } - ?.filter { Files.exists(it) } - - return object : Statement() { - override fun evaluate() { - statement.evaluate() - } - } - } -} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/VaultUpdateDeserializationTest.kt b/node/src/integration-test/kotlin/net/corda/node/VaultUpdateDeserializationTest.kt new file mode 100644 index 0000000000..48aadf45d4 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/VaultUpdateDeserializationTest.kt @@ -0,0 +1,244 @@ +package net.corda.node + +import co.paralleluniverse.strands.Strand +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertNotNull +import junit.framework.TestCase.assertTrue +import net.corda.core.internal.InputStreamAndHash +import net.corda.core.internal.deleteRecursively +import net.corda.core.internal.div +import net.corda.core.messaging.startFlow +import net.corda.core.messaging.vaultQueryBy +import net.corda.core.utilities.getOrThrow +import net.corda.core.utilities.seconds +import net.corda.flows.incompatible.version1.AttachmentIssueFlow +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.singleIdentity +import net.corda.testing.driver.DriverParameters +import net.corda.testing.driver.NodeHandle +import net.corda.testing.driver.NodeParameters +import net.corda.testing.driver.OutOfProcess +import net.corda.testing.driver.driver +import net.corda.testing.driver.internal.incrementalPortAllocation +import net.corda.testing.flows.waitForAllFlowsToComplete +import net.corda.testing.node.NotarySpec +import net.corda.testing.node.TestCordapp +import net.corda.testing.node.internal.cordappWithPackages +import org.junit.Test +import java.util.concurrent.TimeoutException +import net.corda.contracts.incompatible.version1.AttachmentContract as AttachmentContractV1 +import net.corda.flows.incompatible.version1.AttachmentFlow as AttachmentFlowV1 + +class VaultUpdateDeserializationTest { + companion object { + // uses ReceiveFinalityFlow + val flowVersion1 = cordappWithPackages("net.corda.flows.incompatible.version1") + // single state field of type SecureHash.SHA256 with system property driven run-time behaviour: + // -force contract verify failure: -Dnet.corda.contracts.incompatible.AttachmentContract.fail.verify=true + // -force contract state init failure: -Dnet.corda.contracts.incompatible.AttachmentContract.fail.state=true + val contractVersion1 = cordappWithPackages("net.corda.contracts.incompatible.version1") + + fun driverParameters(cordapps: List): DriverParameters { + return DriverParameters( + portAllocation = incrementalPortAllocation(), + inMemoryDB = false, + startNodesInProcess = false, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME)), + cordappsForAllNodes = cordapps + ) + } + } + + /* + * Transaction sent from A -> B with Notarisation + * Test that a deserialization error is raised where the receiver node of a transaction has an incompatible contract jar. + * But only on new transactions, and not in the back chain. + * In the case of a notarised transaction, a deserialisation error is thrown in the receiver in the second phase of finality + * when updating the vault. The sender will not block, and the back chain is successfully recorded + * on the receiver even though those states have deserialization errors too. The flow on the receiver is hospitalised. + * The flow will be retried by the receiver upon installing the correct contract jar + * version at the receiver and re-starting the node. + */ + @Test(timeout = 300_000) + fun `Notarised transaction fails but back chain succeeds upon receiver deserialization failure when using incompatible contract jar`() { + driver(driverParameters(listOf(flowVersion1, contractVersion1))) { + val alice = startNode(NodeParameters(additionalCordapps = listOf(flowVersion1, contractVersion1)), + providedName = ALICE_NAME).getOrThrow() + val bob = startNode(NodeParameters(additionalCordapps = listOf(flowVersion1, contractVersion1), + systemProperties = mapOf("net.corda.contracts.incompatible.AttachmentContract.fail.state" to "true")), + providedName = BOB_NAME).getOrThrow() + + val (inputStream, hash) = InputStreamAndHash.createInMemoryTestZip(1024, 0) + alice.rpc.uploadAttachment(inputStream) + + val stx = alice.rpc.startFlow(::AttachmentIssueFlow, hash, defaultNotaryIdentity).returnValue.getOrThrow(30.seconds) + val spendableState = stx.coreTransaction.outRef(0) + + alice.rpc.startFlow(::AttachmentFlowV1, bob.nodeInfo.singleIdentity(), defaultNotaryIdentity, hash, spendableState).returnValue.getOrThrow(30.seconds) + + assertEquals(0, bob.rpc.vaultQueryBy().states.size) + assertEquals(1, alice.rpc.vaultQueryBy().states.size) + // check transaction records + @Suppress("DEPRECATION") + assertEquals(2, alice.rpc.internalVerifiedTransactionsSnapshot().size) // both + @Suppress("DEPRECATION") + assertEquals(1, bob.rpc.internalVerifiedTransactionsSnapshot().size) // issuance only + + // restart Bob with correct contract jar version + (bob as OutOfProcess).process.destroyForcibly() + bob.stop() + (baseDirectory(BOB_NAME) / "cordapps").deleteRecursively() + + val restartedBob = startNode(NodeParameters(additionalCordapps = listOf(flowVersion1, contractVersion1)), + providedName = BOB_NAME).getOrThrow() + // original hospitalized transaction should now have been re-processed with correct contract jar + assertEquals(1, waitForVaultUpdate(restartedBob)) + assertEquals(1, alice.rpc.vaultQueryBy().states.size) + @Suppress("DEPRECATION") + assertEquals(2, restartedBob.rpc.internalVerifiedTransactionsSnapshot().size) // both + assertEquals(1, restartedBob.rpc.vaultQueryBy().states.size) + } + } + + /* + * Transaction sent from A -> B with Notarisation + * + * Test original deserialization failure behaviour by setting a new configurable java system property. + * The ledger will enter an inconsistent state from which is cannot auto-recover. + */ + @Test(timeout=300_000) + fun `Notarised transaction when using incompatible contract jar and overriden system property`() { + driver(driverParameters(listOf(flowVersion1, contractVersion1))) { + val alice = startNode(NodeParameters(additionalCordapps = listOf(flowVersion1, contractVersion1)), + providedName = ALICE_NAME).getOrThrow() + val bob = startNode(NodeParameters(additionalCordapps = listOf(flowVersion1, contractVersion1), + systemProperties = mapOf("net.corda.contracts.incompatible.AttachmentContract.fail.state" to "true", + "net.corda.vaultupdate.ignore.transaction.deserialization.errors" to "true", + "net.corda.recordtransaction.signature.verification.disabled" to "true")), + providedName = BOB_NAME).getOrThrow() + + val (inputStream, hash) = InputStreamAndHash.createInMemoryTestZip(1024, 0) + alice.rpc.uploadAttachment(inputStream) + + val stx = alice.rpc.startFlow(::AttachmentIssueFlow, hash, defaultNotaryIdentity).returnValue.getOrThrow(30.seconds) + val spendableState = stx.coreTransaction.outRef(0) + + // Flow completes successfully (deserialisation error on Receiver node is ignored) + alice.rpc.startFlow(::AttachmentFlowV1, bob.nodeInfo.singleIdentity(), defaultNotaryIdentity, hash, spendableState).returnValue.getOrThrow(30.seconds) + + // sender node correctly updated + @Suppress("DEPRECATION") + assertEquals(2, alice.rpc.internalVerifiedTransactionsSnapshot().size) + assertEquals(1, alice.rpc.vaultQueryBy().states.size) + + // receiver node has transaction but vault not updated! + @Suppress("DEPRECATION") + assertEquals(2, bob.rpc.internalVerifiedTransactionsSnapshot().size) + assertEquals(0, bob.rpc.vaultQueryBy().states.size) + } + } + + /* + * Transaction sent from A -> B without Notarisation + * Test that a deserialization error is raised where the receiver node of a finality flow has an incompatible contract jar. + * The ledger will be temporarily inconsistent until the correct contract jar version is installed and the receiver node is re-started. + */ + @Test(timeout=300_000) + fun `un-notarised transaction is hospitalized at receiver upon deserialization failure in vault update when using incompatible contract jar`() { + driver(driverParameters(emptyList())) { + val alice = startNode(NodeParameters(additionalCordapps = listOf(flowVersion1, contractVersion1)), + providedName = ALICE_NAME).getOrThrow() + val bob = startNode(NodeParameters(additionalCordapps = listOf(flowVersion1, contractVersion1), + systemProperties = mapOf("net.corda.contracts.incompatible.AttachmentContract.fail.state" to "true")), + providedName = BOB_NAME).getOrThrow() + + val (inputStream, hash) = InputStreamAndHash.createInMemoryTestZip(1024, 0) + alice.rpc.uploadAttachment(inputStream) + + // ISSUE: exception is not propagating from Receiver + try { + alice.rpc.startFlow(::AttachmentFlowV1, bob.nodeInfo.singleIdentity(), defaultNotaryIdentity, hash, null).returnValue.getOrThrow(30.seconds) + } + catch(e: TimeoutException) { + println("Alice: Timeout awaiting flow completion.") + } + assertEquals(0, bob.rpc.vaultQueryBy().states.size) + // check transaction records + @Suppress("DEPRECATION") + assertTrue(alice.rpc.internalVerifiedTransactionsSnapshot().isNotEmpty()) + @Suppress("DEPRECATION") + assertTrue(bob.rpc.internalVerifiedTransactionsSnapshot().isEmpty()) + + // restart Bob with correct contract jar version + (bob as OutOfProcess).process.destroyForcibly() + bob.stop() + (baseDirectory(BOB_NAME) / "cordapps").deleteRecursively() + + val restartedBob = startNode(NodeParameters(additionalCordapps = listOf(flowVersion1, contractVersion1)), + providedName = BOB_NAME).getOrThrow() + // original hospitalized transaction should now have been re-processed with correct contract jar + assertEquals(1, waitForVaultUpdate(restartedBob)) + @Suppress("DEPRECATION") + assertTrue(restartedBob.rpc.internalVerifiedTransactionsSnapshot().isNotEmpty()) + } + } + + /* + * Transaction sent from A -> B without Notarisation + * Test original deserialization failure behaviour by setting a new configurable java system property. + * The ledger will enter an inconsistent state from which is cannot auto-recover. + */ + @Test(timeout = 300_000) + fun `un-notarised transaction ignores deserialization failure in vault update when using incompatible contract jar and overriden system property`() { + driver(driverParameters(emptyList())) { + val alice = startNode(NodeParameters(additionalCordapps = listOf(flowVersion1, contractVersion1)), + providedName = ALICE_NAME).getOrThrow() + val bob = startNode(NodeParameters(additionalCordapps = listOf(flowVersion1, contractVersion1), + systemProperties = mapOf( + "net.corda.contracts.incompatible.AttachmentContract.fail.state" to "true", + "net.corda.vaultupdate.ignore.transaction.deserialization.errors" to "true", + "net.corda.recordtransaction.signature.verification.disabled" to "true")), + providedName = BOB_NAME).getOrThrow() + + val (inputStream, hash) = InputStreamAndHash.createInMemoryTestZip(1024, 0) + alice.rpc.uploadAttachment(inputStream) + + // Note: TransactionDeserialisationException is swallowed on the receiver node (without updating the vault). + val stx = alice.rpc.startFlow(::AttachmentFlowV1, bob.nodeInfo.singleIdentity(), defaultNotaryIdentity, hash, null).returnValue.getOrThrow(30.seconds) + println("Alice txId: ${stx.id}") + + waitForAllFlowsToComplete(bob) + val txId = bob.rpc.stateMachineRecordedTransactionMappingSnapshot().single().transactionId + println("Bob txId: $txId") + + assertEquals(0, bob.rpc.vaultQueryBy().states.size) + + // restart Bob with correct contract jar version + (bob as OutOfProcess).process.destroyForcibly() + bob.stop() + (baseDirectory(BOB_NAME) / "cordapps").deleteRecursively() + + val restartedBob = startNode(NodeParameters(additionalCordapps = listOf(flowVersion1, contractVersion1)), + providedName = BOB_NAME).getOrThrow() + // transaction recorded + @Suppress("DEPRECATION") + assertNotNull(restartedBob.rpc.internalFindVerifiedTransaction(txId)) + // but vault states not updated + assertEquals(0, restartedBob.rpc.vaultQueryBy().states.size) + } + } + + private fun waitForVaultUpdate(nodeHandle: NodeHandle, maxIterations: Int = 5, iterationDelay: Long = 500): Int { + repeat((0..maxIterations).count()) { + val count = nodeHandle.rpc.vaultQueryBy().states + if (count.isNotEmpty()) { + return count.size + } + Strand.sleep(iterationDelay) + } + return 0 + } +} + diff --git a/node/src/integration-test/kotlin/net/corda/node/flows/FlowReloadAfterCheckpointTest.kt b/node/src/integration-test/kotlin/net/corda/node/flows/FlowReloadAfterCheckpointTest.kt index add9ecb651..7f83f94cb6 100644 --- a/node/src/integration-test/kotlin/net/corda/node/flows/FlowReloadAfterCheckpointTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/flows/FlowReloadAfterCheckpointTest.kt @@ -336,8 +336,8 @@ class FlowReloadAfterCheckpointTest { .toSet() .single() reloads.await(DEFAULT_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) - assertEquals(7, reloads.filter { it == flowStartedByAlice }.size) - assertEquals(6, reloads.filter { it == flowStartedByBob }.size) + assertEquals(8, reloads.filter { it == flowStartedByAlice }.size) + assertEquals(7, reloads.filter { it == flowStartedByBob }.size) } } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index 7dda30ca06..e62b1f4883 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -45,7 +45,7 @@ class AttachmentLoadingTests { } @Test(timeout=300_000) - fun `contracts downloaded from the network are not executed without the DJVM`() { + fun `contracts downloaded from the network are not executed`() { driver(DriverParameters( startNodesInProcess = false, notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = false)), diff --git a/node/src/integration-test/kotlin/net/corda/node/services/CordaServiceLifecycleTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/CordaServiceLifecycleTests.kt index e615e91a60..93e0f7e10f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/CordaServiceLifecycleTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/CordaServiceLifecycleTests.kt @@ -156,6 +156,6 @@ class CordaServiceLifecycleTests { /** * Given an event, was the SMM started when the event was received? */ - fun getSmmStartedForEvent(event: ServiceLifecycleEvent) : Int = smmStateAtEvent.getOrDefault(event, STATE_MACHINE_MANAGER_UNKNOWN_STATUS) + fun getSmmStartedForEvent(event: ServiceLifecycleEvent) : Int = smmStateAtEvent[event] ?: checkSmmStarted() } } \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicCashIssueAndPaymentTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicCashIssueAndPaymentTest.kt deleted file mode 100644 index 9b3bdf77ef..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicCashIssueAndPaymentTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -package net.corda.node.services - -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.loggerFor -import net.corda.finance.DOLLARS -import net.corda.finance.flows.CashIssueAndPaymentFlow -import net.corda.node.DeterministicSourcesRule -import net.corda.node.services.config.NodeConfiguration -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.core.singleIdentity -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.driver -import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.node.NotarySpec -import net.corda.testing.node.internal.findCordapp -import org.junit.ClassRule -import org.junit.Test -import org.junit.jupiter.api.assertDoesNotThrow - -@Suppress("FunctionName") -class DeterministicCashIssueAndPaymentTest { - companion object { - private val logger = loggerFor() - - private val configOverrides = mapOf(NodeConfiguration::reloadCheckpointAfterSuspend.name to true) - private val CASH_AMOUNT = 500.DOLLARS - - @ClassRule - @JvmField - val djvmSources = DeterministicSourcesRule() - - fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters { - return DriverParameters( - portAllocation = incrementalPortAllocation(), - startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), - notaryCustomOverrides = configOverrides, - cordappsForAllNodes = listOf( - findCordapp("net.corda.finance.contracts"), - findCordapp("net.corda.finance.workflows") - ), - djvmBootstrapSource = djvmSources.bootstrap, - djvmCordaSource = djvmSources.corda - ) - } - } - - @Test(timeout = 300_000) - fun `test DJVM can issue cash`() { - val reference = OpaqueBytes.of(0x01) - driver(parametersFor(djvmSources)) { - val alice = startNode(providedName = ALICE_NAME, customOverrides = configOverrides).getOrThrow() - val aliceParty = alice.nodeInfo.singleIdentity() - val notaryParty = notaryHandles.single().identity - val txId = assertDoesNotThrow { - alice.rpc.startFlow(::CashIssueAndPaymentFlow, - CASH_AMOUNT, - reference, - aliceParty, - false, - notaryParty - ).use { flowHandle -> - flowHandle.returnValue.getOrThrow() - } - } - logger.info("TX-ID: {}", txId) - } - } -} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCannotMutateTransactionTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCannotMutateTransactionTest.kt deleted file mode 100644 index 41c80ea7d9..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCannotMutateTransactionTest.kt +++ /dev/null @@ -1,55 +0,0 @@ -package net.corda.node.services - -import net.corda.client.rpc.CordaRPCClient -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.loggerFor -import net.corda.flows.mutator.MutatorFlow -import net.corda.node.DeterministicSourcesRule -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.driver -import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.node.NotarySpec -import net.corda.testing.node.User -import net.corda.testing.node.internal.cordappWithPackages -import org.junit.ClassRule -import org.junit.Test - -class DeterministicContractCannotMutateTransactionTest { - companion object { - private val logger = loggerFor() - private val user = User("u", "p", setOf(Permissions.all())) - private val mutatorFlowCorDapp = cordappWithPackages("net.corda.flows.mutator").signed() - private val mutatorContractCorDapp = cordappWithPackages("net.corda.contracts.mutator").signed() - - @ClassRule - @JvmField - val djvmSources = DeterministicSourcesRule() - - fun driverParameters(runInProcess: Boolean = false): DriverParameters { - return DriverParameters( - portAllocation = incrementalPortAllocation(), - startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), - cordappsForAllNodes = listOf(mutatorContractCorDapp, mutatorFlowCorDapp), - djvmBootstrapSource = djvmSources.bootstrap, - djvmCordaSource = djvmSources.corda - ) - } - } - - @Test(timeout = 300_000) - fun testContractCannotModifyTransaction() { - driver(driverParameters()) { - val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() - val txID = CordaRPCClient(hostAndPort = alice.rpcAddress) - .start(user.username, user.password) - .use { client -> - client.proxy.startFlow(::MutatorFlow).returnValue.getOrThrow() - } - logger.info("TX-ID: {}", txID) - } - } -} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCryptoTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCryptoTest.kt deleted file mode 100644 index 5d28ae41b8..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractCryptoTest.kt +++ /dev/null @@ -1,73 +0,0 @@ -package net.corda.node.services - -import net.corda.contracts.djvm.crypto.DeterministicCryptoContract.Validate -import net.corda.core.crypto.Crypto -import net.corda.core.crypto.Crypto.DEFAULT_SIGNATURE_SCHEME -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.loggerFor -import net.corda.flows.djvm.crypto.DeterministicCryptoFlow -import net.corda.node.DeterministicSourcesRule -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.driver -import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.node.NotarySpec -import net.corda.testing.node.internal.CustomCordapp -import net.corda.testing.node.internal.cordappWithPackages -import org.junit.ClassRule -import org.junit.Test -import org.junit.jupiter.api.assertDoesNotThrow -import java.security.KeyPairGenerator - -@Suppress("FunctionName") -class DeterministicContractCryptoTest { - companion object { - const val MESSAGE = "Very Important Data! Do Not Change!" - val logger = loggerFor() - - @ClassRule - @JvmField - val djvmSources = DeterministicSourcesRule() - - fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters { - return DriverParameters( - portAllocation = incrementalPortAllocation(), - startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), - cordappsForAllNodes = listOf( - cordappWithPackages("net.corda.flows.djvm.crypto"), - CustomCordapp( - packages = setOf("net.corda.contracts.djvm.crypto"), - name = "deterministic-crypto-contract" - ).signed() - ), - djvmBootstrapSource = djvmSources.bootstrap, - djvmCordaSource = djvmSources.corda - ) - } - } - - @Test(timeout=300_000) - fun `test DJVM can verify using crypto`() { - val keyPair = KeyPairGenerator.getInstance(DEFAULT_SIGNATURE_SCHEME.algorithmName).genKeyPair() - val importantData = OpaqueBytes(MESSAGE.toByteArray()) - val signature = OpaqueBytes(Crypto.doSign(DEFAULT_SIGNATURE_SCHEME, keyPair.`private`, importantData.bytes)) - - val validate = Validate( - schemeCodeName = DEFAULT_SIGNATURE_SCHEME.schemeCodeName, - publicKey = keyPair.`public` - ) - - driver(parametersFor(djvmSources)) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val txId = assertDoesNotThrow { - alice.rpc.startFlow(::DeterministicCryptoFlow, validate, importantData, signature) - .returnValue.getOrThrow() - } - logger.info("TX-ID: {}", txId) - } - } -} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithCustomSerializerTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithCustomSerializerTest.kt deleted file mode 100644 index 447cd2d6a6..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithCustomSerializerTest.kt +++ /dev/null @@ -1,89 +0,0 @@ -package net.corda.node.services - -import net.corda.contracts.serialization.custom.Currantsy -import net.corda.contracts.serialization.custom.CustomSerializerContract -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.loggerFor -import net.corda.flows.serialization.custom.CustomSerializerFlow -import net.corda.node.DeterministicSourcesRule -import net.corda.node.assertNotCordaSerializable -import net.corda.node.internal.djvm.DeterministicVerificationException -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.driver -import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.node.NotarySpec -import net.corda.testing.node.TestCordapp -import net.corda.testing.node.internal.cordappWithPackages -import org.assertj.core.api.Assertions.assertThat -import org.junit.BeforeClass -import org.junit.ClassRule -import org.junit.Test -import org.junit.jupiter.api.assertDoesNotThrow -import org.junit.jupiter.api.assertThrows - -@Suppress("FunctionName") -class DeterministicContractWithCustomSerializerTest { - companion object { - val logger = loggerFor() - const val GOOD_CURRANTS = 1201L - const val BAD_CURRANTS = 4703L - - @ClassRule - @JvmField - val djvmSources = DeterministicSourcesRule() - - @JvmField - val flowCordapp = cordappWithPackages("net.corda.flows.serialization.custom").signed() - - @JvmField - val contractCordapp = cordappWithPackages("net.corda.contracts.serialization.custom").signed() - - fun parametersFor(djvmSources: DeterministicSourcesRule, cordapps: List, runInProcess: Boolean = false): DriverParameters { - return DriverParameters( - portAllocation = incrementalPortAllocation(), - startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), - cordappsForAllNodes = cordapps.toList(), - djvmBootstrapSource = djvmSources.bootstrap, - djvmCordaSource = djvmSources.corda - ) - } - - @BeforeClass - @JvmStatic - fun checkData() { - assertNotCordaSerializable() - } - } - - @Test(timeout=300_000) - fun `test DJVM can verify using custom serializer`() { - driver(parametersFor(djvmSources, listOf(flowCordapp, contractCordapp))) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val txId = assertDoesNotThrow { - alice.rpc.startFlow(::CustomSerializerFlow, Currantsy(GOOD_CURRANTS)) - .returnValue.getOrThrow() - } - logger.info("TX-ID: {}", txId) - } - } - - @Test(timeout=300_000) - fun `test DJVM can fail verify using custom serializer`() { - driver(parametersFor(djvmSources, listOf(flowCordapp, contractCordapp))) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val currantsy = Currantsy(BAD_CURRANTS) - val ex = assertThrows { - alice.rpc.startFlow(::CustomSerializerFlow, currantsy) - .returnValue.getOrThrow() - } - assertThat(ex) - .hasMessageStartingWith("sandbox.net.corda.core.contracts.TransactionVerificationException\$ContractRejection -> ") - .hasMessageContaining(" Contract verification failed: Too many currants! $currantsy is unraisinable!, ") - .hasMessageContaining(" contract: sandbox.${CustomSerializerContract::class.java.name}, ") - } - } -} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithGenericTypeTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithGenericTypeTest.kt deleted file mode 100644 index d2cae60136..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithGenericTypeTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -package net.corda.node.services - -import net.corda.client.rpc.CordaRPCClient -import net.corda.contracts.serialization.generics.DataObject -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.loggerFor -import net.corda.flows.serialization.generics.GenericTypeFlow -import net.corda.node.DeterministicSourcesRule -import net.corda.node.internal.djvm.DeterministicVerificationException -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.driver -import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.node.NotarySpec -import net.corda.testing.node.User -import net.corda.testing.node.internal.cordappWithPackages -import org.assertj.core.api.Assertions.assertThat -import org.junit.ClassRule -import org.junit.Test -import org.junit.jupiter.api.assertThrows - -@Suppress("FunctionName") -class DeterministicContractWithGenericTypeTest { - companion object { - const val DATA_VALUE = 5000L - - @JvmField - val logger = loggerFor() - - @JvmField - val user = User("u", "p", setOf(Permissions.all())) - - @ClassRule - @JvmField - val djvmSources = DeterministicSourcesRule() - - fun parameters(runInProcess: Boolean = false): DriverParameters { - return DriverParameters( - portAllocation = incrementalPortAllocation(), - startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), - cordappsForAllNodes = listOf( - cordappWithPackages("net.corda.flows.serialization.generics").signed(), - cordappWithPackages("net.corda.contracts.serialization.generics").signed() - ), - djvmBootstrapSource = djvmSources.bootstrap, - djvmCordaSource = djvmSources.corda - ) - } - } - - @Test(timeout = 300_000) - fun `test DJVM can deserialise command with generic type`() { - driver(parameters()) { - val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() - val txID = CordaRPCClient(hostAndPort = alice.rpcAddress) - .start(user.username, user.password) - .use { client -> - client.proxy.startFlow(::GenericTypeFlow, DataObject(DATA_VALUE)) - .returnValue - .getOrThrow() - } - logger.info("TX-ID=$txID") - } - } - - @Test(timeout = 300_000) - fun `test DJVM can deserialise command without value of generic type`() { - driver(parameters()) { - val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() - val ex = assertThrows { - CordaRPCClient(hostAndPort = alice.rpcAddress) - .start(user.username, user.password) - .use { client -> - client.proxy.startFlow(::GenericTypeFlow, null) - .returnValue - .getOrThrow() - } - } - assertThat(ex).hasMessageContaining("Contract verification failed: Failed requirement: Purchase has a price,") - } - } -} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithSerializationWhitelistTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithSerializationWhitelistTest.kt deleted file mode 100644 index 9b0e057453..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicContractWithSerializationWhitelistTest.kt +++ /dev/null @@ -1,89 +0,0 @@ -package net.corda.node.services - -import net.corda.contracts.djvm.whitelist.DeterministicWhitelistContract -import net.corda.contracts.djvm.whitelist.WhitelistData -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.loggerFor -import net.corda.flows.djvm.whitelist.DeterministicWhitelistFlow -import net.corda.node.DeterministicSourcesRule -import net.corda.node.assertNotCordaSerializable -import net.corda.node.internal.djvm.DeterministicVerificationException -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.driver -import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.node.NotarySpec -import net.corda.testing.node.TestCordapp -import net.corda.testing.node.internal.cordappWithPackages -import org.assertj.core.api.Assertions.assertThat -import org.junit.BeforeClass -import org.junit.ClassRule -import org.junit.Test -import org.junit.jupiter.api.assertDoesNotThrow -import org.junit.jupiter.api.assertThrows - -@Suppress("FunctionName") -class DeterministicContractWithSerializationWhitelistTest { - companion object { - val logger = loggerFor() - const val GOOD_VALUE = 1201L - const val BAD_VALUE = 6333L - - @ClassRule - @JvmField - val djvmSources = DeterministicSourcesRule() - - @JvmField - val flowCordapp = cordappWithPackages("net.corda.flows.djvm.whitelist").signed() - - @JvmField - val contractCordapp = cordappWithPackages("net.corda.contracts.djvm.whitelist").signed() - - fun parametersFor(djvmSources: DeterministicSourcesRule, cordapps: List, runInProcess: Boolean = false): DriverParameters { - return DriverParameters( - portAllocation = incrementalPortAllocation(), - startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), - cordappsForAllNodes = cordapps.toList(), - djvmBootstrapSource = djvmSources.bootstrap, - djvmCordaSource = djvmSources.corda - ) - } - - @BeforeClass - @JvmStatic - fun checkData() { - assertNotCordaSerializable() - } - } - - @Test(timeout=300_000) - fun `test DJVM can verify using whitelist`() { - driver(parametersFor(djvmSources, listOf(flowCordapp, contractCordapp))) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val txId = assertDoesNotThrow { - alice.rpc.startFlow(::DeterministicWhitelistFlow, WhitelistData(GOOD_VALUE)) - .returnValue.getOrThrow() - } - logger.info("TX-ID: {}", txId) - } - } - - @Test(timeout=300_000) - fun `test DJVM can fail verify using whitelist`() { - driver(parametersFor(djvmSources, listOf(flowCordapp, contractCordapp))) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val badData = WhitelistData(BAD_VALUE) - val ex = assertThrows { - alice.rpc.startFlow(::DeterministicWhitelistFlow, badData) - .returnValue.getOrThrow() - } - assertThat(ex) - .hasMessageStartingWith("sandbox.net.corda.core.contracts.TransactionVerificationException\$ContractRejection -> ") - .hasMessageContaining(" Contract verification failed: WhitelistData $badData exceeds maximum value!, ") - .hasMessageContaining(" contract: sandbox.${DeterministicWhitelistContract::class.java.name}, ") - } - } -} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicEvilContractCannotModifyStatesTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/DeterministicEvilContractCannotModifyStatesTest.kt deleted file mode 100644 index 5188f0b4fd..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/services/DeterministicEvilContractCannotModifyStatesTest.kt +++ /dev/null @@ -1,71 +0,0 @@ -package net.corda.node.services - -import net.corda.client.rpc.CordaRPCClient -import net.corda.contracts.multiple.vulnerable.MutableDataObject -import net.corda.contracts.multiple.vulnerable.VulnerablePaymentContract -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.getOrThrow -import net.corda.flows.multiple.evil.EvilFlow -import net.corda.node.DeterministicSourcesRule -import net.corda.node.internal.djvm.DeterministicVerificationException -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.driver -import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.node.NotarySpec -import net.corda.testing.node.User -import net.corda.testing.node.internal.cordappWithPackages -import org.assertj.core.api.Assertions.assertThat -import org.junit.ClassRule -import org.junit.Test -import kotlin.test.assertFailsWith - -class DeterministicEvilContractCannotModifyStatesTest { - companion object { - private val user = User("u", "p", setOf(Permissions.all())) - private val evilFlowCorDapp = cordappWithPackages("net.corda.flows.multiple.evil").signed() - private val evilContractCorDapp = cordappWithPackages("net.corda.contracts.multiple.evil").signed() - private val vulnerableContractCorDapp = cordappWithPackages("net.corda.contracts.multiple.vulnerable").signed() - - private val NOTHING = MutableDataObject(0) - - @ClassRule - @JvmField - val djvmSources = DeterministicSourcesRule() - - fun driverParameters(runInProcess: Boolean = false): DriverParameters { - return DriverParameters( - portAllocation = incrementalPortAllocation(), - startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), - cordappsForAllNodes = listOf( - vulnerableContractCorDapp, - evilContractCorDapp, - evilFlowCorDapp - ), - djvmBootstrapSource = djvmSources.bootstrap, - djvmCordaSource = djvmSources.corda - ) - } - } - - @Test(timeout = 300_000) - fun testContractThatTriesToModifyStates() { - val evilData = MutableDataObject(5000) - driver(driverParameters()) { - val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() - val ex = assertFailsWith { - CordaRPCClient(hostAndPort = alice.rpcAddress) - .start(user.username, user.password) - .use { client -> - client.proxy.startFlow(::EvilFlow, evilData).returnValue.getOrThrow() - } - } - assertThat(ex) - .hasMessageStartingWith("sandbox.net.corda.core.contracts.TransactionVerificationException\$ContractRejection -> ") - .hasMessageContaining(" Contract verification failed: Failed requirement: Purchase payment of $NOTHING should be at least ") - .hasMessageContaining(", contract: sandbox.${VulnerablePaymentContract::class.java.name}, ") - } - } -} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/NonDeterministicContractVerifyTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/NonDeterministicContractVerifyTest.kt deleted file mode 100644 index a200d5db41..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/services/NonDeterministicContractVerifyTest.kt +++ /dev/null @@ -1,133 +0,0 @@ -package net.corda.node.services - -import net.corda.contracts.djvm.broken.NonDeterministicContract.CurrentTimeMillis -import net.corda.contracts.djvm.broken.NonDeterministicContract.InstantNow -import net.corda.contracts.djvm.broken.NonDeterministicContract.NanoTime -import net.corda.contracts.djvm.broken.NonDeterministicContract.NoOperation -import net.corda.contracts.djvm.broken.NonDeterministicContract.RandomUUID -import net.corda.contracts.djvm.broken.NonDeterministicContract.WithReflection -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.loggerFor -import net.corda.flows.djvm.broken.NonDeterministicFlow -import net.corda.node.DeterministicSourcesRule -import net.corda.node.internal.djvm.DeterministicVerificationException -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.driver -import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.node.NotarySpec -import net.corda.testing.node.internal.CustomCordapp -import net.corda.testing.node.internal.cordappWithPackages -import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat -import org.junit.ClassRule -import org.junit.Test -import org.junit.jupiter.api.assertDoesNotThrow -import org.junit.jupiter.api.assertThrows - -@Suppress("FunctionName") -class NonDeterministicContractVerifyTest { - companion object { - val logger = loggerFor() - - @ClassRule - @JvmField - val djvmSources = DeterministicSourcesRule() - - fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters { - return DriverParameters( - portAllocation = incrementalPortAllocation(), - startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), - cordappsForAllNodes = listOf( - cordappWithPackages("net.corda.flows.djvm.broken"), - CustomCordapp( - packages = setOf("net.corda.contracts.djvm.broken"), - name = "nondeterministic-contract" - ).signed() - ), - djvmBootstrapSource = djvmSources.bootstrap, - djvmCordaSource = djvmSources.corda - ) - } - } - - @Test(timeout=300_000) - fun `test DJVM rejects contract that uses Instant now`() { - driver(parametersFor(djvmSources)) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val ex = assertThrows { - alice.rpc.startFlow(::NonDeterministicFlow, InstantNow()) - .returnValue.getOrThrow() - } - assertThat(ex) - .hasMessageMatching("^NoSuchMethodError: .*[\\Qsandbox.java.time.Instant.now()\\E|\\Qsandbox.java/time/Instant/now()\\E].*\$") - } - } - - @Test(timeout=300_000) - fun `test DJVM rejects contract that uses System currentTimeMillis`() { - driver(parametersFor(djvmSources)) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val ex = assertThrows { - alice.rpc.startFlow(::NonDeterministicFlow, CurrentTimeMillis()) - .returnValue.getOrThrow() - } - assertThat(ex) - .hasMessageStartingWith("RuleViolationError: Disallowed reference to API; java.lang.System.currentTimeMillis(), ") - } - } - - @Test(timeout=300_000) - fun `test DJVM rejects contract that uses System nanoTime`() { - driver(parametersFor(djvmSources)) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val ex = assertThrows { - alice.rpc.startFlow(::NonDeterministicFlow, NanoTime()) - .returnValue.getOrThrow() - } - assertThat(ex) - .hasMessageStartingWith("RuleViolationError: Disallowed reference to API; java.lang.System.nanoTime(), ") - } - } - - @Test(timeout=300_000) - fun `test DJVM rejects contract that uses UUID randomUUID`() { - driver(parametersFor(djvmSources)) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val ex = assertThrows { - alice.rpc.startFlow(::NonDeterministicFlow, RandomUUID()) - .returnValue.getOrThrow() - } - assertThat(ex) - .hasMessageMatching("^NoSuchMethodError: .*[\\Qsandbox.java.util.UUID.randomUUID()\\E|\\Qsandbox/java/util/UUID/randomUUID()\\E].*\$") - } - } - - @Test(timeout=300_000) - fun `test DJVM rejects contract that uses reflection`() { - driver(parametersFor(djvmSources)) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val ex = assertThrows { - alice.rpc.startFlow(::NonDeterministicFlow, WithReflection()) - .returnValue.getOrThrow() - } - assertThat(ex).hasMessageStartingWith( - "RuleViolationError: Disallowed reference to API; java.lang.Class.getDeclaredConstructor(Class[]), " - ) - } - } - - @Test(timeout=300_000) - fun `test DJVM can succeed`() { - driver(parametersFor(djvmSources)) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val txId = assertDoesNotThrow { - alice.rpc.startFlow(::NonDeterministicFlow, NoOperation()) - .returnValue.getOrThrow() - } - logger.info("TX-ID: {}", txId) - } - } -} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/SandboxAttachmentsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/SandboxAttachmentsTest.kt deleted file mode 100644 index e868566f58..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/services/SandboxAttachmentsTest.kt +++ /dev/null @@ -1,80 +0,0 @@ -package net.corda.node.services - -import net.corda.contracts.djvm.attachment.SandboxAttachmentContract -import net.corda.contracts.djvm.attachment.SandboxAttachmentContract.ExtractFile -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.loggerFor -import net.corda.flows.djvm.attachment.SandboxAttachmentFlow -import net.corda.node.DeterministicSourcesRule -import net.corda.node.internal.djvm.DeterministicVerificationException -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.driver -import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.node.NotarySpec -import net.corda.testing.node.internal.CustomCordapp -import net.corda.testing.node.internal.cordappWithPackages -import org.assertj.core.api.Assertions.assertThat -import org.junit.ClassRule -import org.junit.Test -import org.junit.jupiter.api.assertDoesNotThrow -import org.junit.jupiter.api.assertThrows - -@Suppress("FunctionName") -class SandboxAttachmentsTest { - companion object { - val logger = loggerFor() - - @ClassRule - @JvmField - val djvmSources = DeterministicSourcesRule() - - fun parametersFor(djvmSources: DeterministicSourcesRule, runInProcess: Boolean = false): DriverParameters { - return DriverParameters( - portAllocation = incrementalPortAllocation(), - startNodesInProcess = runInProcess, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = runInProcess, validating = true)), - cordappsForAllNodes = listOf( - cordappWithPackages("net.corda.flows.djvm.attachment"), - CustomCordapp( - packages = setOf("net.corda.contracts.djvm.attachment"), - name = "sandbox-attachment-contract" - ).signed() - ), - djvmBootstrapSource = djvmSources.bootstrap, - djvmCordaSource = djvmSources.corda - ) - } - } - - @Test(timeout=300_000) - fun `test attachment accessible within sandbox`() { - val extractFile = ExtractFile(SandboxAttachmentContract::class.java.name.replace('.', '/') + ".class") - driver(parametersFor(djvmSources)) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val txId = assertDoesNotThrow { - alice.rpc.startFlow(::SandboxAttachmentFlow, extractFile) - .returnValue.getOrThrow() - } - logger.info("TX-ID: {}", txId) - } - } - - @Test(timeout=300_000) - fun `test attachment file not found within sandbox`() { - val extractFile = ExtractFile("does/not/Exist.class") - driver(parametersFor(djvmSources)) { - val alice = startNode(providedName = ALICE_NAME).getOrThrow() - val ex = assertThrows { - alice.rpc.startFlow(::SandboxAttachmentFlow, extractFile) - .returnValue.getOrThrow() - } - assertThat(ex) - .hasMessageStartingWith("sandbox.net.corda.core.contracts.TransactionVerificationException\$ContractRejection -> ") - .hasMessageContaining(" Contract verification failed: does/not/Exist.class, ") - .hasMessageContaining(" contract: sandbox.net.corda.contracts.djvm.attachment.SandboxAttachmentContract, ") - } - } -} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/identity/CertificateRotationTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/identity/CertificateRotationTest.kt index 3b59ffa55c..34ad626ef5 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/identity/CertificateRotationTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/identity/CertificateRotationTest.kt @@ -42,7 +42,9 @@ class CertificateRotationTest { @After fun tearDown() { - mockNet.stopNodes() + if (::mockNet.isInitialized) { + mockNet.stopNodes() + } } @Test(timeout = 300_000) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt index 6a690004d4..a3738df972 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt @@ -7,6 +7,7 @@ import net.corda.core.messaging.ParametersUpdateInfo import net.corda.core.node.NetworkParameters import net.corda.core.node.NodeInfo import net.corda.core.serialization.serialize +import net.corda.core.utilities.days import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.nodeapi.internal.SignedNodeInfo @@ -76,7 +77,11 @@ class NetworkMapTest { val nextParams = networkMapServer.networkParameters.copy( epoch = 3, modifiedTime = Instant.ofEpochMilli(random63BitValue()), - maxMessageSize = networkMapServer.networkParameters.maxMessageSize + 1) + maxMessageSize = networkMapServer.networkParameters.maxMessageSize + 1, + recoveryMaximumBackupInterval = networkMapServer.networkParameters + .recoveryMaximumBackupInterval?.minus(10.days) ?: 10.days, + confidentialIdentityMinimumBackupInterval = networkMapServer.networkParameters + .confidentialIdentityMinimumBackupInterval?.minus(10.days) ?: 10.days) val nextHash = nextParams.serialize().hash val snapshot = alice.rpc.networkParametersFeed().snapshot val updates = alice.rpc.networkParametersFeed().updates.bufferUntilSubscribed() diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index bf8d2910b6..d469788526 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -24,7 +24,7 @@ import org.junit.Rule import org.junit.Test class PersistentNetworkMapCacheTest { - private companion object { + internal companion object { val ALICE = TestIdentity(ALICE_NAME, 70) val BOB = TestIdentity(BOB_NAME, 80) val CHARLIE = TestIdentity(CHARLIE_NAME, 90) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentPartyInfoCacheTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentPartyInfoCacheTest.kt new file mode 100644 index 0000000000..bb8e4ec9fc --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentPartyInfoCacheTest.kt @@ -0,0 +1,96 @@ +package net.corda.node.services.network + +import net.corda.core.crypto.SecureHash +import net.corda.core.node.NodeInfo +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.node.services.identity.InMemoryIdentityService +import net.corda.node.services.network.PersistentNetworkMapCacheTest.Companion.ALICE +import net.corda.node.services.network.PersistentNetworkMapCacheTest.Companion.BOB +import net.corda.node.services.network.PersistentNetworkMapCacheTest.Companion.CHARLIE +import net.corda.nodeapi.internal.DEV_ROOT_CA +import net.corda.nodeapi.internal.persistence.DatabaseConfig +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity +import net.corda.testing.internal.TestingNamedCacheFactory +import net.corda.testing.internal.configureDatabase +import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties +import org.assertj.core.api.Assertions.assertThat +import org.junit.Rule +import org.junit.Test + +class PersistentPartyInfoCacheTest { + + @Rule + @JvmField + val testSerialization = SerializationEnvironmentRule() + + private var portCounter = 1000 + private val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), { null }, { null }) + private val charlieNetMapCache = PersistentNetworkMapCache(TestingNamedCacheFactory(), database, InMemoryIdentityService(trustRoot = DEV_ROOT_CA.certificate)) + + @Test(timeout=300_000) + fun `get party id from CordaX500Name sourced from NetworkMapCache`() { + charlieNetMapCache.addOrUpdateNodes(listOf( + createNodeInfo(listOf(ALICE)), + createNodeInfo(listOf(BOB)), + createNodeInfo(listOf(CHARLIE)))) + val partyInfoCache = PersistentPartyInfoCache(charlieNetMapCache, TestingNamedCacheFactory(), database) + partyInfoCache.start() + assertThat(partyInfoCache.getPartyIdByCordaX500Name(ALICE.name)).isEqualTo(SecureHash.sha256(ALICE.name.toString())) + assertThat(partyInfoCache.getPartyIdByCordaX500Name(BOB.name)).isEqualTo(SecureHash.sha256(BOB.name.toString())) + assertThat(partyInfoCache.getPartyIdByCordaX500Name(CHARLIE.name)).isEqualTo(SecureHash.sha256(CHARLIE.name.toString())) + } + + @Test(timeout=300_000) + fun `get party id from CordaX500Name sourced from backing database`() { + charlieNetMapCache.addOrUpdateNodes(listOf( + createNodeInfo(listOf(ALICE)), + createNodeInfo(listOf(BOB)), + createNodeInfo(listOf(CHARLIE)))) + PersistentPartyInfoCache(charlieNetMapCache, TestingNamedCacheFactory(), database).start() + // clear network map cache & bootstrap another PersistentInfoCache + charlieNetMapCache.clearNetworkMapCache() + val partyInfoCache = PersistentPartyInfoCache(charlieNetMapCache, TestingNamedCacheFactory(), database) + assertThat(partyInfoCache.getPartyIdByCordaX500Name(ALICE.name)).isEqualTo(SecureHash.sha256(ALICE.name.toString())) + assertThat(partyInfoCache.getPartyIdByCordaX500Name(BOB.name)).isEqualTo(SecureHash.sha256(BOB.name.toString())) + assertThat(partyInfoCache.getPartyIdByCordaX500Name(CHARLIE.name)).isEqualTo(SecureHash.sha256(CHARLIE.name.toString())) + } + + @Test(timeout=300_000) + fun `get party CordaX500Name from id sourced from NetworkMapCache`() { + charlieNetMapCache.addOrUpdateNodes(listOf( + createNodeInfo(listOf(ALICE)), + createNodeInfo(listOf(BOB)), + createNodeInfo(listOf(CHARLIE)))) + val partyInfoCache = PersistentPartyInfoCache(charlieNetMapCache, TestingNamedCacheFactory(), database) + partyInfoCache.start() + assertThat(partyInfoCache.getCordaX500NameByPartyId(SecureHash.sha256(ALICE.name.toString()))).isEqualTo(ALICE.name) + assertThat(partyInfoCache.getCordaX500NameByPartyId(SecureHash.sha256(BOB.name.toString()))).isEqualTo(BOB.name) + assertThat(partyInfoCache.getCordaX500NameByPartyId(SecureHash.sha256(CHARLIE.name.toString()))).isEqualTo(CHARLIE.name) + } + + @Test(timeout=300_000) + fun `get party CordaX500Name from id sourced from backing database`() { + charlieNetMapCache.addOrUpdateNodes(listOf( + createNodeInfo(listOf(ALICE)), + createNodeInfo(listOf(BOB)), + createNodeInfo(listOf(CHARLIE)))) + PersistentPartyInfoCache(charlieNetMapCache, TestingNamedCacheFactory(), database).start() + // clear network map cache & bootstrap another PersistentInfoCache + charlieNetMapCache.clearNetworkMapCache() + val partyInfoCache = PersistentPartyInfoCache(charlieNetMapCache, TestingNamedCacheFactory(), database) + assertThat(partyInfoCache.getCordaX500NameByPartyId(SecureHash.sha256(ALICE.name.toString()))).isEqualTo(ALICE.name) + assertThat(partyInfoCache.getCordaX500NameByPartyId(SecureHash.sha256(BOB.name.toString()))).isEqualTo(BOB.name) + assertThat(partyInfoCache.getCordaX500NameByPartyId(SecureHash.sha256(CHARLIE.name.toString()))).isEqualTo(CHARLIE.name) + } + + private fun createNodeInfo(identities: List, + address: NetworkHostAndPort = NetworkHostAndPort("localhost", portCounter++)): NodeInfo { + return NodeInfo( + addresses = listOf(address), + legalIdentitiesAndCerts = identities.map { it.identity }, + platformVersion = 3, + serial = 1 + ) + } +} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowHospitalTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowHospitalTest.kt index 139bb89505..f14f60cc5b 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowHospitalTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowHospitalTest.kt @@ -19,7 +19,10 @@ import net.corda.core.flows.ReceiveFinalityFlow import net.corda.core.flows.SignTransactionFlow import net.corda.core.flows.StartableByRPC import net.corda.core.flows.UnexpectedFlowEndException +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party +import net.corda.core.internal.PLATFORM_VERSION +import net.corda.core.internal.PlatformVersionSwitches import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.StateMachineUpdate import net.corda.core.messaging.startFlow @@ -39,15 +42,24 @@ import net.corda.testing.core.CHARLIE_NAME import net.corda.testing.core.singleIdentity import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.driver +import net.corda.testing.flows.waitForAllFlowsToComplete import net.corda.testing.node.User +import net.corda.testing.node.internal.CustomCordapp +import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP +import net.corda.testing.node.internal.InternalMockNetwork +import net.corda.testing.node.internal.InternalMockNodeParameters +import net.corda.testing.node.internal.MOCK_VERSION_INFO +import net.corda.testing.node.internal.TestCordappInternal +import net.corda.testing.node.internal.TestStartedNode import net.corda.testing.node.internal.enclosedCordapp import net.corda.testing.node.internal.findCordapp +import net.corda.testing.node.testContext import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Before import org.junit.Test import java.sql.SQLException -import java.util.* +import java.util.Random import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException @@ -59,6 +71,11 @@ class FlowHospitalTest { private val rpcUser = User("user1", "test", permissions = setOf(Permissions.all())) + companion object { + private val mockNet = InternalMockNetwork(cordappsForAllNodes = setOf(DUMMY_CONTRACTS_CORDAPP, enclosedCordapp(), + CustomCordapp(targetPlatformVersion = 3, classes = setOf(FinalityFlow::class.java)))) + } + @Before fun before() { SpendStateAndCatchDoubleSpendResponderFlow.exceptionSeenInUserFlow = false @@ -231,6 +248,8 @@ class FlowHospitalTest { it.startFlow(::SpendStateAndCatchDoubleSpendFlow, nodeBHandle.nodeInfo.singleIdentity(), ref).returnValue.getOrThrow(20.seconds) it.startFlow(::SpendStateAndCatchDoubleSpendFlow, nodeBHandle.nodeInfo.singleIdentity(), ref).returnValue.getOrThrow(20.seconds) } + waitForAllFlowsToComplete(nodeAHandle) + waitForAllFlowsToComplete(nodeBHandle) } // 1 is the notary failing to notarise and propagating the error // 2 is the receiving flow failing due to the unexpected session end error @@ -238,6 +257,80 @@ class FlowHospitalTest { assertTrue(SpendStateAndCatchDoubleSpendResponderFlow.exceptionSeenInUserFlow) } + @Test(timeout=300_000) + fun `catching a notary error - two phase finality flow initiator to pre-2PF peer`() { + var dischargedCounter = 0 + StaffedFlowHospital.onFlowErrorPropagated.add { _, _ -> + ++dischargedCounter + } + val aliceNode = createNode(ALICE_NAME, platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY) + val bobNode = createNode(BOB_NAME, platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY - 1) + + val handle = aliceNode.services.startFlow(CreateTransactionFlow(bobNode.info.singleIdentity()), testContext()) + mockNet.runNetwork() + val ref = handle.getOrThrow().resultFuture.get() + aliceNode.services.startFlow(SpendStateAndCatchDoubleSpendFlow(bobNode.info.singleIdentity(), ref), testContext()).getOrThrow() + aliceNode.services.startFlow(SpendStateAndCatchDoubleSpendFlow(bobNode.info.singleIdentity(), ref), testContext()).getOrThrow() + mockNet.runNetwork() + + // 1 is the notary failing to notarise and propagating the error + // 2 is the receiving flow failing due to the unexpected session end error + assertEquals(2, dischargedCounter) + assertTrue(SpendStateAndCatchDoubleSpendResponderFlow.exceptionSeenInUserFlow) + } + + @Test(timeout=300_000) + fun `catching a notary error - pre-2PF initiator to two phase finality flow peer`() { + var dischargedCounter = 0 + StaffedFlowHospital.onFlowErrorPropagated.add { _, _ -> + ++dischargedCounter + } + val aliceNode = createNode(ALICE_NAME, platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY - 1) + val bobNode = createNode(BOB_NAME, platformVersion = PlatformVersionSwitches.TWO_PHASE_FINALITY) + + val handle = aliceNode.services.startFlow(CreateTransactionFlow(bobNode.info.singleIdentity()), testContext()) + mockNet.runNetwork() + val ref = handle.getOrThrow().resultFuture.get() + aliceNode.services.startFlow(SpendStateAndCatchDoubleSpendFlow(bobNode.info.singleIdentity(), ref), testContext()).getOrThrow() + aliceNode.services.startFlow(SpendStateAndCatchDoubleSpendFlow(bobNode.info.singleIdentity(), ref), testContext()).getOrThrow() + mockNet.runNetwork() + + // 1 is the notary failing to notarise and propagating the error + // 2 is the receiving flow failing due to the unexpected session end error + assertEquals(2, dischargedCounter) + assertTrue(SpendStateAndCatchDoubleSpendResponderFlow.exceptionSeenInUserFlow) + } + + private fun createNode(legalName: CordaX500Name, cordapps: List = emptyList(), platformVersion: Int = PLATFORM_VERSION): TestStartedNode { + return mockNet.createNode(InternalMockNodeParameters(legalName = legalName, additionalCordapps = cordapps, + version = MOCK_VERSION_INFO.copy(platformVersion = platformVersion))) + } + + @Test(timeout = 300_000) + fun `old finality flow catching a notary error will cause a peer to fail with unexpected session end during ReceiveFinalityFlow that passes through user code`() { + var dischargedCounter = 0 + StaffedFlowHospital.onFlowErrorPropagated.add { _, _ -> + ++dischargedCounter + } + val user = User("mark", "dadada", setOf(Permissions.all())) + driver(DriverParameters(isDebug = false, startNodesInProcess = true)) { + + val nodeAHandle = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() + val nodeBHandle = startNode(providedName = BOB_NAME, rpcUsers = listOf(user)).getOrThrow() + nodeAHandle.rpc.let { + val ref = it.startFlow(::CreateTransactionFlow, nodeBHandle.nodeInfo.singleIdentity()).returnValue.getOrThrow(20.seconds) + it.startFlow(::SpendStateAndCatchDoubleSpendOldFinalityFlow, nodeBHandle.nodeInfo.singleIdentity(), ref).returnValue.getOrThrow(20.seconds) + it.startFlow(::SpendStateAndCatchDoubleSpendOldFinalityFlow, nodeBHandle.nodeInfo.singleIdentity(), ref).returnValue.getOrThrow(20.seconds) + } + waitForAllFlowsToComplete(nodeAHandle) + waitForAllFlowsToComplete(nodeBHandle) + } + // 1 is the notary failing to notarise and propagating the error + // 2 is the receiving flow failing due to the unexpected session end error + assertEquals(2, dischargedCounter) + assertTrue(SpendStateAndCatchDoubleSpendResponderOldFinalityFlow.exceptionSeenInUserFlow) + } + @Test(timeout = 300_000) fun `unexpected session end errors outside of ReceiveFinalityFlow are not handled`() { var dischargedCounter = 0 @@ -260,6 +353,8 @@ class FlowHospitalTest { val ref3 = it.startFlow(::SpendStateAndCatchDoubleSpendFlow, nodeCHandle.nodeInfo.singleIdentity(), ref2).returnValue.getOrThrow(20.seconds) it.startFlow(::CreateTransactionButDontFinalizeFlow, nodeBHandle.nodeInfo.singleIdentity(), ref3).returnValue.getOrThrow(20.seconds) } + waitForAllFlowsToComplete(nodeAHandle) + waitForAllFlowsToComplete(nodeBHandle) } assertEquals(0, dischargedCounter) assertEquals(1, observationCounter) @@ -286,6 +381,8 @@ class FlowHospitalTest { it.startFlow(::SpendStateAndCatchDoubleSpendFlow, nodeBHandle.nodeInfo.singleIdentity(), ref).returnValue.getOrThrow(20.seconds) it.startFlow(::SpendStateAndCatchDoubleSpendFlow, nodeBHandle.nodeInfo.singleIdentity(), ref, true).returnValue.getOrThrow(20.seconds) } + waitForAllFlowsToComplete(nodeAHandle) + waitForAllFlowsToComplete(nodeBHandle) } // 1 is the notary failing to notarise and propagating the error assertEquals(1, dischargedCounter) @@ -464,6 +561,7 @@ class FlowHospitalTest { var exceptionSeenInUserFlow = false } + @Suppress("TooGenericExceptionCaught") @Suspendable override fun call() { val consumeError = session.receive().unwrap { it } @@ -474,6 +572,67 @@ class FlowHospitalTest { }) try { subFlow(ReceiveFinalityFlow(session, stx.id)) + } catch (ex: Exception) { + when (ex) { + is NotaryException, + is UnexpectedFlowEndException -> { + exceptionSeenInUserFlow = true + if (!consumeError) { + throw ex + } + } + } + } + } + } + + @InitiatingFlow + @StartableByRPC + class SpendStateAndCatchDoubleSpendOldFinalityFlow( + private val peer: Party, + private val ref: StateAndRef, + private val consumePeerError: Boolean + ) : FlowLogic>() { + + constructor(peer: Party, ref: StateAndRef): this(peer, ref, false) + + @Suspendable + override fun call(): StateAndRef { + val tx = TransactionBuilder(serviceHub.networkMapCache.notaryIdentities.first()).apply { + addInputState(ref) + addOutputState(DummyState(participants = listOf(ourIdentity))) + addCommand(DummyContract.Commands.Move(), listOf(ourIdentity.owningKey, peer.owningKey)) + } + val stx = serviceHub.signInitialTransaction(tx) + val session = initiateFlow(peer) + session.send(consumePeerError) + val ftx = subFlow(CollectSignaturesFlow(stx, listOf(session))) + try { + subFlow(OldFinalityFlow(ftx, session)) + } catch(e: NotaryException) { + logger.info("Caught notary exception") + } + return ftx.coreTransaction.outRef(0) + } + } + + @InitiatedBy(SpendStateAndCatchDoubleSpendOldFinalityFlow::class) + class SpendStateAndCatchDoubleSpendResponderOldFinalityFlow(private val session: FlowSession) : FlowLogic() { + + companion object { + var exceptionSeenInUserFlow = false + } + + @Suspendable + override fun call() { + val consumeError = session.receive().unwrap { it } + val stx = subFlow(object : SignTransactionFlow(session) { + override fun checkTransaction(stx: SignedTransaction) { + + } + }) + try { + subFlow(OldReceiveFinalityFlow(session, stx.id)) } catch (e: UnexpectedFlowEndException) { exceptionSeenInUserFlow = true if (!consumeError) { diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/OldFinalityFlow.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/OldFinalityFlow.kt new file mode 100644 index 0000000000..e352ac02e9 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/OldFinalityFlow.kt @@ -0,0 +1,306 @@ +package net.corda.node.services.statemachine + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.CordaInternal +import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.isFulfilledBy +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.NotaryException +import net.corda.core.flows.NotaryFlow +import net.corda.core.flows.ReceiveTransactionFlow +import net.corda.core.flows.SendTransactionFlow +import net.corda.core.flows.UnexpectedFlowEndException +import net.corda.core.identity.Party +import net.corda.core.identity.groupAbstractPartyByWellKnownParty +import net.corda.core.internal.telemetry.telemetryServiceInternal +import net.corda.core.internal.warnOnce +import net.corda.core.node.StatesToRecord +import net.corda.core.node.StatesToRecord.ONLY_RELEVANT +import net.corda.core.transactions.LedgerTransaction +import net.corda.core.transactions.SignedTransaction +import net.corda.core.utilities.ProgressTracker +import net.corda.core.utilities.debug + +/** + * Verifies the given transaction, then sends it to the named notary. If the notary agrees that the transaction + * is acceptable then it is from that point onwards committed to the ledger, and will be written through to the + * vault. Additionally it will be distributed to the parties reflected in the participants list of the states. + * + * By default, the initiating flow will commit states that are relevant to the initiating party as indicated by + * [StatesToRecord.ONLY_RELEVANT]. Relevance is determined by the union of all participants to states which have been + * included in the transaction. This default behaviour may be modified by passing in an alternate value for [StatesToRecord]. + * + * The transaction is expected to have already been resolved: if its dependencies are not available in local + * storage, verification will fail. It must have signatures from all necessary parties other than the notary. + * + * A list of [FlowSession]s is required for each non-local participant of the transaction. These participants will receive + * the final notarised transaction by calling [ReceiveFinalityFlow] in their counterpart flows. Sessions with non-participants + * can also be included, but they must specify [StatesToRecord.ALL_VISIBLE] for statesToRecord if they wish to record the + * contract states into their vaults. + * + * The flow returns the same transaction but with the additional signatures from the notary. + * + * NOTE: This is an inlined flow but for backwards compatibility is annotated with [InitiatingFlow]. + */ +// To maintain backwards compatibility with the old API, FinalityFlow can act both as an initiating flow and as an inlined flow. +// This is only possible because a flow is only truly initiating when the first call to initiateFlow is made (where the +// presence of @InitiatingFlow is checked). So the new API is inlined simply because that code path doesn't call initiateFlow. +@InitiatingFlow +class OldFinalityFlow private constructor(val transaction: SignedTransaction, + private val oldParticipants: Collection, + override val progressTracker: ProgressTracker, + private val sessions: Collection, + private val newApi: Boolean, + private val statesToRecord: StatesToRecord = ONLY_RELEVANT) : FlowLogic() { + + @CordaInternal + data class ExtraConstructorArgs(val oldParticipants: Collection, val sessions: Collection, val newApi: Boolean, val statesToRecord: StatesToRecord) + + @CordaInternal + fun getExtraConstructorArgs() = ExtraConstructorArgs(oldParticipants, sessions, newApi, statesToRecord) + + @Deprecated(DEPRECATION_MSG) + constructor(transaction: SignedTransaction, extraRecipients: Set, progressTracker: ProgressTracker) : this( + transaction, extraRecipients, progressTracker, emptyList(), false + ) + @Deprecated(DEPRECATION_MSG) + constructor(transaction: SignedTransaction, extraRecipients: Set) : this(transaction, extraRecipients, tracker(), emptyList(), false) + @Deprecated(DEPRECATION_MSG) + constructor(transaction: SignedTransaction) : this(transaction, emptySet(), tracker(), emptyList(), false) + @Deprecated(DEPRECATION_MSG) + constructor(transaction: SignedTransaction, progressTracker: ProgressTracker) : this(transaction, emptySet(), progressTracker, emptyList(), false) + + /** + * Notarise the given transaction and broadcast it to the given [FlowSession]s. This list **must** at least include + * all the non-local participants of the transaction. Sessions to non-participants can also be provided. + * + * @param transaction What to commit. + */ + constructor(transaction: SignedTransaction, firstSession: FlowSession, vararg restSessions: FlowSession) : this( + transaction, listOf(firstSession) + restSessions.asList() + ) + + /** + * Notarise the given transaction and broadcast it to all the participants. + * + * @param transaction What to commit. + * @param sessions A collection of [FlowSession]s for each non-local participant of the transaction. Sessions to non-participants can + * also be provided. + */ + @JvmOverloads + constructor( + transaction: SignedTransaction, + sessions: Collection, + progressTracker: ProgressTracker = tracker() + ) : this(transaction, emptyList(), progressTracker, sessions, true) + + /** + * Notarise the given transaction and broadcast it to all the participants. + * + * @param transaction What to commit. + * @param sessions A collection of [FlowSession]s for each non-local participant of the transaction. Sessions to non-participants can + * also be provided. + * @param statesToRecord Which states to commit to the vault. + */ + @JvmOverloads + constructor( + transaction: SignedTransaction, + sessions: Collection, + statesToRecord: StatesToRecord, + progressTracker: ProgressTracker = tracker() + ) : this(transaction, emptyList(), progressTracker, sessions, true, statesToRecord) + + /** + * Notarise the given transaction and broadcast it to all the participants. + * + * @param transaction What to commit. + * @param sessions A collection of [FlowSession]s for each non-local participant. + * @param oldParticipants An **optional** collection of parties for participants who are still using the old API. + * + * You will only need to use this parameter if you have upgraded your CorDapp from the V3 FinalityFlow API but are required to provide + * backwards compatibility with participants running V3 nodes. If you're writing a new CorDapp then this does not apply and this + * parameter should be ignored. + */ + @Deprecated(DEPRECATION_MSG) + constructor( + transaction: SignedTransaction, + sessions: Collection, + oldParticipants: Collection, + progressTracker: ProgressTracker + ) : this(transaction, oldParticipants, progressTracker, sessions, true) + + companion object { + private const val DEPRECATION_MSG = "It is unsafe to use this constructor as it requires nodes to automatically " + + "accept notarised transactions without first checking their relevancy. Instead, use one of the constructors " + + "that requires only FlowSessions." + + object NOTARISING : ProgressTracker.Step("Requesting signature by notary service") { + override fun childProgressTracker() = NotaryFlow.Client.tracker() + } + + object BROADCASTING : ProgressTracker.Step("Broadcasting transaction to participants") + + @JvmStatic + fun tracker() = ProgressTracker(NOTARISING, BROADCASTING) + } + + @Suppress("ComplexMethod") + @Suspendable + @Throws(NotaryException::class) + override fun call(): SignedTransaction { + if (!newApi) { + logger.warnOnce("The current usage of FinalityFlow is unsafe. Please consider upgrading your CorDapp to use " + + "FinalityFlow with FlowSessions. (${serviceHub.getAppContext().cordapp.info})") + } else { + require(sessions.none { serviceHub.myInfo.isLegalIdentity(it.counterparty) }) { + "Do not provide flow sessions for the local node. FinalityFlow will record the notarised transaction locally." + } + } + + // Note: this method is carefully broken up to minimize the amount of data reachable from the stack at + // the point where subFlow is invoked, as that minimizes the checkpointing work to be done. + // + // Lookup the resolved transactions and use them to map each signed transaction to the list of participants. + // Then send to the notary if needed, record locally and distribute. + + logCommandData() + val ledgerTransaction = verifyTx() + val externalTxParticipants = extractExternalParticipants(ledgerTransaction) + + if (newApi) { + val sessionParties = sessions.map { it.counterparty } + val missingRecipients = externalTxParticipants - sessionParties - oldParticipants + require(missingRecipients.isEmpty()) { + "Flow sessions were not provided for the following transaction participants: $missingRecipients" + } + sessionParties.intersect(oldParticipants).let { + require(it.isEmpty()) { "The following parties are specified both in flow sessions and in the oldParticipants list: $it" } + } + } + + val notarised = notariseAndRecord() + + progressTracker.currentStep = BROADCASTING + + if (newApi) { + oldV3Broadcast(notarised, oldParticipants.toSet()) + for (session in sessions) { + try { + subFlow(SendTransactionFlow(session, notarised)) + logger.info("Party ${session.counterparty} received the transaction.") + } catch (e: UnexpectedFlowEndException) { + throw UnexpectedFlowEndException( + "${session.counterparty} has finished prematurely and we're trying to send them the finalised transaction. " + + "Did they forget to call ReceiveFinalityFlow? (${e.message})", + e.cause, + e.originalErrorId + ) + } + } + } else { + oldV3Broadcast(notarised, (externalTxParticipants + oldParticipants).toSet()) + } + + logger.info("All parties received the transaction successfully.") + + return notarised + } + + @Suspendable + private fun oldV3Broadcast(notarised: SignedTransaction, recipients: Set) { + for (recipient in recipients) { + if (!serviceHub.myInfo.isLegalIdentity(recipient)) { + logger.debug { "Sending transaction to party $recipient." } + val session = initiateFlow(recipient) + subFlow(SendTransactionFlow(session, notarised)) + logger.info("Party $recipient received the transaction.") + } + } + } + + private fun logCommandData() { + if (logger.isDebugEnabled) { + val commandDataTypes = transaction.tx.commands.asSequence().mapNotNull { it.value::class.qualifiedName }.distinct() + logger.debug("Started finalization, commands are ${commandDataTypes.joinToString(", ", "[", "]")}.") + } + } + + @Suspendable + private fun notariseAndRecord(): SignedTransaction { + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#notariseAndRecord", flowLogic = this) { + val notarised = if (needsNotarySignature(transaction)) { + progressTracker.currentStep = NOTARISING + val notarySignatures = subFlow(NotaryFlow.Client(transaction, skipVerification = true)) + transaction + notarySignatures + } else { + logger.info("No need to notarise this transaction.") + transaction + } + serviceHub.telemetryServiceInternal.span("${this::class.java.name}#notariseAndRecord:recordTransactions", flowLogic = this) { + logger.info("Recording transaction locally.") + serviceHub.recordTransactions(statesToRecord, listOf(notarised)) + logger.info("Recorded transaction locally successfully.") + } + return notarised + } + } + + private fun needsNotarySignature(stx: SignedTransaction): Boolean { + val wtx = stx.tx + val needsNotarisation = wtx.inputs.isNotEmpty() || wtx.references.isNotEmpty() || wtx.timeWindow != null + return needsNotarisation && hasNoNotarySignature(stx) + } + + private fun hasNoNotarySignature(stx: SignedTransaction): Boolean { + val notaryKey = stx.tx.notary?.owningKey + val signers = stx.sigs.asSequence().map { it.by }.toSet() + return notaryKey?.isFulfilledBy(signers) != true + } + + private fun extractExternalParticipants(ltx: LedgerTransaction): Set { + val participants = ltx.outputStates.flatMap { it.participants } + ltx.inputStates.flatMap { it.participants } + return groupAbstractPartyByWellKnownParty(serviceHub, participants).keys - serviceHub.myInfo.legalIdentities + } + + private fun verifyTx(): LedgerTransaction { + val notary = transaction.tx.notary + // The notary signature(s) are allowed to be missing but no others. + if (notary != null) transaction.verifySignaturesExcept(notary.owningKey) else transaction.verifyRequiredSignatures() + // TODO= [CORDA-3267] Remove duplicate signature verification + val ltx = transaction.toLedgerTransaction(serviceHub, false) + ltx.verify() + return ltx + } +} + +/** + * The receiving counterpart to [FinalityFlow]. + * + * All parties who are receiving a finalised transaction from a sender flow must subcall this flow in their own flows. + * + * It's typical to have already signed the transaction proposal in the same workflow using [SignTransactionFlow]. If so + * then the transaction ID can be passed in as an extra check to ensure the finalised transaction is the one that was signed + * before it's committed to the vault. + * + * @param otherSideSession The session which is providing the transaction to record. + * @param expectedTxId Expected ID of the transaction that's about to be received. This is typically retrieved from + * [SignTransactionFlow]. Setting it to null disables the expected transaction ID check. + * @param statesToRecord Which states to commit to the vault. Defaults to [StatesToRecord.ONLY_RELEVANT]. + */ +class OldReceiveFinalityFlow @JvmOverloads constructor(private val otherSideSession: FlowSession, + private val expectedTxId: SecureHash? = null, + private val statesToRecord: StatesToRecord = ONLY_RELEVANT) : FlowLogic() { + @Suspendable + override fun call(): SignedTransaction { + return subFlow(object : ReceiveTransactionFlow(otherSideSession, checkSufficientSignatures = true, statesToRecord = statesToRecord) { + override fun checkBeforeRecording(stx: SignedTransaction) { + require(expectedTxId == null || expectedTxId == stx.id) { + "We expected to receive transaction with ID $expectedTxId but instead got ${stx.id}. Transaction was" + + "not recorded and nor its states sent to the vault." + } + } + }) + } +} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt index 198b9d3ab6..10ff0364d7 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt @@ -39,7 +39,6 @@ import org.assertj.core.api.Assertions import org.junit.After import org.junit.Assert import org.junit.Test -import java.lang.IllegalStateException import java.sql.SQLException import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.TimeUnit @@ -422,9 +421,10 @@ class VaultObserverExceptionTest { /** * An error is thrown inside of the [VaultService.rawUpdates] observable while recording a transaction inside of the initiating node. * - * This causes the transaction to not be saved on the local node but the notary still records the transaction as spent. The transaction - * also is not send to the counterparty node since it failed before reaching the send. Therefore no subscriber events occur on the - * counterparty node. + * Two Phase Finality update: + * This causes the transaction to not be finalised on the local node but the notary still records the transaction as spent. The transaction + * does finalize at the counterparty node since the notary signatures are broadcast to peers prior to initiator node finalisation. + * Subscriber events will occur on the counterparty node. * * More importantly, the observer listening to the [VaultService.rawUpdates] observable should not unsubscribe. * @@ -487,29 +487,32 @@ class VaultObserverExceptionTest { println("First set of flows") val stateId = startErrorInObservableWhenConsumingState() assertEquals(0, aliceNode.getStatesById(stateId, Vault.StateStatus.CONSUMED).size) - assertEquals(0, bobNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) + // Ledger is temporarily inconsistent as peer finalised transaction but initiator error'ed before finalisation. + assertEquals(1, bobNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) assertEquals(1, notary.getNotarisedTransactionIds().size) assertEquals(1, observationCounter) assertEquals(2, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) - assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + assertEquals(1, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) println("Second set of flows") val stateId2 = startErrorInObservableWhenConsumingState() assertEquals(0, aliceNode.getStatesById(stateId2, Vault.StateStatus.CONSUMED).size) - assertEquals(0, bobNode.getStatesById(stateId2, Vault.StateStatus.UNCONSUMED).size) + // Ledger is temporarily inconsistent as peer finalised transaction but initiator error'ed before finalisation. + assertEquals(1, bobNode.getStatesById(stateId2, Vault.StateStatus.UNCONSUMED).size) assertEquals(2, notary.getNotarisedTransactionIds().size) assertEquals(2, observationCounter) assertEquals(4, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) - assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + assertEquals(2, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) } } /** * An error is thrown inside of the [VaultService.rawUpdates] observable while recording a transaction inside of the initiating node. * - * This causes the transaction to not be saved on the local node but the notary still records the transaction as spent. The transaction - * also is not send to the counterparty node since it failed before reaching the send. Therefore no subscriber events occur on the - * counterparty node. + * Two Phase Finality update: + * This causes the transaction to not be finalised on the local node but the notary still records the transaction as spent. The transaction + * does finalize at the counterparty node since the notary signatures are broadcast to peers prior to initiator node finalisation. + * Subscriber events will occur on the counterparty node. * * More importantly, the observer listening to the [VaultService.rawUpdates] observable should not unsubscribe. * @@ -578,19 +581,21 @@ class VaultObserverExceptionTest { val stateId = startErrorInObservableWhenConsumingState() assertEquals(0, aliceNode.getStatesById(stateId, Vault.StateStatus.CONSUMED).size) - assertEquals(0, bobNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) + // Ledger is temporarily inconsistent as peer finalised transaction but initiator error'ed before finalisation. + assertEquals(1, bobNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) assertEquals(1, notary.getNotarisedTransactionIds().size) assertEquals(1, observationCounter) assertEquals(3, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) - assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + assertEquals(1, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) val stateId2 = startErrorInObservableWhenConsumingState() assertEquals(0, aliceNode.getStatesById(stateId2, Vault.StateStatus.CONSUMED).size) - assertEquals(0, bobNode.getStatesById(stateId2, Vault.StateStatus.UNCONSUMED).size) + // Ledger is temporarily inconsistent as peer finalised transaction but initiator error'ed before finalisation. + assertEquals(1, bobNode.getStatesById(stateId2, Vault.StateStatus.UNCONSUMED).size) assertEquals(2, notary.getNotarisedTransactionIds().size) assertEquals(2, observationCounter) assertEquals(6, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) - assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + assertEquals(2, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) } } @@ -681,9 +686,10 @@ class VaultObserverExceptionTest { /** * An error is thrown inside of the [VaultService.updates] observable while recording a transaction inside of the initiating node. * - * This causes the transaction to not be saved on the local node but the notary still records the transaction as spent. The transaction - * also is not send to the counterparty node since it failed before reaching the send. Therefore no subscriber events occur on the - * counterparty node. + * Two Phase Finality update: + * This causes the transaction to not be finalised on the local node but the notary still records the transaction as spent. The transaction + * does finalize at the counterparty node since the notary signatures are broadcast to peers prior to initiator node finalisation. + * Subscriber events will occur on the counterparty node. * * More importantly, the observer listening to the [VaultService.updates] observable should not unsubscribe. * @@ -743,19 +749,21 @@ class VaultObserverExceptionTest { val stateId = startErrorInObservableWhenConsumingState() assertEquals(0, aliceNode.getStatesById(stateId, Vault.StateStatus.CONSUMED).size) assertEquals(1, aliceNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) - assertEquals(0, bobNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) + // Ledger is temporarily inconsistent as peer finalised transaction but initiator error'ed before finalisation. + assertEquals(1, bobNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) assertEquals(1, notary.getNotarisedTransactionIds().size) assertEquals(1, observationCounter) assertEquals(2, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) - assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + assertEquals(1, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) val stateId2 = startErrorInObservableWhenConsumingState() assertEquals(0, aliceNode.getStatesById(stateId2, Vault.StateStatus.CONSUMED).size) assertEquals(2, aliceNode.getAllStates(Vault.StateStatus.UNCONSUMED).size) - assertEquals(0, bobNode.getStatesById(stateId2, Vault.StateStatus.UNCONSUMED).size) + // Ledger is temporarily inconsistent as peer finalised transaction but initiator error'ed before finalisation. + assertEquals(1, bobNode.getStatesById(stateId2, Vault.StateStatus.UNCONSUMED).size) assertEquals(2, notary.getNotarisedTransactionIds().size) assertEquals(4, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) - assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + assertEquals(2, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) } } diff --git a/node/src/main/java/sandbox/java/lang/CharSequence.java b/node/src/main/java/sandbox/java/lang/CharSequence.java deleted file mode 100644 index 9b62762a11..0000000000 --- a/node/src/main/java/sandbox/java/lang/CharSequence.java +++ /dev/null @@ -1,8 +0,0 @@ -package sandbox.java.lang; - -/** - * This is a dummy class that implements just enough of {@link java.lang.CharSequence} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -public interface CharSequence extends java.lang.CharSequence { -} diff --git a/node/src/main/java/sandbox/java/lang/Comparable.java b/node/src/main/java/sandbox/java/lang/Comparable.java deleted file mode 100644 index 5ca4f4871c..0000000000 --- a/node/src/main/java/sandbox/java/lang/Comparable.java +++ /dev/null @@ -1,8 +0,0 @@ -package sandbox.java.lang; - -/** - * This is a dummy class that implements just enough of {@link java.lang.Comparable} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -public interface Comparable extends java.lang.Comparable { -} diff --git a/node/src/main/java/sandbox/java/lang/Number.java b/node/src/main/java/sandbox/java/lang/Number.java deleted file mode 100644 index a98d60dbd6..0000000000 --- a/node/src/main/java/sandbox/java/lang/Number.java +++ /dev/null @@ -1,8 +0,0 @@ -package sandbox.java.lang; - -/** - * This is a dummy class that implements just enough of {@link java.lang.Number} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -public class Number extends Object { -} diff --git a/node/src/main/java/sandbox/java/math/BigInteger.java b/node/src/main/java/sandbox/java/math/BigInteger.java deleted file mode 100644 index d58328fd3c..0000000000 --- a/node/src/main/java/sandbox/java/math/BigInteger.java +++ /dev/null @@ -1,8 +0,0 @@ -package sandbox.java.math; - -/** - * This is a dummy class that implements just enough of {@link java.math.BigInteger} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -public class BigInteger extends sandbox.java.lang.Number { -} diff --git a/node/src/main/java/sandbox/java/security/Key.java b/node/src/main/java/sandbox/java/security/Key.java deleted file mode 100644 index 9c5c952852..0000000000 --- a/node/src/main/java/sandbox/java/security/Key.java +++ /dev/null @@ -1,13 +0,0 @@ -package sandbox.java.security; - -import sandbox.java.lang.String; - -/** - * This is a dummy class that implements just enough of {@link java.security.Key} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -public interface Key { - String getAlgorithm(); - String getFormat(); - byte[] getEncoded(); -} diff --git a/node/src/main/java/sandbox/java/security/KeyPair.java b/node/src/main/java/sandbox/java/security/KeyPair.java deleted file mode 100644 index 653e27de8e..0000000000 --- a/node/src/main/java/sandbox/java/security/KeyPair.java +++ /dev/null @@ -1,8 +0,0 @@ -package sandbox.java.security; - -/** - * This is a dummy class that implements just enough of {@link java.security.KeyPair} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -public final class KeyPair extends sandbox.java.lang.Object implements java.io.Serializable { -} \ No newline at end of file diff --git a/node/src/main/java/sandbox/java/security/PrivateKey.java b/node/src/main/java/sandbox/java/security/PrivateKey.java deleted file mode 100644 index a314aa6234..0000000000 --- a/node/src/main/java/sandbox/java/security/PrivateKey.java +++ /dev/null @@ -1,8 +0,0 @@ -package sandbox.java.security; - -/** - * This is a dummy class that implements just enough of {@link java.security.PrivateKey} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -public interface PrivateKey extends Key { -} diff --git a/node/src/main/java/sandbox/java/security/PublicKey.java b/node/src/main/java/sandbox/java/security/PublicKey.java deleted file mode 100644 index 451f33141a..0000000000 --- a/node/src/main/java/sandbox/java/security/PublicKey.java +++ /dev/null @@ -1,8 +0,0 @@ -package sandbox.java.security; - -/** - * This is a dummy class that implements just enough of {@link java.security.PublicKey} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -public interface PublicKey extends Key { -} diff --git a/node/src/main/java/sandbox/java/security/spec/AlgorithmParameterSpec.java b/node/src/main/java/sandbox/java/security/spec/AlgorithmParameterSpec.java deleted file mode 100644 index 7943cf4612..0000000000 --- a/node/src/main/java/sandbox/java/security/spec/AlgorithmParameterSpec.java +++ /dev/null @@ -1,8 +0,0 @@ -package sandbox.java.security.spec; - -/** - * This is a dummy class that implements just enough of {@link java.security.spec.AlgorithmParameterSpec} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -public interface AlgorithmParameterSpec { -} diff --git a/node/src/main/java/sandbox/java/util/ArrayList.java b/node/src/main/java/sandbox/java/util/ArrayList.java deleted file mode 100644 index 7eb5df9f09..0000000000 --- a/node/src/main/java/sandbox/java/util/ArrayList.java +++ /dev/null @@ -1,16 +0,0 @@ -package sandbox.java.util; - -/** - * This is a dummy class that implements just enough of {@link java.util.ArrayList} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -@SuppressWarnings("unused") -public class ArrayList extends sandbox.java.lang.Object implements List { - public ArrayList(int size) { - } - - @Override - public boolean add(T item) { - throw new UnsupportedOperationException("Dummy class - not implemented"); - } -} diff --git a/node/src/main/java/sandbox/java/util/List.java b/node/src/main/java/sandbox/java/util/List.java deleted file mode 100644 index 0f7dbfe22c..0000000000 --- a/node/src/main/java/sandbox/java/util/List.java +++ /dev/null @@ -1,9 +0,0 @@ -package sandbox.java.util; - -/** - * This is a dummy class that implements just enough of {@link java.util.List} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -public interface List { - boolean add(T item); -} diff --git a/node/src/main/java/sandbox/net/corda/core/crypto/DJVM.java b/node/src/main/java/sandbox/net/corda/core/crypto/DJVM.java deleted file mode 100644 index 8004d44666..0000000000 --- a/node/src/main/java/sandbox/net/corda/core/crypto/DJVM.java +++ /dev/null @@ -1,62 +0,0 @@ -package sandbox.net.corda.core.crypto; - -import org.jetbrains.annotations.NotNull; -import sandbox.java.lang.Integer; -import sandbox.java.lang.String; -import sandbox.java.util.ArrayList; -import sandbox.java.util.List; -import sandbox.org.bouncycastle.asn1.x509.AlgorithmIdentifier; - -import java.io.IOException; - -/** - * Helper class for {@link sandbox.net.corda.core.crypto.Crypto}. - * Deliberately package-private. - */ -final class DJVM { - private DJVM() {} - - @NotNull - static SignatureScheme toDJVM(@NotNull net.corda.core.crypto.SignatureScheme scheme) { - // The AlgorithmParameterSpec is deliberately left as null - // because it is computationally expensive to generate these - // objects inside the sandbox every time. - return new SignatureScheme( - scheme.getSchemeNumberID(), - String.toDJVM(scheme.getSchemeCodeName()), - toDJVM(scheme.getSignatureOID()), - toDJVM(scheme.getAlternativeOIDs()), - String.toDJVM(scheme.getProviderName()), - String.toDJVM(scheme.getAlgorithmName()), - String.toDJVM(scheme.getSignatureName()), - null, - Integer.toDJVM(scheme.getKeySize()), - String.toDJVM(scheme.getDesc()) - ); - } - - static org.bouncycastle.asn1.x509.AlgorithmIdentifier fromDJVM(@NotNull AlgorithmIdentifier oid) { - try { - return org.bouncycastle.asn1.x509.AlgorithmIdentifier.getInstance(oid.getEncoded()); - } catch (IOException e) { - throw sandbox.java.lang.DJVM.toRuleViolationError(e); - } - } - - static AlgorithmIdentifier toDJVM(@NotNull org.bouncycastle.asn1.x509.AlgorithmIdentifier oid) { - try { - return AlgorithmIdentifier.getInstance(oid.getEncoded()); - } catch (IOException e) { - throw sandbox.java.lang.DJVM.toRuleViolationError(e); - } - } - - @NotNull - static List toDJVM(@NotNull java.util.List list) { - ArrayList djvmList = new ArrayList<>(list.size()); - for (org.bouncycastle.asn1.x509.AlgorithmIdentifier oid : list) { - djvmList.add(toDJVM(oid)); - } - return djvmList; - } -} diff --git a/node/src/main/java/sandbox/net/corda/core/crypto/DJVMPublicKey.java b/node/src/main/java/sandbox/net/corda/core/crypto/DJVMPublicKey.java deleted file mode 100644 index 199acc39ce..0000000000 --- a/node/src/main/java/sandbox/net/corda/core/crypto/DJVMPublicKey.java +++ /dev/null @@ -1,61 +0,0 @@ -package sandbox.net.corda.core.crypto; - -import org.jetbrains.annotations.NotNull; -import sandbox.java.lang.Object; -import sandbox.java.lang.String; -import sandbox.java.security.PublicKey; - -/** - * We shall delegate as much cryptography as possible to Corda's - * underlying {@link net.corda.core.crypto.Crypto} object and its - * {@link java.security.Provider} classes. This wrapper only needs - * to implement {@link #equals} and {@link #hashCode}. - */ -final class DJVMPublicKey extends Object implements PublicKey { - private final java.security.PublicKey underlying; - private final String algorithm; - private final String format; - private final int hashCode; - - DJVMPublicKey(@NotNull java.security.PublicKey underlying) { - this.underlying = underlying; - this.algorithm = String.toDJVM(underlying.getAlgorithm()); - this.format = String.toDJVM(underlying.getFormat()); - this.hashCode = underlying.hashCode(); - } - - java.security.PublicKey getUnderlying() { - return underlying; - } - - @Override - public boolean equals(java.lang.Object other) { - if (this == other) { - return true; - } else if (!(other instanceof DJVMPublicKey)) { - return false; - } else { - return underlying.equals(((DJVMPublicKey) other).underlying); - } - } - - @Override - public int hashCode() { - return hashCode; - } - - @Override - public String getAlgorithm() { - return algorithm; - } - - @Override - public String getFormat() { - return format; - } - - @Override - public byte[] getEncoded() { - return underlying.getEncoded(); - } -} diff --git a/node/src/main/java/sandbox/org/bouncycastle/asn1/ASN1Encodable.java b/node/src/main/java/sandbox/org/bouncycastle/asn1/ASN1Encodable.java deleted file mode 100644 index e75ac2e6b4..0000000000 --- a/node/src/main/java/sandbox/org/bouncycastle/asn1/ASN1Encodable.java +++ /dev/null @@ -1,9 +0,0 @@ -package sandbox.org.bouncycastle.asn1; - -/** - * This is a dummy class that implements just enough of {@link org.bouncycastle.asn1.ASN1Encodable} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -@SuppressWarnings("WeakerAccess") -public interface ASN1Encodable { -} diff --git a/node/src/main/java/sandbox/org/bouncycastle/asn1/ASN1Object.java b/node/src/main/java/sandbox/org/bouncycastle/asn1/ASN1Object.java deleted file mode 100644 index d71661cc71..0000000000 --- a/node/src/main/java/sandbox/org/bouncycastle/asn1/ASN1Object.java +++ /dev/null @@ -1,16 +0,0 @@ -package sandbox.org.bouncycastle.asn1; - -import sandbox.java.lang.Object; - -import java.io.IOException; - -/** - * This is a dummy class that implements just enough of {@link org.bouncycastle.asn1.ASN1Object} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -@SuppressWarnings("RedundantThrows") -public class ASN1Object extends Object implements ASN1Encodable { - public byte[] getEncoded() throws IOException { - throw new UnsupportedOperationException("Dummy class - not implemented"); - } -} diff --git a/node/src/main/java/sandbox/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java b/node/src/main/java/sandbox/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java deleted file mode 100644 index 144b5f9260..0000000000 --- a/node/src/main/java/sandbox/org/bouncycastle/asn1/x509/AlgorithmIdentifier.java +++ /dev/null @@ -1,14 +0,0 @@ -package sandbox.org.bouncycastle.asn1.x509; - -import sandbox.org.bouncycastle.asn1.ASN1Object; - -/** - * This is a dummy class that implements just enough of {@link org.bouncycastle.asn1.x509.AlgorithmIdentifier} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -@SuppressWarnings("unused") -public class AlgorithmIdentifier extends ASN1Object { - public static AlgorithmIdentifier getInstance(Object obj) { - throw new UnsupportedOperationException("Dummy class - not implemented"); - } -} diff --git a/node/src/main/java/sandbox/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java b/node/src/main/java/sandbox/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java deleted file mode 100644 index a84fb60772..0000000000 --- a/node/src/main/java/sandbox/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo.java +++ /dev/null @@ -1,10 +0,0 @@ -package sandbox.org.bouncycastle.asn1.x509; - -import sandbox.org.bouncycastle.asn1.ASN1Object; - -/** - * This is a dummy class that implements just enough of {@link org.bouncycastle.asn1.x509.SubjectPublicKeyInfo} - * to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}. - */ -public class SubjectPublicKeyInfo extends ASN1Object { -} 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 64d9e12887..4574d7363f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -43,6 +43,10 @@ import net.corda.core.internal.div import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps import net.corda.core.internal.notary.NotaryService import net.corda.core.internal.rootMessage +import net.corda.core.internal.telemetry.OpenTelemetryComponent +import net.corda.core.internal.telemetry.SimpleLogTelemetryComponent +import net.corda.core.internal.telemetry.TelemetryComponent +import net.corda.core.internal.telemetry.TelemetryServiceImpl import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.ClientRpcSslOptions import net.corda.core.messaging.CordaRPCOps @@ -57,10 +61,6 @@ import net.corda.core.node.services.ContractUpgradeService import net.corda.core.node.services.CordaService import net.corda.core.node.services.IdentityService import net.corda.core.node.services.KeyManagementService -import net.corda.core.internal.telemetry.SimpleLogTelemetryComponent -import net.corda.core.internal.telemetry.TelemetryComponent -import net.corda.core.internal.telemetry.OpenTelemetryComponent -import net.corda.core.internal.telemetry.TelemetryServiceImpl import net.corda.core.node.services.TelemetryService import net.corda.core.node.services.TransactionVerifierService import net.corda.core.node.services.diagnostics.DiagnosticsService @@ -76,9 +76,6 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.days import net.corda.core.utilities.millis import net.corda.core.utilities.minutes -import net.corda.djvm.source.ApiSource -import net.corda.djvm.source.EmptyApi -import net.corda.djvm.source.UserSource import net.corda.node.CordaClock import net.corda.node.VersionInfo import net.corda.node.internal.attachments.AttachmentTrustInfoRPCOpsImpl @@ -123,13 +120,15 @@ import net.corda.node.services.network.NetworkParameterUpdateListener import net.corda.node.services.network.NetworkParametersHotloader import net.corda.node.services.network.NodeInfoWatcher import net.corda.node.services.network.PersistentNetworkMapCache +import net.corda.node.services.network.PersistentPartyInfoCache import net.corda.node.services.persistence.AbstractPartyDescriptor import net.corda.node.services.persistence.AbstractPartyToX500NameAsStringConverter import net.corda.node.services.persistence.AttachmentStorageInternal import net.corda.node.services.persistence.DBCheckpointPerformanceRecorder import net.corda.node.services.persistence.DBCheckpointStorage +import net.corda.node.services.persistence.AesDbEncryptionService import net.corda.node.services.persistence.DBTransactionMappingStorage -import net.corda.node.services.persistence.DBTransactionStorage +import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.persistence.NodePropertiesPersistentStore import net.corda.node.services.persistence.PublicKeyToOwningIdentityCacheImpl @@ -143,10 +142,7 @@ import net.corda.node.services.statemachine.FlowOperator import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.services.statemachine.SingleThreadedStateMachineManager import net.corda.node.services.statemachine.StateMachineManager -import net.corda.node.services.transactions.BasicVerifierFactoryService -import net.corda.node.services.transactions.DeterministicVerifierFactoryService import net.corda.node.services.transactions.InMemoryTransactionVerifierService -import net.corda.node.services.transactions.VerifierFactoryService import net.corda.node.services.upgrade.ContractUpgradeServiceImpl import net.corda.node.services.vault.NodeVaultService import net.corda.node.utilities.AffinityExecutor @@ -211,8 +207,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration, protected val flowManager: FlowManager, val serverThread: AffinityExecutor.ServiceAffinityExecutor, val busyNodeLatch: ReusableLatch = ReusableLatch(), - djvmBootstrapSource: ApiSource = EmptyApi, - djvmCordaSource: UserSource? = null, protected val allowHibernateToManageAppSchema: Boolean = false, private val allowAppSchemaUpgradeWithCheckpoints: Boolean = false) : SingletonSerializeAsToken() { @@ -286,6 +280,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } val networkMapCache = PersistentNetworkMapCache(cacheFactory, database, identityService).tokenize() + val partyInfoCache = PersistentPartyInfoCache(networkMapCache, cacheFactory, database) + val encryptionService = AesDbEncryptionService(database) + @Suppress("LeakingThis") + val cryptoService = makeCryptoService() @Suppress("LeakingThis") val transactionStorage = makeTransactionStorage(configuration.transactionCacheSizeBytes).tokenize() val networkMapClient: NetworkMapClient? = configuration.networkServices?.let { NetworkMapClient(it.networkMapURL, versionInfo) } @@ -297,8 +295,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration, ).tokenize() val attachmentTrustCalculator = makeAttachmentTrustCalculator(configuration, database, rotatedKeys) @Suppress("LeakingThis") - val cryptoService = makeCryptoService() - @Suppress("LeakingThis") val networkParametersStorage = makeNetworkParametersStorage() val cordappProvider = CordappProviderImpl(cordappLoader, CordappConfigFileProvider(configuration.cordappDirectories), attachments).tokenize() val diagnosticsService = NodeDiagnosticsService().tokenize() @@ -657,6 +653,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration, tokenizableServices = null verifyCheckpointsCompatible(frozenTokenizableServices) + partyInfoCache.start() + encryptionService.start(nodeInfo.legalIdentities[0]) + /* Note the .get() at the end of the distributeEvent call, below. This will block until all Corda Services have returned from processing the event, allowing a service to prevent the state machine manager from starting (just below this) until the service is ready. @@ -695,6 +694,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, log.warn("Not distributing events as NetworkMap is not ready") } } + setNodeStatus(NodeStatus.STARTED) return resultingNodeInfo } @@ -1086,7 +1086,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } protected open fun makeTransactionStorage(transactionCacheSizeBytes: Long): WritableTransactionStorage { - return DBTransactionStorage(database, cacheFactory, platformClock) + return DBTransactionStorageLedgerRecovery(database, cacheFactory, platformClock, encryptionService, partyInfoCache) } protected open fun makeNetworkParametersStorage(): NetworkParametersStorage { @@ -1325,8 +1325,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } override fun specialise(ltx: LedgerTransaction): LedgerTransaction { - val ledgerTransaction = servicesForResolution.specialise(ltx) - return verifierFactoryService.apply(ledgerTransaction) + return servicesForResolution.specialise(ltx) } override fun onNewNetworkParameters(networkParameters: NetworkParameters) { 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 f86bd83eda..963095597d 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -4,7 +4,6 @@ import com.codahale.metrics.MetricFilter import com.codahale.metrics.MetricRegistry import com.codahale.metrics.jmx.JmxReporter import com.github.benmanes.caffeine.cache.Caffeine -import com.jcabi.manifests.Manifests import com.palominolabs.metrics.newrelic.AllEnabledMetricAttributeFilter import com.palominolabs.metrics.newrelic.NewRelicReporter import io.netty.util.NettyRuntime @@ -20,7 +19,6 @@ import net.corda.core.internal.concurrent.thenMatch import net.corda.core.internal.div import net.corda.core.internal.errors.AddressBindingException import net.corda.core.internal.getJavaUpdateVersion -import net.corda.core.internal.isRegularFile import net.corda.core.internal.notary.NotaryService import net.corda.core.messaging.RPCOps import net.corda.core.node.NetworkParameters @@ -30,11 +28,6 @@ import net.corda.core.serialization.internal.SerializationEnvironment import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger -import net.corda.djvm.source.ApiSource -import net.corda.djvm.source.BootstrapClassLoader -import net.corda.djvm.source.EmptyApi -import net.corda.djvm.source.UserPathSource -import net.corda.djvm.source.UserSource import net.corda.node.CordaClock import net.corda.node.SimpleClock import net.corda.node.VersionInfo @@ -98,7 +91,6 @@ import java.lang.Long.max import java.lang.Long.min import java.net.BindException import java.net.InetAddress -import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths import java.time.Clock @@ -124,8 +116,6 @@ open class Node(configuration: NodeConfiguration, private val initialiseSerialization: Boolean = true, flowManager: FlowManager = NodeFlowManager(configuration.flowOverrides), cacheFactoryPrototype: BindableNamedCacheFactory = DefaultNamedCacheFactory(), - djvmBootstrapSource: ApiSource = createBootstrapSource(configuration), - djvmCordaSource: UserSource? = createCordaSource(configuration), allowHibernateToManageAppSchema: Boolean = false ) : AbstractNode( configuration, @@ -135,8 +125,6 @@ open class Node(configuration: NodeConfiguration, flowManager, // Under normal (non-test execution) it will always be "1" AffinityExecutor.ServiceAffinityExecutor("Node thread-${sameVmNodeCounter.incrementAndGet()}", 1), - djvmBootstrapSource = djvmBootstrapSource, - djvmCordaSource = djvmCordaSource, allowHibernateToManageAppSchema = allowHibernateToManageAppSchema ) { @@ -144,10 +132,6 @@ open class Node(configuration: NodeConfiguration, nodeInfo companion object { - private const val CORDA_DETERMINISTIC_RUNTIME_ATTR = "Corda-Deterministic-Runtime" - private const val CORDA_DETERMINISTIC_CLASSPATH_ATTR = "Corda-Deterministic-Classpath" - private const val CORDA_DJVM = "net.corda.djvm" - private val staticLog = contextLogger() var renderBasicInfoToConsole = true @@ -206,74 +190,6 @@ open class Node(configuration: NodeConfiguration, false } } - - private fun manifestValue(attrName: String): String? = if (Manifests.exists(attrName)) Manifests.read(attrName) else null - - private fun createManifestCordaSource(config: NodeConfiguration): UserSource? { - val classpathSource = config.baseDirectory.resolve("djvm") - val djvmClasspath = manifestValue(CORDA_DETERMINISTIC_CLASSPATH_ATTR) - - return if (djvmClasspath == null) { - staticLog.warn("{} missing from MANIFEST.MF - deterministic contract verification now impossible!", - CORDA_DETERMINISTIC_CLASSPATH_ATTR) - null - } else if (!Files.isDirectory(classpathSource)) { - staticLog.warn("{} directory does not exist - deterministic contract verification now impossible!", - classpathSource.toAbsolutePath()) - null - } else { - val files = djvmClasspath.split("\\s++".toRegex(), 0).map { classpathSource.resolve(it) } - .filter { Files.isRegularFile(it) || Files.isSymbolicLink(it) } - staticLog.info("Corda Deterministic Libraries: {}", files.map(Path::getFileName).joinToString()) - - val jars = files.map { it.toUri().toURL() }.toTypedArray() - UserPathSource(jars) - } - } - - private fun createManifestBootstrapSource(config: NodeConfiguration): ApiSource { - val deterministicRt = manifestValue(CORDA_DETERMINISTIC_RUNTIME_ATTR) - if (deterministicRt == null) { - staticLog.warn("{} missing from MANIFEST.MF - will use host JVM for deterministic runtime.", - CORDA_DETERMINISTIC_RUNTIME_ATTR) - return EmptyApi - } - - val bootstrapSource = config.baseDirectory.resolve("djvm").resolve(deterministicRt) - return if (bootstrapSource.isRegularFile()) { - staticLog.info("Deterministic Runtime: {}", bootstrapSource.fileName) - BootstrapClassLoader(bootstrapSource) - } else { - staticLog.warn("NO DETERMINISTIC RUNTIME FOUND - will use host JVM instead.") - EmptyApi - } - } - - private fun createBootstrapSource(config: NodeConfiguration): ApiSource { - val djvm = config.devModeOptions?.djvm - return if (config.devMode && djvm != null) { - djvm.bootstrapSource?.let { BootstrapClassLoader(Paths.get(it)) } ?: EmptyApi - } else if (java.lang.Boolean.getBoolean(CORDA_DJVM)) { - createManifestBootstrapSource(config) - } else { - EmptyApi - } - } - - private fun createCordaSource(config: NodeConfiguration): UserSource? { - val djvm = config.devModeOptions?.djvm - return if (config.devMode && djvm != null) { - if (djvm.cordaSource.isEmpty()) { - null - } else { - UserPathSource(djvm.cordaSource.map { Paths.get(it) }) - } - } else if (java.lang.Boolean.getBoolean(CORDA_DJVM)) { - createManifestCordaSource(config) - } else { - null - } - } } override val log: Logger get() = staticLog diff --git a/node/src/main/kotlin/net/corda/node/internal/djvm/AttachmentFactory.kt b/node/src/main/kotlin/net/corda/node/internal/djvm/AttachmentFactory.kt deleted file mode 100644 index d272a7428e..0000000000 --- a/node/src/main/kotlin/net/corda/node/internal/djvm/AttachmentFactory.kt +++ /dev/null @@ -1,49 +0,0 @@ -package net.corda.node.internal.djvm - -import net.corda.core.contracts.Attachment -import net.corda.core.contracts.ContractAttachment -import net.corda.core.serialization.serialize -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.node.djvm.AttachmentBuilder -import java.util.function.Function - -class AttachmentFactory( - classLoader: SandboxClassLoader, - private val taskFactory: Function>, out Function>, - private val sandboxBasicInput: Function, - private val serializer: Serializer -) { - private val sandboxOpenAttachment: Function = classLoader.createForImport( - Function(Attachment::open).andThen(sandboxBasicInput) - ) - - fun toSandbox(attachments: List): Any? { - val builder = taskFactory.apply(AttachmentBuilder::class.java) - for (attachment in attachments) { - builder.apply(generateArgsFor(attachment)) - } - return builder.apply(null) - } - - private fun generateArgsFor(attachment: Attachment): Array { - val signerKeys = serializer.deserialize(attachment.signerKeys.serialize()) - val id = serializer.deserialize(attachment.id.serialize()) - val size = sandboxBasicInput.apply(attachment.size) - return if (attachment is ContractAttachment) { - val underlyingAttachment = attachment.attachment - arrayOf( - serializer.deserialize(underlyingAttachment.signerKeys.serialize()), - size, id, - underlyingAttachment, - sandboxOpenAttachment, - sandboxBasicInput.apply(attachment.contract), - sandboxBasicInput.apply(attachment.additionalContracts.toTypedArray()), - sandboxBasicInput.apply(attachment.uploader), - signerKeys, - sandboxBasicInput.apply(attachment.version) - ) - } else { - arrayOf(signerKeys, size, id, attachment, sandboxOpenAttachment) - } - } -} diff --git a/node/src/main/kotlin/net/corda/node/internal/djvm/CommandFactory.kt b/node/src/main/kotlin/net/corda/node/internal/djvm/CommandFactory.kt deleted file mode 100644 index 3727b1b0b6..0000000000 --- a/node/src/main/kotlin/net/corda/node/internal/djvm/CommandFactory.kt +++ /dev/null @@ -1,17 +0,0 @@ -package net.corda.node.internal.djvm - -import net.corda.node.djvm.CommandBuilder -import java.util.function.Function - -class CommandFactory( - private val taskFactory: Function>, out Function> -) { - fun toSandbox(signers: Any?, commands: Any?, partialMerkleLeafIndices: IntArray?): Any? { - val builder = taskFactory.apply(CommandBuilder::class.java) - return builder.apply(arrayOf( - signers, - commands, - partialMerkleLeafIndices - )) - } -} diff --git a/node/src/main/kotlin/net/corda/node/internal/djvm/ComponentFactory.kt b/node/src/main/kotlin/net/corda/node/internal/djvm/ComponentFactory.kt deleted file mode 100644 index adba2daa13..0000000000 --- a/node/src/main/kotlin/net/corda/node/internal/djvm/ComponentFactory.kt +++ /dev/null @@ -1,43 +0,0 @@ -@file:JvmName("ComponentUtils") -package net.corda.node.internal.djvm - -import net.corda.core.contracts.ComponentGroupEnum -import net.corda.core.crypto.DigestService -import net.corda.core.transactions.ComponentGroup -import net.corda.core.transactions.FilteredComponentGroup -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.node.djvm.ComponentBuilder -import java.util.function.Function - -class ComponentFactory( - private val classLoader: SandboxClassLoader, - private val taskFactory: Function>, out Function>, - private val sandboxBasicInput: Function, - private val serializer: Serializer, - private val componentGroups: List -) { - fun toSandbox( - groupType: ComponentGroupEnum, - clazz: Class<*> - ): Any? { - val components = (componentGroups.firstOrNull(groupType::isSameType) ?: return null).components - val componentBytes = Array(components.size) { idx -> components[idx].bytes } - return taskFactory.apply(ComponentBuilder::class.java).apply(arrayOf( - classLoader.createForImport(serializer.deserializerFor(clazz)), - sandboxBasicInput.apply(groupType), - componentBytes - )) - } - - fun calculateLeafIndicesFor(groupType: ComponentGroupEnum, digestService: DigestService): IntArray? { - val componentGroup = componentGroups.firstOrNull(groupType::isSameType) as? FilteredComponentGroup ?: return null - val componentHashes = componentGroup.components.mapIndexed { index, component -> - digestService.componentHash(componentGroup.nonces[index], component) - } - return componentHashes.map { componentGroup.partialMerkleTree.leafIndex(it) }.toIntArray() - } -} - -private fun ComponentGroupEnum.isSameType(group: ComponentGroup): Boolean { - return group.groupIndex == ordinal -} diff --git a/node/src/main/kotlin/net/corda/node/internal/djvm/DeterministicVerifier.kt b/node/src/main/kotlin/net/corda/node/internal/djvm/DeterministicVerifier.kt deleted file mode 100644 index 3263868aa8..0000000000 --- a/node/src/main/kotlin/net/corda/node/internal/djvm/DeterministicVerifier.kt +++ /dev/null @@ -1,147 +0,0 @@ -package net.corda.node.internal.djvm - -import net.corda.core.contracts.CommandData -import net.corda.core.contracts.ComponentGroupEnum.COMMANDS_GROUP -import net.corda.core.contracts.ComponentGroupEnum.OUTPUTS_GROUP -import net.corda.core.contracts.ComponentGroupEnum.SIGNERS_GROUP -import net.corda.core.contracts.TransactionState -import net.corda.core.contracts.TransactionVerificationException -import net.corda.core.crypto.SecureHash -import net.corda.core.internal.TransactionVerifier -import net.corda.core.internal.Verifier -import net.corda.core.internal.getNamesOfClassesImplementing -import net.corda.core.serialization.SerializationCustomSerializer -import net.corda.core.serialization.SerializationWhitelist -import net.corda.core.serialization.serialize -import net.corda.core.transactions.LedgerTransaction -import net.corda.core.utilities.contextLogger -import net.corda.djvm.SandboxConfiguration -import net.corda.djvm.execution.ExecutionSummary -import net.corda.djvm.execution.IsolatedTask -import net.corda.djvm.execution.SandboxException -import net.corda.djvm.messages.Message -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.djvm.source.ClassSource -import net.corda.node.djvm.LtxSupplierFactory -import java.util.function.Function -import kotlin.collections.LinkedHashSet - -class DeterministicVerifier( - private val ltx: LedgerTransaction, - private val transactionClassLoader: ClassLoader, - private val sandboxConfiguration: SandboxConfiguration -) : Verifier { - private companion object { - private val logger = contextLogger() - } - - /** - * Read the whitelisted classes without using the [java.util.ServiceLoader] mechanism - * because the whitelists themselves are untrusted. - */ - private fun getSerializationWhitelistNames(classLoader: ClassLoader): Set { - return classLoader.getResources("META-INF/services/${SerializationWhitelist::class.java.name}").asSequence() - .flatMapTo(LinkedHashSet()) { url -> - url.openStream().bufferedReader().useLines { lines -> - // Parse file format, as documented for java.util.ServiceLoader: - // - Remove everything after comment character '#'. - // - Strip whitespace. - // - Ignore empty lines. - lines.map { it.substringBefore('#') }.map(String::trim).filterNot(String::isEmpty).toList() - }.asSequence() - } - } - - override fun verify() { - val customSerializerNames = getNamesOfClassesImplementing(transactionClassLoader, SerializationCustomSerializer::class.java) - val serializationWhitelistNames = getSerializationWhitelistNames(transactionClassLoader) - val result = IsolatedTask(ltx.id.toString(), sandboxConfiguration).run(Function { classLoader -> - (classLoader.parent as? SandboxClassLoader)?.apply { - /** - * We don't need to add either Java APIs or Corda's own classes - * into the external cache because these are already being cached - * more efficiently inside the [SandboxConfiguration]. - * - * The external cache is for this Nodes's CorDapps, where classes - * with the same names may appear in multiple different jars. - */ - externalCaching = false - } - - val taskFactory = classLoader.createRawTaskFactory().compose(classLoader.createSandboxFunction()) - val sandboxBasicInput = classLoader.createBasicInput() - - /** - * Deserialise the [LedgerTransaction] again into something - * that we can execute inside the DJVM's sandbox. - */ - val sandboxTx = ltx.transform { componentGroups, serializedInputs, serializedReferences -> - val serializer = Serializer(classLoader, customSerializerNames, serializationWhitelistNames) - val componentFactory = ComponentFactory( - classLoader, - taskFactory, - sandboxBasicInput, - serializer, - componentGroups - ) - val attachmentFactory = AttachmentFactory( - classLoader, - taskFactory, - sandboxBasicInput, - serializer - ) - - val idData = ltx.id.serialize() - val notaryData = ltx.notary?.serialize() - val timeWindowData = ltx.timeWindow?.serialize() - val privacySaltData = ltx.privacySalt.serialize() - val networkingParametersData = ltx.networkParameters?.serialize() - val digestServiceData = ltx.digestService.serialize() - - val createSandboxTx = taskFactory.apply(LtxSupplierFactory::class.java) - createSandboxTx.apply(arrayOf( - classLoader.createForImport(Function { serializer.deserialize(serializedInputs) }), - componentFactory.toSandbox(OUTPUTS_GROUP, TransactionState::class.java), - CommandFactory(taskFactory).toSandbox( - componentFactory.toSandbox(SIGNERS_GROUP, List::class.java), - componentFactory.toSandbox(COMMANDS_GROUP, CommandData::class.java), - componentFactory.calculateLeafIndicesFor(COMMANDS_GROUP, ltx.digestService) - ), - attachmentFactory.toSandbox(ltx.attachments), - serializer.deserialize(idData), - serializer.deserialize(notaryData), - serializer.deserialize(timeWindowData), - serializer.deserialize(privacySaltData), - serializer.deserialize(networkingParametersData), - classLoader.createForImport(Function { serializer.deserialize(serializedReferences) }), - serializer.deserialize(digestServiceData) - )) - } - - val verifier = taskFactory.apply(TransactionVerifier::class.java) - - // Now execute the contract verifier task within the sandbox... - verifier.apply(sandboxTx) - }) - - with (result.costs) { - logger.info("Verify {} complete: allocations={}, invocations={}, jumps={}, throws={}", - ltx.id, allocations, invocations, jumps, throws) - } - - result.exception?.run { - val sandboxEx = SandboxException( - Message.getMessageFromException(this), - result.identifier, - ClassSource.fromClassName(TransactionVerifier::class.java.name), - ExecutionSummary(result.costs), - this - ) - logger.error("Error validating transaction ${ltx.id}.", sandboxEx) - throw DeterministicVerificationException(ltx.id, sandboxEx.message ?: "", sandboxEx) - } - } -} - -class DeterministicVerificationException(txId: SecureHash, message: String, cause: Throwable) - : TransactionVerificationException(txId, message, cause) diff --git a/node/src/main/kotlin/net/corda/node/internal/djvm/Serializer.kt b/node/src/main/kotlin/net/corda/node/internal/djvm/Serializer.kt deleted file mode 100644 index 32fedf52fa..0000000000 --- a/node/src/main/kotlin/net/corda/node/internal/djvm/Serializer.kt +++ /dev/null @@ -1,71 +0,0 @@ -package net.corda.node.internal.djvm - -import net.corda.core.internal.SerializedStateAndRef -import net.corda.core.serialization.AMQP_ENVELOPE_CACHE_INITIAL_CAPACITY -import net.corda.core.serialization.AMQP_ENVELOPE_CACHE_PROPERTY -import net.corda.core.serialization.DESERIALIZATION_CACHE_PROPERTY -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.SerializationFactory -import net.corda.core.serialization.SerializedBytes -import net.corda.core.serialization.serialize -import net.corda.core.utilities.ByteSequence -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.node.djvm.ComponentBuilder -import net.corda.serialization.djvm.createSandboxSerializationEnv -import java.util.function.Function - -class Serializer( - private val classLoader: SandboxClassLoader, - customSerializerNames: Set, - serializationWhitelists: Set -) { - private val factory: SerializationFactory - private val context: SerializationContext - - init { - val env = createSandboxSerializationEnv(classLoader, customSerializerNames, serializationWhitelists) - factory = env.serializationFactory - context = env.p2pContext.withProperties(mapOf( - // Duplicate the P2P SerializationContext and give it - // these extra properties, just for this transaction. - AMQP_ENVELOPE_CACHE_PROPERTY to HashMap(AMQP_ENVELOPE_CACHE_INITIAL_CAPACITY), - DESERIALIZATION_CACHE_PROPERTY to HashMap() - )) - } - - /** - * Convert a list of [SerializedStateAndRef] objects into arrays - * of deserialized sandbox objects. We will pass this array into - * [LtxSupplierFactory][net.corda.node.djvm.LtxSupplierFactory] - * to be transformed finally to a list of - * [StateAndRef][net.corda.core.contracts.StateAndRef] objects, - */ - fun deserialize(stateRefs: List): Array> { - return stateRefs.map { - arrayOf(deserialize(it.serializedState), deserialize(it.ref.serialize())) - }.toTypedArray() - } - - /** - * Generate a [Function] that deserializes a [ByteArray] into an instance - * of the given sandbox class. We import this [Function] into the sandbox - * so that [ComponentBuilder] can deserialize objects lazily. - */ - fun deserializerFor(clazz: Class<*>): Function { - val sandboxClass = classLoader.toSandboxClass(clazz) - return Function { bytes -> - bytes?.run { - factory.deserialize(ByteSequence.of(this), sandboxClass, context) - } - } - } - - fun deserializeTo(clazz: Class<*>, bytes: ByteSequence): Any { - val sandboxClass = classLoader.toSandboxClass(clazz) - return factory.deserialize(bytes, sandboxClass, context) - } - - inline fun deserialize(bytes: SerializedBytes?): Any? { - return deserializeTo(T::class.java, bytes ?: return null) - } -} diff --git a/node/src/main/kotlin/net/corda/node/services/DbTransactionsResolver.kt b/node/src/main/kotlin/net/corda/node/services/DbTransactionsResolver.kt index bc6cf3d2af..9a8e9d5eda 100644 --- a/node/src/main/kotlin/net/corda/node/services/DbTransactionsResolver.kt +++ b/node/src/main/kotlin/net/corda/node/services/DbTransactionsResolver.kt @@ -8,10 +8,12 @@ import net.corda.core.internal.ResolveTransactionsFlow import net.corda.core.internal.TransactionsResolver import net.corda.core.internal.dependencies import net.corda.core.node.StatesToRecord +import net.corda.core.node.services.TransactionStatus import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.debug -import net.corda.core.utilities.trace import net.corda.core.utilities.seconds +import net.corda.core.utilities.trace +import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.api.WritableTransactionStorage import java.util.* @@ -20,7 +22,8 @@ class DbTransactionsResolver(private val flow: ResolveTransactionsFlow) : Transa private val logger = flow.logger @Suspendable - override fun downloadDependencies(batchMode: Boolean) { + override fun downloadDependencies(batchMode: Boolean, recoveryMode: Boolean) { + if (recoveryMode) throw NotImplementedError("Enterprise only Ledger Recovery feature") logger.debug { "Downloading dependencies for transactions ${flow.txHashes}" } val transactionStorage = flow.serviceHub.validatedTransactions as WritableTransactionStorage @@ -98,15 +101,14 @@ class DbTransactionsResolver(private val flow: ResolveTransactionsFlow) : Transa override fun recordDependencies(usedStatesToRecord: StatesToRecord) { val sortedDependencies = checkNotNull(this.sortedDependencies) logger.trace { "Recording ${sortedDependencies.size} dependencies for ${flow.txHashes.size} transactions" } - val transactionStorage = flow.serviceHub.validatedTransactions as WritableTransactionStorage for (txId in sortedDependencies) { // Retrieve and delete the transaction from the unverified store. - val (tx, isVerified) = checkNotNull(transactionStorage.getTransactionInternal(txId)) { + val (tx, txStatus) = checkNotNull(flow.serviceHub.validatedTransactions.getTransactionWithStatus(txId)) { "Somehow the unverified transaction ($txId) that we stored previously is no longer there." } - if (!isVerified) { + if (txStatus == TransactionStatus.UNVERIFIED) { tx.verify(flow.serviceHub) - flow.serviceHub.recordTransactions(usedStatesToRecord, listOf(tx)) + (flow.serviceHub as ServiceHubInternal).recordTransactions(usedStatesToRecord, listOf(tx), false, disableSoftLocking = true) } else { logger.debug { "No need to record $txId as it's already been verified" } } diff --git a/node/src/main/kotlin/net/corda/node/services/EncryptionService.kt b/node/src/main/kotlin/net/corda/node/services/EncryptionService.kt new file mode 100644 index 0000000000..85dea166e0 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/EncryptionService.kt @@ -0,0 +1,42 @@ +package net.corda.node.services + +/** + * A service for encrypting data. This abstraction does not mandate any security properties except the same service instance will be + * able to decrypt ciphertext encrypted by it. Further security properties are defined by the implementations. This includes the encryption + * protocol used. + */ +interface EncryptionService { + /** + * Encrypt the given [plaintext]. The encryption key used is dependent on the implementation. The returned ciphertext can be decrypted + * using [decrypt]. + * + * An optional public [additionalData] bytes can also be provided which will be authenticated (thus tamperproof) alongside the + * ciphertext but not encrypted. It will be incorporated into the returned bytes in an implementation dependent fashion. + */ + fun encrypt(plaintext: ByteArray, additionalData: ByteArray? = null): ByteArray + + /** + * Decrypt ciphertext that was encrypted using [encrypt] and return the original plaintext plus the additional data authenticated (if + * present). The service will select the correct encryption key to use. + */ + fun decrypt(ciphertext: ByteArray): PlaintextAndAAD + + /** + * Extracts the (unauthenticated) additional data, if present, from the given [ciphertext]. This is the public data that would have been + * given at encryption time. + * + * Note, this method does not verify if the data was tampered with, and hence is unauthenticated. To have it authenticated requires + * calling [decrypt]. This is still useful however, as it doesn't require the encryption key, and so a third-party can view the + * additional data without needing access to the key. + */ + fun extractUnauthenticatedAdditionalData(ciphertext: ByteArray): ByteArray? + + + /** + * Represents the decrypted plaintext and the optional authenticated additional data bytes. + */ + class PlaintextAndAAD(val plaintext: ByteArray, val authenticatedAdditionalData: ByteArray?) { + operator fun component1() = plaintext + operator fun component2() = authenticatedAdditionalData + } +} diff --git a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt index 8139b3b0a4..b283c137c4 100644 --- a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt +++ b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt @@ -3,10 +3,21 @@ package net.corda.node.services.api import net.corda.core.concurrent.CordaFuture import net.corda.core.context.InvocationContext import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.TransactionSignature import net.corda.core.flows.FlowLogic import net.corda.core.flows.StateMachineRunId -import net.corda.core.internal.* +import net.corda.core.flows.TransactionMetadata +import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.FlowStateMachineHandle +import net.corda.core.internal.NamedCacheFactory +import net.corda.core.internal.ResolveTransactionsFlow +import net.corda.core.internal.ServiceHubCoreInternal +import net.corda.core.internal.TransactionsResolver +import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.concurrent.OpenFuture +import net.corda.core.internal.dependencies +import net.corda.core.internal.requireSupportedHashType +import net.corda.core.internal.warnOnce import net.corda.core.messaging.DataFeed import net.corda.core.messaging.StateMachineTransactionMapping import net.corda.core.node.NodeInfo @@ -15,6 +26,7 @@ import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCacheBase import net.corda.core.node.services.TransactionStorage import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.contextLogger import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.cordapp.CordappProviderInternal @@ -26,8 +38,12 @@ import net.corda.node.services.persistence.AttachmentStorageInternal import net.corda.node.services.statemachine.ExternalEvent import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.nodeapi.internal.persistence.CordaPersistence -import java.security.PublicKey -import java.util.* +import java.security.SignatureException +import java.util.ArrayList +import java.util.Collections +import java.util.HashMap +import java.util.HashSet +import java.util.LinkedHashSet interface NetworkMapCacheInternal : NetworkMapCache, NetworkMapCacheBase { override val nodeReady: OpenFuture @@ -53,6 +69,7 @@ interface NetworkMapCacheInternal : NetworkMapCache, NetworkMapCacheBase { interface ServiceHubInternal : ServiceHubCoreInternal { companion object { private val log = contextLogger() + private val SIGNATURE_VERIFICATION_DISABLED = java.lang.Boolean.getBoolean("net.corda.recordtransaction.signature.verification.disabled") private fun topologicalSort(transactions: Collection): Collection { if (transactions.size == 1) return transactions @@ -63,12 +80,16 @@ interface ServiceHubInternal : ServiceHubCoreInternal { return sort.complete() } + @Suppress("LongParameterList") fun recordTransactions(statesToRecord: StatesToRecord, txs: Collection, validatedTransactions: WritableTransactionStorage, stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage, vaultService: VaultServiceInternal, - database: CordaPersistence) { + database: CordaPersistence, + disableSoftLocking: Boolean = false, + updateFn: (SignedTransaction) -> Boolean = validatedTransactions::addTransaction + ) { database.transaction { require(txs.isNotEmpty()) { "No transactions passed in for recording" } @@ -79,9 +100,9 @@ interface ServiceHubInternal : ServiceHubCoreInternal { // for transactions being recorded at ONLY_RELEVANT, if this transaction has been seen before its outputs should already // have been recorded at ONLY_RELEVANT, so there shouldn't be anything to re-record here. val (recordedTransactions, previouslySeenTxs) = if (statesToRecord != StatesToRecord.ALL_VISIBLE) { - orderedTxs.filter(validatedTransactions::addTransaction) to emptyList() + orderedTxs.filter(updateFn) to emptyList() } else { - orderedTxs.partition(validatedTransactions::addTransaction) + orderedTxs.partition(updateFn) } val stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id if (stateMachineRunId != null) { @@ -126,7 +147,22 @@ interface ServiceHubInternal : ServiceHubCoreInternal { // // Because the primary use case for recording irrelevant states is observer/regulator nodes, who are unlikely // to make writes to the ledger very often or at all, we choose to punt this issue for the time being. - vaultService.notifyAll(statesToRecord, recordedTransactions.map { it.coreTransaction }, previouslySeenTxs.map { it.coreTransaction }) + vaultService.notifyAll(statesToRecord, recordedTransactions.map { it.coreTransaction }, previouslySeenTxs.map { it.coreTransaction }, disableSoftLocking) + } + } + + @Suppress("LongParameterList") + fun finalizeTransactionWithExtraSignatures(statesToRecord: StatesToRecord, + txn: SignedTransaction, + sigs: Collection, + validatedTransactions: WritableTransactionStorage, + stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage, + vaultService: VaultServiceInternal, + database: CordaPersistence) { + database.transaction { + recordTransactions(statesToRecord, listOf(txn), validatedTransactions, stateMachineRecordedTransactionMapping, vaultService, database) { + validatedTransactions.finalizeTransactionWithExtraSignatures(it, sigs) + } } } } @@ -155,18 +191,89 @@ interface ServiceHubInternal : ServiceHubCoreInternal { fun getFlowFactory(initiatingFlowClass: Class>): InitiatedFlowFactory<*>? val cacheFactory: NamedCacheFactory - override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) { - txs.forEach { requireSupportedHashType(it) } + override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) = + recordTransactions(statesToRecord, txs, SIGNATURE_VERIFICATION_DISABLED) + + override fun recordSenderTransactionRecoveryMetadata(txnId: SecureHash, txnMetadata: TransactionMetadata) = + validatedTransactions.addSenderTransactionRecoveryMetadata(txnId, txnMetadata) + + override fun recordReceiverTransactionRecoveryMetadata(txnId: SecureHash, sender: CordaX500Name, txnMetadata: TransactionMetadata) = + validatedTransactions.addReceiverTransactionRecoveryMetadata(txnId, sender, txnMetadata) + + @Suppress("NestedBlockDepth") + @VisibleForTesting + fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable, disableSignatureVerification: Boolean, disableSoftLocking: Boolean = false) { + txs.forEach { + requireSupportedHashType(it) + if (it.coreTransaction is WireTransaction) { + if (disableSignatureVerification) { + log.warnOnce("The current usage of recordTransactions is unsafe." + + "Recording transactions without signature verification may lead to severe problems with ledger consistency.") + } else { + try { + it.verifyRequiredSignatures() + } + catch (e: SignatureException) { + throw IllegalStateException("Signature verification failed", e) + } + } + } + } recordTransactions( statesToRecord, txs as? Collection ?: txs.toList(), // We can't change txs to a Collection as it's now part of the public API validatedTransactions, stateMachineRecordedTransactionMapping, vaultService, + database, + disableSoftLocking + ) + } + + override fun finalizeTransactionWithExtraSignatures(txn: SignedTransaction, sigs: Collection, statesToRecord: StatesToRecord) { + requireSupportedHashType(txn) + require(sigs.isNotEmpty()) { "No signatures passed in for recording" } + if (txn.coreTransaction is WireTransaction) + (txn + sigs).verifyRequiredSignatures() + finalizeTransactionWithExtraSignatures( + statesToRecord, + txn, + sigs, + validatedTransactions, + stateMachineRecordedTransactionMapping, + vaultService, database ) } + override fun finalizeTransaction(txn: SignedTransaction, statesToRecord: StatesToRecord) { + requireSupportedHashType(txn) + if (txn.coreTransaction is WireTransaction) + txn.verifyRequiredSignatures() + database.transaction { + recordTransactions(statesToRecord, listOf(txn), validatedTransactions, stateMachineRecordedTransactionMapping, vaultService, database) { + validatedTransactions.finalizeTransaction(txn) + } + } + } + + override fun recordUnnotarisedTransaction(txn: SignedTransaction) { + if (txn.coreTransaction is WireTransaction) { + txn.notary?.let { notary -> + txn.verifySignaturesExcept(notary.owningKey) + } ?: txn.verifyRequiredSignatures() + } + database.transaction { + validatedTransactions.addUnnotarisedTransaction(txn) + } + } + + override fun removeUnnotarisedTransaction(id: SecureHash) { + database.transaction { + validatedTransactions.removeUnnotarisedTransaction(id) + } + } + override fun createTransactionsResolver(flow: ResolveTransactionsFlow): TransactionsResolver = DbTransactionsResolver(flow) /** @@ -253,17 +360,61 @@ interface WritableTransactionStorage : TransactionStorage { // TODO: Throw an exception if trying to add a transaction with fewer signatures than an existing entry. fun addTransaction(transaction: SignedTransaction): Boolean + /** + * Add an un-notarised transaction to the store with a status of *MISSING_TRANSACTION_SIG* and inclusive of flow recovery metadata. + * + * @param transaction The transaction to be recorded. + * @return true if the transaction was recorded as a *new* transaction, false if the transaction already exists. + */ + fun addUnnotarisedTransaction(transaction: SignedTransaction): Boolean + + /** + * Records Sender [TransactionMetadata] for a given txnId. + * + * @param txId The SecureHash of a transaction. + * @param metadata The recovery metadata associated with a transaction. + * @return encrypted distribution list (hashed peers -> StatesToRecord values). + */ + fun addSenderTransactionRecoveryMetadata(txId: SecureHash, metadata: TransactionMetadata): ByteArray? + + /** + * Records Received [TransactionMetadata] for a given txnId. + * + * @param txId The SecureHash of a transaction. + * @param sender The sender of the transaction. + * @param metadata The recovery metadata associated with a transaction. + */ + fun addReceiverTransactionRecoveryMetadata(txId: SecureHash, + sender: CordaX500Name, + metadata: TransactionMetadata) + + /** + * Removes an un-notarised transaction (with a status of *MISSING_TRANSACTION_SIG*) from the data store. + * Returns null if no transaction with the ID exists. + */ + fun removeUnnotarisedTransaction(id: SecureHash): Boolean + + /** + * Add a finalised transaction to the store with flow recovery metadata. + * + * @param transaction The transaction to be recorded. + * @return true if the transaction was recorded as a *new* transaction, false if the transaction already exists. + */ + fun finalizeTransaction(transaction: SignedTransaction): Boolean + + /** + * Update a previously un-notarised transaction including associated notary signatures. + * @param transaction The notarised transaction to be finalized. + * @param signatures The notary signatures. + * @return true if the transaction is recorded as a *finalized* transaction, false if the transaction already exists. + */ + fun finalizeTransactionWithExtraSignatures(transaction: SignedTransaction, signatures: Collection) : Boolean + /** * Add a new *unverified* transaction to the store. */ fun addUnverifiedTransaction(transaction: SignedTransaction) - /** - * Return the transaction with the given ID from the store, and a flag of whether it's verified. Returns null if no transaction with the - * ID exists. - */ - fun getTransactionInternal(id: SecureHash): Pair? - /** * Returns a future that completes with the transaction corresponding to [id] once it has been committed. Do not warn when run inside * a DB transaction. diff --git a/node/src/main/kotlin/net/corda/node/services/api/VaultServiceInternal.kt b/node/src/main/kotlin/net/corda/node/services/api/VaultServiceInternal.kt index e95ba4b015..3c54a3321d 100644 --- a/node/src/main/kotlin/net/corda/node/services/api/VaultServiceInternal.kt +++ b/node/src/main/kotlin/net/corda/node/services/api/VaultServiceInternal.kt @@ -15,7 +15,8 @@ interface VaultServiceInternal : VaultService { * indicate whether an update consists entirely of regular or notary change transactions, which may require * different processing logic. */ - fun notifyAll(statesToRecord: StatesToRecord, txns: Iterable, previouslySeenTxns: Iterable = emptyList()) + fun notifyAll(statesToRecord: StatesToRecord, txns: Iterable, previouslySeenTxns: Iterable = emptyList(), + disableSoftLocking: Boolean = false) /** * Same as notifyAll but with a single transaction. diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 51564a773c..ec2a43694b 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -141,8 +141,7 @@ data class DevModeOptions( "Use [NodeConfiguration.disableReloadCheckpointAfterSuspend] instead." ) val disableCheckpointChecker: Boolean = Defaults.disableCheckpointChecker, - val allowCompatibilityZone: Boolean = Defaults.allowCompatibilityZone, - val djvm: DJVMOptions? = null + val allowCompatibilityZone: Boolean = Defaults.allowCompatibilityZone ) { internal object Defaults { val disableCheckpointChecker = false @@ -150,11 +149,6 @@ data class DevModeOptions( } } -data class DJVMOptions( - val bootstrapSource: String?, - val cordaSource: List -) - fun NodeConfiguration.shouldStartSSHDaemon() = this.sshd != null fun NodeConfiguration.shouldStartLocalShell() = !this.noLocalShell && System.console() != null && this.devMode fun NodeConfiguration.shouldInitCrashShell() = shouldStartLocalShell() || shouldStartSSHDaemon() diff --git a/node/src/main/kotlin/net/corda/node/services/config/schema/v1/ConfigSections.kt b/node/src/main/kotlin/net/corda/node/services/config/schema/v1/ConfigSections.kt index d2af82d190..0a8165be4f 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/schema/v1/ConfigSections.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/schema/v1/ConfigSections.kt @@ -17,7 +17,6 @@ import net.corda.core.internal.notary.NotaryServiceFlow import net.corda.node.services.config.AuthDataSourceType import net.corda.node.services.config.CertChainPolicyConfig import net.corda.node.services.config.CertChainPolicyType -import net.corda.node.services.config.DJVMOptions import net.corda.node.services.config.DevModeOptions import net.corda.node.services.config.FlowOverride import net.corda.node.services.config.FlowOverrideConfig @@ -159,21 +158,10 @@ internal object SecurityConfigurationSpec : Configuration.Specification("DevModeOptions") { private val disableCheckpointChecker by boolean().optional().withDefaultValue(DevModeOptions.Defaults.disableCheckpointChecker) private val allowCompatibilityZone by boolean().optional().withDefaultValue(DevModeOptions.Defaults.allowCompatibilityZone) - private val djvm by nested(DJVMOptionsSpec).optional() - - private object DJVMOptionsSpec : Configuration.Specification("DJVMOptions") { - private val bootstrapSource by string().optional() - private val cordaSource by string().list() - - override fun parseValid(configuration: Config, options: Configuration.Options): Valid { - val config = configuration.withOptions(options) - return valid(DJVMOptions(config[bootstrapSource], config[cordaSource])) - } - } override fun parseValid(configuration: Config, options: Configuration.Options): Valid { val config = configuration.withOptions(options) - return valid(DevModeOptions(config[disableCheckpointChecker], config[allowCompatibilityZone], config[djvm])) + return valid(DevModeOptions(config[disableCheckpointChecker], config[allowCompatibilityZone])) } } 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 09909b34c7..a36fdef315 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 @@ -47,7 +47,9 @@ import java.security.cert.CertificateNotYetValidException import java.security.cert.CollectionCertStoreParameters import java.security.cert.TrustAnchor import java.security.cert.X509Certificate -import java.util.* +import java.util.HashSet +import java.util.Optional +import java.util.UUID import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.stream.Stream @@ -136,7 +138,7 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri ) }, toPersistentEntity = { key: String, value: PublicKey -> - PersistentHashToPublicKey(key, value.encoded) + PersistentHashToPublicKey(key, Crypto.encodePublicKey(value)) }, persistentEntityClass = PersistentHashToPublicKey::class.java) } diff --git a/node/src/main/kotlin/net/corda/node/services/keys/BasicHSMKeyManagementService.kt b/node/src/main/kotlin/net/corda/node/services/keys/BasicHSMKeyManagementService.kt index 1655a517a3..cfadb10d4e 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/BasicHSMKeyManagementService.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/BasicHSMKeyManagementService.kt @@ -1,6 +1,14 @@ package net.corda.node.services.keys -import net.corda.core.crypto.* +import net.corda.core.crypto.Crypto +import net.corda.core.crypto.DigitalSignature +import net.corda.core.crypto.SignableData +import net.corda.core.crypto.SignatureScheme +import net.corda.core.crypto.TransactionSignature +import net.corda.core.crypto.generateKeyPair +import net.corda.core.crypto.keys +import net.corda.core.crypto.sign +import net.corda.core.crypto.toStringShort import net.corda.core.internal.NamedCacheFactory import net.corda.core.internal.telemetry.TelemetryServiceImpl import net.corda.core.serialization.SingletonSerializeAsToken @@ -16,9 +24,12 @@ import org.bouncycastle.operator.ContentSigner import java.security.KeyPair import java.security.PrivateKey import java.security.PublicKey -import java.util.* -import javax.persistence.* -import kotlin.collections.LinkedHashSet +import java.util.UUID +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.Id +import javax.persistence.Lob +import javax.persistence.Table /** * A persistent implementation of [KeyManagementServiceInternal] to support CryptoService for initial keys and @@ -52,7 +63,7 @@ class BasicHSMKeyManagementService( var privateKey: ByteArray = EMPTY_BYTE_ARRAY ) { constructor(publicKey: PublicKey, privateKey: PrivateKey) - : this(publicKey.toStringShort(), publicKey.encoded, privateKey.encoded) + : this(publicKey.toStringShort(), Crypto.encodePublicKey(publicKey), privateKey.encoded) } private companion object { diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentPartyInfoCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentPartyInfoCache.kt new file mode 100644 index 0000000000..6cee01c45a --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentPartyInfoCache.kt @@ -0,0 +1,80 @@ +package net.corda.node.services.network + +import net.corda.core.crypto.SecureHash +import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.NamedCacheFactory +import net.corda.core.node.services.NetworkMapCache +import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery +import net.corda.node.utilities.NonInvalidatingCache +import net.corda.nodeapi.internal.persistence.CordaPersistence +import org.hibernate.Session +import rx.Observable + +class PersistentPartyInfoCache(private val networkMapCache: PersistentNetworkMapCache, + cacheFactory: NamedCacheFactory, + private val database: CordaPersistence) { + + // probably better off using a BiMap here: https://www.baeldung.com/guava-bimap + private val cordaX500NameToPartyIdCache = NonInvalidatingCache( + cacheFactory = cacheFactory, + name = "RecoveryPartyInfoCache_byCordaX500Name") { key -> + database.transaction { queryByCordaX500Name(session, key) } + } + + private val partyIdToCordaX500NameCache = NonInvalidatingCache( + cacheFactory = cacheFactory, + name = "RecoveryPartyInfoCache_byPartyId") { key -> + database.transaction { queryByPartyId(session, key) } + } + + private lateinit var trackNetworkMapUpdates: Observable + + fun start() { + val (snapshot, updates) = networkMapCache.track() + snapshot.map { entry -> + entry.legalIdentities.map { party -> + add(SecureHash.sha256(party.name.toString()), party.name) + } + } + trackNetworkMapUpdates = updates + trackNetworkMapUpdates.cache().forEach { nodeInfo -> + nodeInfo.node.legalIdentities.map { party -> + add(SecureHash.sha256(party.name.toString()), party.name) + } + } + } + + fun getPartyIdByCordaX500Name(name: CordaX500Name): SecureHash = cordaX500NameToPartyIdCache[name] ?: throw IllegalStateException("Missing cache entry for $name") + + fun getCordaX500NameByPartyId(partyId: SecureHash): CordaX500Name = partyIdToCordaX500NameCache[partyId] ?: throw IllegalStateException("Missing cache entry for $partyId") + + private fun add(partyHashCode: SecureHash, partyName: CordaX500Name) { + partyIdToCordaX500NameCache.cache.put(partyHashCode, partyName) + cordaX500NameToPartyIdCache.cache.put(partyName, partyHashCode) + updateInfoDB(partyHashCode, partyName) + } + + private fun updateInfoDB(partyHashCode: SecureHash, partyName: CordaX500Name) { + database.transaction { + if (queryByPartyId(session, partyHashCode) == null) { + session.save(DBTransactionStorageLedgerRecovery.DBRecoveryPartyInfo(partyHashCode.toString(), partyName.toString())) + } + } + } + + private fun queryByCordaX500Name(session: Session, key: CordaX500Name): SecureHash? { + val query = session.createQuery( + "FROM ${DBTransactionStorageLedgerRecovery.DBRecoveryPartyInfo::class.java.name} WHERE partyName = :partyName", + DBTransactionStorageLedgerRecovery.DBRecoveryPartyInfo::class.java) + query.setParameter("partyName", key.toString()) + return query.resultList.singleOrNull()?.let { SecureHash.parse(it.partyId) } + } + + private fun queryByPartyId(session: Session, key: SecureHash): CordaX500Name? { + val query = session.createQuery( + "FROM ${DBTransactionStorageLedgerRecovery.DBRecoveryPartyInfo::class.java.name} WHERE partyId = :partyId", + DBTransactionStorageLedgerRecovery.DBRecoveryPartyInfo::class.java) + query.setParameter("partyId", key.toString()) + return query.resultList.singleOrNull()?.partyName?.let { CordaX500Name.parse(it) } + } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/AesDbEncryptionService.kt b/node/src/main/kotlin/net/corda/node/services/persistence/AesDbEncryptionService.kt new file mode 100644 index 0000000000..0b4c14638b --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/persistence/AesDbEncryptionService.kt @@ -0,0 +1,158 @@ +package net.corda.node.services.persistence + +import net.corda.core.crypto.newSecureRandom +import net.corda.core.identity.Party +import net.corda.core.internal.copyBytes +import net.corda.core.serialization.SingletonSerializeAsToken +import net.corda.node.services.EncryptionService +import net.corda.nodeapi.internal.crypto.AesEncryption +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX +import org.hibernate.annotations.Type +import java.nio.ByteBuffer +import java.security.Key +import java.security.MessageDigest +import java.util.UUID +import javax.crypto.Cipher +import javax.crypto.SecretKey +import javax.crypto.spec.SecretKeySpec +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.Id +import javax.persistence.Table + +/** + * [EncryptionService] which uses AES keys stored in the node database. A random key is chosen for encryption, and the resultant ciphertext + * encodes the key used so that it can be decrypted without needing further information. + * + * **Storing encryption keys in a database is not secure, and so only use this service if the data being encrypted is also stored + * unencrypted in the same database.** + * + * To obfuscate the keys, they are stored wrapped using another AES key (called the wrapping key or key-encryption-key) derived from the + * node's legal identity. This is not a security measure; it's only meant to reduce the impact of accidental leakage. + */ +// TODO Add support for key expiry +class AesDbEncryptionService(private val database: CordaPersistence) : EncryptionService, SingletonSerializeAsToken() { + companion object { + private const val INITIAL_KEY_COUNT = 10 + private const val UUID_BYTES = 16 + private const val VERSION_TAG = 1 + } + + private val aesKeys = ArrayList>() + + fun start(ourIdentity: Party) { + database.transaction { + val criteria = session.criteriaBuilder.createQuery(EncryptionKeyRecord::class.java) + criteria.select(criteria.from(EncryptionKeyRecord::class.java)) + val dbKeyRecords = session.createQuery(criteria).resultList + val keyWrapper = Cipher.getInstance("AESWrap") + if (dbKeyRecords.isEmpty()) { + repeat(INITIAL_KEY_COUNT) { + val keyId = UUID.randomUUID() + val aesKey = AesEncryption.randomKey() + aesKeys += Pair(keyId, aesKey) + val wrappedKey = with(keyWrapper) { + init(Cipher.WRAP_MODE, createKEK(ourIdentity, keyId)) + wrap(aesKey) + } + session.save(EncryptionKeyRecord(keyId = keyId, keyMaterial = wrappedKey)) + } + } else { + for (dbKeyRecord in dbKeyRecords) { + val aesKey = with(keyWrapper) { + init(Cipher.UNWRAP_MODE, createKEK(ourIdentity, dbKeyRecord.keyId)) + unwrap(dbKeyRecord.keyMaterial, "AES", Cipher.SECRET_KEY) as SecretKey + } + aesKeys += Pair(dbKeyRecord.keyId, aesKey) + } + } + } + } + + override fun encrypt(plaintext: ByteArray, additionalData: ByteArray?): ByteArray { + val (keyId, aesKey) = aesKeys[newSecureRandom().nextInt(aesKeys.size)] + val ciphertext = AesEncryption.encrypt(aesKey, plaintext, additionalData) + val buffer = ByteBuffer.allocate(1 + UUID_BYTES + Integer.BYTES + (additionalData?.size ?: 0) + ciphertext.size) + buffer.put(VERSION_TAG.toByte()) + // Prepend the key ID to the returned ciphertext. It's OK that this is not included in the authenticated additional data because + // changing this value will lead to either an non-existent key or an another key which will not be able decrypt the ciphertext. + buffer.putUUID(keyId) + if (additionalData != null) { + buffer.putInt(additionalData.size) + buffer.put(additionalData) + } else { + buffer.putInt(0) + } + buffer.put(ciphertext) + return buffer.array() + } + + override fun decrypt(ciphertext: ByteArray): EncryptionService.PlaintextAndAAD { + val buffer = wrap(ciphertext) + val keyId = buffer.getUUID() + val aesKey = requireNotNull(aesKeys.find { it.first == keyId }?.second) { "Unable to decrypt" } + val additionalData = buffer.getAdditionaData() + val plaintext = AesEncryption.decrypt(aesKey, buffer.copyBytes(), additionalData) + // Only now is the additional data authenticated + return EncryptionService.PlaintextAndAAD(plaintext, additionalData) + } + + override fun extractUnauthenticatedAdditionalData(ciphertext: ByteArray): ByteArray? { + val buffer = wrap(ciphertext) + buffer.position(buffer.position() + UUID_BYTES) + return buffer.getAdditionaData() + } + + private fun wrap(ciphertext: ByteArray): ByteBuffer { + val buffer = ByteBuffer.wrap(ciphertext) + val version = buffer.get().toInt() + require(version == VERSION_TAG) { "Unknown version $version" } + return buffer + } + + private fun ByteBuffer.getAdditionaData(): ByteArray? { + val additionalDataSize = getInt() + return if (additionalDataSize > 0) ByteArray(additionalDataSize).also { get(it) } else null + } + + private fun UUID.toByteArray(): ByteArray { + val buffer = ByteBuffer.allocate(UUID_BYTES) + buffer.putUUID(this) + return buffer.array() + } + + /** + * Derive the key-encryption-key (KEK) from the the node's identity and the persisted key's ID. + */ + private fun createKEK(ourIdentity: Party, keyId: UUID): Key { + val digest = MessageDigest.getInstance("SHA-256") + digest.update(ourIdentity.name.x500Principal.encoded) + digest.update(keyId.toByteArray()) + return SecretKeySpec(digest.digest(), 0, AesEncryption.KEY_SIZE_BYTES, "AES") + } + + + @Entity + @Table(name = "${NODE_DATABASE_PREFIX}aes_encryption_keys") + class EncryptionKeyRecord( + @Id + @Type(type = "uuid-char") + @Column(name = "key_id", nullable = false) + val keyId: UUID, + + @Column(name = "key_material", nullable = false) + val keyMaterial: ByteArray + ) +} + +internal fun ByteBuffer.putUUID(uuid: UUID) { + putLong(uuid.mostSignificantBits) + putLong(uuid.leastSignificantBits) +} + +internal fun ByteBuffer.getUUID(): UUID { + val mostSigBits = getLong() + val leastSigBits = getLong() + return UUID(mostSigBits, leastSigBits) +} diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt index 24046f2941..42078c9764 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt @@ -3,17 +3,26 @@ package net.corda.node.services.persistence import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.SecureHash import net.corda.core.crypto.TransactionSignature +import net.corda.core.flows.TransactionMetadata +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.NamedCacheFactory import net.corda.core.internal.ThreadBox import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.concurrent.doneFuture import net.corda.core.messaging.DataFeed -import net.corda.core.serialization.* +import net.corda.core.node.services.SignedTransactionWithStatus +import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializationDefaults +import net.corda.core.serialization.SerializedBytes +import net.corda.core.serialization.SingletonSerializeAsToken +import net.corda.core.serialization.deserialize import net.corda.core.serialization.internal.effectiveSerializationEnv +import net.corda.core.serialization.serialize import net.corda.core.toFuture import net.corda.core.transactions.CoreTransaction import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug import net.corda.node.CordaClock @@ -21,22 +30,36 @@ import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.utilities.AppendOnlyPersistentMapBase import net.corda.node.utilities.WeightBasedAppendOnlyPersistentMap -import net.corda.nodeapi.internal.persistence.* +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX +import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit +import net.corda.nodeapi.internal.persistence.contextTransactionOrNull +import net.corda.nodeapi.internal.persistence.currentDBSession +import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction import net.corda.serialization.internal.CordaSerializationEncoding.SNAPPY +import org.hibernate.annotations.Type import rx.Observable import rx.subjects.PublishSubject import java.time.Instant -import java.util.* -import javax.persistence.* +import java.util.Collections +import javax.persistence.AttributeConverter +import javax.persistence.Column +import javax.persistence.Convert +import javax.persistence.Converter +import javax.persistence.Entity +import javax.persistence.Id +import javax.persistence.Lob +import javax.persistence.Table import kotlin.streams.toList -class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: NamedCacheFactory, - private val clock: CordaClock) : WritableTransactionStorage, SingletonSerializeAsToken() { +@Suppress("TooManyFunctions") +open class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: NamedCacheFactory, + private val clock: CordaClock) : WritableTransactionStorage, SingletonSerializeAsToken() { @Suppress("MagicNumber") // database column width @Entity @Table(name = "${NODE_DATABASE_PREFIX}transactions") - class DBTransaction( + data class DBTransaction( @Id @Column(name = "tx_id", length = 144, nullable = false) val txId: String, @@ -53,17 +76,23 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: val status: TransactionStatus, @Column(name = "timestamp", nullable = false) - val timestamp: Instant - ) + val timestamp: Instant, + + @Column(name = "signatures") + @Type(type = "corda-blob") + val signatures: ByteArray? + ) enum class TransactionStatus { UNVERIFIED, - VERIFIED; + VERIFIED, + IN_FLIGHT; fun toDatabaseValue(): String { return when (this) { UNVERIFIED -> "U" VERIFIED -> "V" + IN_FLIGHT -> "F" } } @@ -71,11 +100,20 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: return this == VERIFIED } + fun toTransactionStatus(): net.corda.core.node.services.TransactionStatus { + return when(this) { + UNVERIFIED -> net.corda.core.node.services.TransactionStatus.UNVERIFIED + VERIFIED -> net.corda.core.node.services.TransactionStatus.VERIFIED + IN_FLIGHT -> net.corda.core.node.services.TransactionStatus.IN_FLIGHT + } + } + companion object { fun fromDatabaseValue(databaseValue: String): TransactionStatus { return when (databaseValue) { "V" -> VERIFIED "U" -> UNVERIFIED + "F" -> IN_FLIGHT else -> throw UnexpectedStatusValueException(databaseValue) } } @@ -107,7 +145,7 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: private val logger = contextLogger() - private fun contextToUse(): SerializationContext { + fun contextToUse(): SerializationContext { return if (effectiveSerializationEnv.serializationFactory.currentContext?.useCase == SerializationContext.UseCase.Storage) { effectiveSerializationEnv.serializationFactory.currentContext!! } else { @@ -117,14 +155,16 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: private fun createTransactionsMap(cacheFactory: NamedCacheFactory, clock: CordaClock) : AppendOnlyPersistentMapBase { - return WeightBasedAppendOnlyPersistentMap( + return WeightBasedAppendOnlyPersistentMap( cacheFactory = cacheFactory, name = "DBTransactionStorage_transactions", toPersistentEntityKey = SecureHash::toString, - fromPersistentEntity = { - SecureHash.create(it.txId) to TxCacheValue( - it.transaction.deserialize(context = contextToUse()), - it.status) + fromPersistentEntity = { dbTxn -> + SecureHash.create(dbTxn.txId) to TxCacheValue( + dbTxn.transaction.deserialize(context = contextToUse()), + dbTxn.status, + dbTxn.signatures?.deserialize(context = contextToUse()) + ) }, toPersistentEntity = { key: SecureHash, value: TxCacheValue -> DBTransaction( @@ -132,7 +172,8 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id?.uuid?.toString(), transaction = value.toSignedTx().serialize(context = contextToUse().withEncoding(SNAPPY)).bytes, status = value.status, - timestamp = clock.instant() + timestamp = clock.instant(), + signatures = value.sigs.serialize(context = contextToUse().withEncoding(SNAPPY)).bytes ) }, persistentEntityClass = DBTransaction::class.java, @@ -158,30 +199,113 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: criteriaUpdate.set(updateRoot.get(DBTransaction::status.name), TransactionStatus.VERIFIED) criteriaUpdate.where(criteriaBuilder.and( criteriaBuilder.equal(updateRoot.get(DBTransaction::txId.name), txId.toString()), - criteriaBuilder.equal(updateRoot.get(DBTransaction::status.name), TransactionStatus.UNVERIFIED) - )) + criteriaBuilder.and(updateRoot.get(DBTransaction::status.name).`in`(setOf(TransactionStatus.UNVERIFIED, TransactionStatus.IN_FLIGHT)) + ))) criteriaUpdate.set(updateRoot.get(DBTransaction::timestamp.name), clock.instant()) val update = session.createQuery(criteriaUpdate) val rowsUpdated = update.executeUpdate() return rowsUpdated != 0 } - override fun addTransaction(transaction: SignedTransaction): Boolean { + override fun addTransaction(transaction: SignedTransaction) = + addTransaction(transaction) { + updateTransaction(transaction.id) + } + + override fun addUnnotarisedTransaction(transaction: SignedTransaction) = + addTransaction(transaction, TransactionStatus.IN_FLIGHT) { + false + } + + override fun addSenderTransactionRecoveryMetadata(txId: SecureHash, metadata: TransactionMetadata): ByteArray? { return null } + + override fun addReceiverTransactionRecoveryMetadata(txId: SecureHash, + sender: CordaX500Name, + metadata: TransactionMetadata + ) { } + + override fun finalizeTransaction(transaction: SignedTransaction) = + addTransaction(transaction) { + false + } + + override fun removeUnnotarisedTransaction(id: SecureHash): Boolean { + return database.transaction { + val criteriaBuilder = session.criteriaBuilder + val delete = criteriaBuilder.createCriteriaDelete(DBTransaction::class.java) + val root = delete.from(DBTransaction::class.java) + delete.where(criteriaBuilder.and( + criteriaBuilder.equal(root.get(DBTransaction::txId.name), id.toString()), + criteriaBuilder.equal(root.get(DBTransaction::status.name), TransactionStatus.IN_FLIGHT) + )) + if (session.createQuery(delete).executeUpdate() != 0) { + txStorage.locked { + txStorage.content.clear(id) + txStorage.content[id] + logger.debug { "Un-notarised transaction $id has been removed." } + } + true + } else false + } + } + + override fun finalizeTransactionWithExtraSignatures(transaction: SignedTransaction, signatures: Collection) = + addTransaction(transaction + signatures) { + finalizeTransactionWithExtraSignatures(transaction.id, signatures) + } + + protected fun addTransaction(transaction: SignedTransaction, + status: TransactionStatus = TransactionStatus.VERIFIED, + updateFn: (SecureHash) -> Boolean): Boolean { return database.transaction { txStorage.locked { - val cachedValue = TxCacheValue(transaction, TransactionStatus.VERIFIED) - val addedOrUpdated = addOrUpdate(transaction.id, cachedValue) { k, _ -> updateTransaction(k) } + val cachedValue = TxCacheValue(transaction, status) + val addedOrUpdated = addOrUpdate(transaction.id, cachedValue) { k, _ -> updateFn(k) } if (addedOrUpdated) { - logger.debug { "Transaction ${transaction.id} has been recorded as verified" } - onNewTx(transaction) + logger.debug { "Transaction ${transaction.id} has been recorded as $status" } + if (status.isVerified()) + onNewTx(transaction) + true } else { - logger.debug { "Transaction ${transaction.id} is already recorded as verified, so no need to re-record" } + logger.debug { "Transaction ${transaction.id} is already recorded as $status, so no need to re-record" } false } } } } + private fun finalizeTransactionWithExtraSignatures(txId: SecureHash, signatures: Collection): Boolean { + return txStorage.locked { + val session = currentDBSession() + val criteriaBuilder = session.criteriaBuilder + val criteriaUpdate = criteriaBuilder.createCriteriaUpdate(DBTransaction::class.java) + val updateRoot = criteriaUpdate.from(DBTransaction::class.java) + criteriaUpdate.set(updateRoot.get(DBTransaction::signatures.name), signatures.serialize(context = contextToUse().withEncoding(SNAPPY)).bytes) + criteriaUpdate.set(updateRoot.get(DBTransaction::status.name), TransactionStatus.VERIFIED) + criteriaUpdate.where(criteriaBuilder.and( + criteriaBuilder.equal(updateRoot.get(DBTransaction::txId.name), txId.toString()), + criteriaBuilder.equal(updateRoot.get(DBTransaction::status.name), TransactionStatus.IN_FLIGHT) + )) + criteriaUpdate.set(updateRoot.get(DBTransaction::timestamp.name), clock.instant()) + val update = session.createQuery(criteriaUpdate) + val rowsUpdated = update.executeUpdate() + if (rowsUpdated == 0) { + val criteriaUpdateUnverified = criteriaBuilder.createCriteriaUpdate(DBTransaction::class.java) + val updateRootUnverified = criteriaUpdateUnverified.from(DBTransaction::class.java) + criteriaUpdateUnverified.set(updateRootUnverified.get(DBTransaction::signatures.name), signatures.serialize(context = contextToUse().withEncoding(SNAPPY)).bytes) + criteriaUpdateUnverified.set(updateRootUnverified.get(DBTransaction::status.name), TransactionStatus.VERIFIED) + criteriaUpdateUnverified.where(criteriaBuilder.and( + criteriaBuilder.equal(updateRootUnverified.get(DBTransaction::txId.name), txId.toString()), + criteriaBuilder.equal(updateRootUnverified.get(DBTransaction::status.name), TransactionStatus.UNVERIFIED) + )) + criteriaUpdateUnverified.set(updateRootUnverified.get(DBTransaction::timestamp.name), clock.instant()) + val updateUnverified = session.createQuery(criteriaUpdateUnverified) + val rowsUpdatedUnverified = updateUnverified.executeUpdate() + rowsUpdatedUnverified != 0 + } else true + } + } + private fun onNewTx(transaction: SignedTransaction): Boolean { updatesPublisher.bufferUntilDatabaseCommit().onNext(transaction) return true @@ -193,11 +317,23 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: } } + override fun getTransactionWithStatus(id: SecureHash): SignedTransactionWithStatus? = + database.transaction { + txStorage.content[id]?.let { SignedTransactionWithStatus(it.toSignedTx(), it.status.toTransactionStatus()) } + } + override fun addUnverifiedTransaction(transaction: SignedTransaction) { + if (transaction.coreTransaction is WireTransaction) + transaction.verifyRequiredSignatures() database.transaction { txStorage.locked { val cacheValue = TxCacheValue(transaction, status = TransactionStatus.UNVERIFIED) - val added = addWithDuplicatesAllowed(transaction.id, cacheValue) + val added = addWithDuplicatesAllowed(transaction.id, cacheValue) { k, v, existingEntry -> + if (existingEntry.status == TransactionStatus.IN_FLIGHT) { + session.merge(toPersistentEntity(k, v)) + true + } else false + } if (added) { logger.debug { "Transaction ${transaction.id} recorded as unverified." } } else { @@ -207,12 +343,6 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: } } - override fun getTransactionInternal(id: SecureHash): Pair? { - return database.transaction { - txStorage.content[id]?.let { it.toSignedTx() to it.status.isVerified() } - } - } - private val updatesPublisher = PublishSubject.create().toSerialized() override val updates: Observable = updatesPublisher.wrapWithDatabaseTransaction() @@ -269,7 +399,7 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: } // Cache value type to just store the immutable bits of a signed transaction plus conversion helpers - private data class TxCacheValue( + internal class TxCacheValue( val txBits: SerializedBytes, val sigs: List, val status: TransactionStatus @@ -277,8 +407,14 @@ class DBTransactionStorage(private val database: CordaPersistence, cacheFactory: constructor(stx: SignedTransaction, status: TransactionStatus) : this( stx.txBits, Collections.unmodifiableList(stx.sigs), - status) + status + ) + constructor(stx: SignedTransaction, status: TransactionStatus, sigs: List?) : this( + stx.txBits, + if (sigs == null) Collections.unmodifiableList(stx.sigs) else Collections.unmodifiableList(stx.sigs + sigs).distinct(), + status + ) fun toSignedTx() = SignedTransaction(txBits, sigs) } } diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorageLedgerRecovery.kt b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorageLedgerRecovery.kt new file mode 100644 index 0000000000..a8e72accee --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorageLedgerRecovery.kt @@ -0,0 +1,216 @@ +package net.corda.node.services.persistence + +import net.corda.core.crypto.SecureHash +import net.corda.core.flows.DistributionList.ReceiverDistributionList +import net.corda.core.flows.DistributionList.SenderDistributionList +import net.corda.core.flows.DistributionRecordKey +import net.corda.core.flows.ReceiverDistributionRecord +import net.corda.core.flows.SenderDistributionRecord +import net.corda.core.flows.TransactionMetadata +import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.NamedCacheFactory +import net.corda.core.node.StatesToRecord +import net.corda.core.utilities.OpaqueBytes +import net.corda.node.CordaClock +import net.corda.node.services.EncryptionService +import net.corda.node.services.network.PersistentPartyInfoCache +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX +import org.hibernate.annotations.Immutable +import java.io.Serializable +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.concurrent.atomic.AtomicInteger +import javax.persistence.Column +import javax.persistence.Embeddable +import javax.persistence.EmbeddedId +import javax.persistence.Entity +import javax.persistence.Id +import javax.persistence.Lob +import javax.persistence.Table + +class DBTransactionStorageLedgerRecovery(private val database: CordaPersistence, + cacheFactory: NamedCacheFactory, + val clock: CordaClock, + private val encryptionService: EncryptionService, + private val partyInfoCache: PersistentPartyInfoCache) : DBTransactionStorage(database, cacheFactory, clock) { + @Embeddable + @Immutable + data class PersistentKey( + @Column(name = "transaction_id", length = 144, nullable = false) + var txId: String, + + @Column(name = "peer_party_id", length = 144, nullable = false) + var peerPartyId: String, + + @Column(name = "timestamp", nullable = false) + var timestamp: Instant, + + @Column(name = "timestamp_discriminator", nullable = false) + var timestampDiscriminator: Int + + ) : Serializable { + constructor(key: Key) : this(key.txId.toString(), key.partyId.toString(), key.timestamp, key.timestampDiscriminator) + } + + @Entity + @Table(name = "${NODE_DATABASE_PREFIX}sender_distr_recs") + data class DBSenderDistributionRecord( + @EmbeddedId + var compositeKey: PersistentKey, + + /** states to record: NONE, ALL_VISIBLE, ONLY_RELEVANT */ + @Column(name = "sender_states_to_record", nullable = false) + var senderStatesToRecord: StatesToRecord, + + /** states to record: NONE, ALL_VISIBLE, ONLY_RELEVANT */ + @Column(name = "receiver_states_to_record", nullable = false) + var receiverStatesToRecord: StatesToRecord + ) { + fun key() = DistributionRecordKey( + SecureHash.parse(this.compositeKey.txId), + this.compositeKey.timestamp, + this.compositeKey.timestampDiscriminator) + + fun toSenderDistributionRecord() = + SenderDistributionRecord( + SecureHash.parse(this.compositeKey.txId), + SecureHash.parse(this.compositeKey.peerPartyId), + this.compositeKey.timestamp, + this.compositeKey.timestampDiscriminator, + this.senderStatesToRecord, + this.receiverStatesToRecord + ) + } + + @Entity + @Table(name = "${NODE_DATABASE_PREFIX}receiver_distr_recs") + data class DBReceiverDistributionRecord( + @EmbeddedId + var compositeKey: PersistentKey, + + /** Encrypted recovery information for sole use by Sender **/ + @Lob + @Column(name = "distribution_list", nullable = false) + val distributionList: ByteArray, + + /** states to record: NONE, ALL_VISIBLE, ONLY_RELEVANT */ + @Column(name = "receiver_states_to_record", nullable = false) + val receiverStatesToRecord: StatesToRecord +) { + constructor(key: Key, encryptedDistributionList: ByteArray, receiverStatesToRecord: StatesToRecord) : + this(PersistentKey(key), + distributionList = encryptedDistributionList, + receiverStatesToRecord = receiverStatesToRecord + ) + + fun key() = DistributionRecordKey( + SecureHash.parse(this.compositeKey.txId), + this.compositeKey.timestamp, + this.compositeKey.timestampDiscriminator) + + fun toReceiverDistributionRecord(): ReceiverDistributionRecord { + return ReceiverDistributionRecord( + SecureHash.parse(this.compositeKey.txId), + SecureHash.parse(this.compositeKey.peerPartyId), + this.compositeKey.timestamp, + this.compositeKey.timestampDiscriminator, + OpaqueBytes(this.distributionList), + this.receiverStatesToRecord + ) + } + } + + @Entity + @Table(name = "${NODE_DATABASE_PREFIX}recovery_party_info") + data class DBRecoveryPartyInfo( + @Id + /** CordaX500Name hashCode() **/ + @Column(name = "party_id", length = 144, nullable = false) + var partyId: String, + + /** CordaX500Name of party **/ + @Column(name = "party_name", nullable = false) + val partyName: String + ) + + class Key( + val txId: SecureHash, + val partyId: SecureHash, + val timestamp: Instant, + val timestampDiscriminator: Int = nextDiscriminatorNumber.andIncrement + ) { + companion object { + val nextDiscriminatorNumber = AtomicInteger() + } + } + + override fun addSenderTransactionRecoveryMetadata(txId: SecureHash, metadata: TransactionMetadata): ByteArray { + return database.transaction { + val senderRecordingTimestamp = clock.instant().truncatedTo(ChronoUnit.SECONDS) + val timeDiscriminator = Key.nextDiscriminatorNumber.andIncrement + val distributionList = metadata.distributionList as? SenderDistributionList ?: throw IllegalStateException("Expecting SenderDistributionList") + distributionList.peersToStatesToRecord.map { (peerCordaX500Name, peerStatesToRecord) -> + val senderDistributionRecord = DBSenderDistributionRecord( + PersistentKey(Key(txId, + partyInfoCache.getPartyIdByCordaX500Name(peerCordaX500Name), + senderRecordingTimestamp, timeDiscriminator)), + distributionList.senderStatesToRecord, + peerStatesToRecord) + session.save(senderDistributionRecord) + } + val hashedPeersToStatesToRecord = distributionList.peersToStatesToRecord.mapKeys { (peer) -> + partyInfoCache.getPartyIdByCordaX500Name(peer) + } + val hashedDistributionList = HashedDistributionList( + distributionList.senderStatesToRecord, + hashedPeersToStatesToRecord, + HashedDistributionList.PublicHeader(senderRecordingTimestamp, timeDiscriminator) + ) + hashedDistributionList.encrypt(encryptionService) + } + } + + override fun addReceiverTransactionRecoveryMetadata(txId: SecureHash, + sender: CordaX500Name, + metadata: TransactionMetadata) { + when (metadata.distributionList) { + is ReceiverDistributionList -> { + val distributionList = metadata.distributionList as ReceiverDistributionList + val publicHeader = HashedDistributionList.PublicHeader.unauthenticatedDeserialise(distributionList.opaqueData, encryptionService) + database.transaction { + val receiverDistributionRecord = DBReceiverDistributionRecord( + Key(txId, partyInfoCache.getPartyIdByCordaX500Name(sender), publicHeader.senderRecordedTimestamp, publicHeader.timeDiscriminator), + distributionList.opaqueData, + distributionList.receiverStatesToRecord + ) + session.saveOrUpdate(receiverDistributionRecord) + } + } + else -> throw IllegalStateException("Expecting ReceiverDistributionList") + } + } + + override fun removeUnnotarisedTransaction(id: SecureHash): Boolean { + return database.transaction { + super.removeUnnotarisedTransaction(id) + val criteriaBuilder = session.criteriaBuilder + val deleteSenderDistributionRecords = criteriaBuilder.createCriteriaDelete(DBSenderDistributionRecord::class.java) + val rootSender = deleteSenderDistributionRecords.from(DBSenderDistributionRecord::class.java) + val compositeKeySender = rootSender.get("compositeKey") + deleteSenderDistributionRecords.where(criteriaBuilder.equal(compositeKeySender.get(PersistentKey::txId.name), id.toString())) + val deletedSenderDistributionRecords = session.createQuery(deleteSenderDistributionRecords).executeUpdate() != 0 + val deleteReceiverDistributionRecords = criteriaBuilder.createCriteriaDelete(DBReceiverDistributionRecord::class.java) + val rootReceiver = deleteReceiverDistributionRecords.from(DBReceiverDistributionRecord::class.java) + val compositeKeyReceiver = rootReceiver.get("compositeKey") + deleteReceiverDistributionRecords.where(criteriaBuilder.equal(compositeKeyReceiver.get(PersistentKey::txId.name), id.toString())) + val deletedReceiverDistributionRecords = session.createQuery(deleteReceiverDistributionRecords).executeUpdate() != 0 + deletedSenderDistributionRecords || deletedReceiverDistributionRecords + } + } + + fun decryptHashedDistributionList(encryptedBytes: ByteArray): HashedDistributionList { + return HashedDistributionList.decrypt(encryptedBytes, encryptionService) + } +} + diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/HashedDistributionList.kt b/node/src/main/kotlin/net/corda/node/services/persistence/HashedDistributionList.kt new file mode 100644 index 0000000000..5fee0f24b2 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/persistence/HashedDistributionList.kt @@ -0,0 +1,111 @@ +package net.corda.node.services.persistence + +import net.corda.core.crypto.SecureHash +import net.corda.core.node.StatesToRecord +import net.corda.core.serialization.CordaSerializable +import net.corda.node.services.EncryptionService +import java.io.ByteArrayOutputStream +import java.io.DataInputStream +import java.io.DataOutputStream +import java.nio.ByteBuffer +import java.time.Instant + +@Suppress("TooGenericExceptionCaught") +@CordaSerializable +data class HashedDistributionList( + val senderStatesToRecord: StatesToRecord, + val peerHashToStatesToRecord: Map, + val publicHeader: PublicHeader +) { + /** + * Encrypt this hashed distribution list using the given [EncryptionService]. The [publicHeader] is not encrypted but is instead + * authenticated so that it is tamperproof. + * + * The same [EncryptionService] instance needs to be used with [decrypt] for decryption. + */ + fun encrypt(encryptionService: EncryptionService): ByteArray { + val baos = ByteArrayOutputStream() + val out = DataOutputStream(baos) + out.writeByte(senderStatesToRecord.ordinal) + out.writeInt(peerHashToStatesToRecord.size) + for (entry in peerHashToStatesToRecord) { + entry.key.writeTo(out) + out.writeByte(entry.value.ordinal) + } + return encryptionService.encrypt(baos.toByteArray(), publicHeader.serialise()) + } + + + @CordaSerializable + data class PublicHeader( + val senderRecordedTimestamp: Instant, + val timeDiscriminator: Int + ) { + fun serialise(): ByteArray { + val buffer = ByteBuffer.allocate(1 + java.lang.Long.BYTES + Integer.BYTES) + buffer.put(VERSION_TAG.toByte()) + buffer.putLong(senderRecordedTimestamp.toEpochMilli()) + buffer.putInt(timeDiscriminator) + return buffer.array() + } + + companion object { + /** + * Deserialise a [PublicHeader] from the given [encryptedBytes]. The bytes is expected is to be a valid encrypted blob that can + * be decrypted by [HashedDistributionList.decrypt] using the same [EncryptionService]. + * + * Because this method does not actually decrypt the bytes, the header returned is not authenticated and any modifications to it + * will not be detected. That can only be done by the encrypting party with [HashedDistributionList.decrypt]. + */ + fun unauthenticatedDeserialise(encryptedBytes: ByteArray, encryptionService: EncryptionService): PublicHeader { + val additionalData = encryptionService.extractUnauthenticatedAdditionalData(encryptedBytes) + requireNotNull(additionalData) { "Missing additional data field" } + return deserialise(additionalData!!) + } + + fun deserialise(bytes: ByteArray): PublicHeader { + val buffer = ByteBuffer.wrap(bytes) + try { + val version = buffer.get().toInt() + require(version == VERSION_TAG) { "Unknown distribution list format $version" } + val senderRecordedTimestamp = Instant.ofEpochMilli(buffer.getLong()) + val timeDiscriminator = buffer.getInt() + return PublicHeader(senderRecordedTimestamp, timeDiscriminator) + } catch (e: Exception) { + throw IllegalArgumentException("Corrupt or not a distribution list header", e) + } + } + } + } + + companion object { + // The version tag is serialised in the header, even though it is separate from the encrypted main body of the distribution list. + // This is because the header and the dist list are cryptographically coupled and we want to avoid declaring the version field twice. + private const val VERSION_TAG = 1 + private const val SECURE_HASH_LENGTH = 32 + private val statesToRecordValues = StatesToRecord.values() // Cache the enum values since .values() returns a new array each time. + + /** + * Decrypt a [HashedDistributionList] from the given [encryptedBytes] using the same [EncryptionService] that was used in [encrypt]. + */ + fun decrypt(encryptedBytes: ByteArray, encryptionService: EncryptionService): HashedDistributionList { + val (plaintext, authenticatedAdditionalData) = encryptionService.decrypt(encryptedBytes) + requireNotNull(authenticatedAdditionalData) { "Missing authenticated header" } + val publicHeader = PublicHeader.deserialise(authenticatedAdditionalData!!) + val input = DataInputStream(plaintext.inputStream()) + try { + val senderStatesToRecord = statesToRecordValues[input.readByte().toInt()] + val numPeerHashToStatesToRecords = input.readInt() + val peerHashToStatesToRecord = mutableMapOf() + repeat(numPeerHashToStatesToRecords) { + val secureHashBytes = ByteArray(SECURE_HASH_LENGTH) + input.readFully(secureHashBytes) + peerHashToStatesToRecord[SecureHash.createSHA256(secureHashBytes)] = statesToRecordValues[input.readByte().toInt()] + } + return HashedDistributionList(senderStatesToRecord, peerHashToStatesToRecord, publicHeader) + } catch (e: Exception) { + throw IllegalArgumentException("Corrupt or not a distribution list", e) + } + } + } +} diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/PublicKeyToTextConverter.kt b/node/src/main/kotlin/net/corda/node/services/persistence/PublicKeyToTextConverter.kt index 4ee5a15e57..a8e8ef132b 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/PublicKeyToTextConverter.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/PublicKeyToTextConverter.kt @@ -13,6 +13,6 @@ import javax.persistence.Converter */ @Converter(autoApply = true) class PublicKeyToTextConverter : AttributeConverter { - override fun convertToDatabaseColumn(key: PublicKey?): String? = key?.encoded?.toHex() + override fun convertToDatabaseColumn(key: PublicKey?): String? = key?.let { Crypto.encodePublicKey(key).toHex() } override fun convertToEntityAttribute(text: String?): PublicKey? = text?.let { Crypto.decodePublicKey(it.hexToByteArray()) } } \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt index f62c40ee1c..68dc445e29 100644 --- a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt +++ b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt @@ -16,6 +16,8 @@ import net.corda.node.services.keys.BasicHSMKeyManagementService import net.corda.node.services.messaging.P2PMessageDeduplicator import net.corda.node.services.network.PersistentNetworkMapCache import net.corda.node.services.persistence.DBCheckpointStorage +import net.corda.node.services.persistence.AesDbEncryptionService +import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.persistence.PublicKeyHashToExternalId @@ -29,7 +31,7 @@ import net.corda.node.services.vault.VaultSchemaV1 * TODO: support plugins for schema version upgrading or custom mapping not supported by original [QueryableState]. * TODO: create whitelisted tables when a CorDapp is first installed */ -class NodeSchemaService(private val extraSchemas: Set = emptySet()) : SchemaService, SingletonSerializeAsToken() { +class NodeSchemaService(extraSchemas: Set = emptySet()) : SchemaService, SingletonSerializeAsToken() { // Core Entities used by a Node object NodeCore @@ -51,7 +53,11 @@ class NodeSchemaService(private val extraSchemas: Set = emptySet() ContractUpgradeServiceImpl.DBContractUpgrade::class.java, DBNetworkParametersStorage.PersistentNetworkParameters::class.java, PublicKeyHashToExternalId::class.java, - PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java + PersistentNetworkMapCache.PersistentPartyToPublicKeyHash::class.java, + DBTransactionStorageLedgerRecovery.DBSenderDistributionRecord::class.java, + DBTransactionStorageLedgerRecovery.DBReceiverDistributionRecord::class.java, + DBTransactionStorageLedgerRecovery.DBRecoveryPartyInfo::class.java, + AesDbEncryptionService.EncryptionKeyRecord::class.java )) { override val migrationResource = "node-core.changelog-master" } diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt index 2d314e9c3b..e853cd66a5 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt @@ -469,6 +469,7 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, } object FinalityDoctor : Staff { + @Suppress("ComplexMethod") override fun consult(flowFiber: FlowFiber, currentState: StateMachineState, newError: Throwable, history: FlowMedicalHistory): Diagnosis { return if (currentState.flowLogic is FinalityHandler) { log.warn("Flow ${flowFiber.id} failed to be finalised. Manual intervention may be required before retrying " + @@ -480,10 +481,18 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, // no need to keep around the flow, since notarisation has already failed at the counterparty. Diagnosis.NOT_MY_SPECIALTY } + isErrorPropagatedFromCounterparty(newError) && isErrorThrownDuringReceiveFinalityFlow(newError) -> { + // no need to keep around the flow, since notarisation has already failed at the counterparty. + Diagnosis.NOT_MY_SPECIALTY + } isEndSessionErrorThrownDuringReceiveTransactionFlow(newError) -> { // Typically occurs if the initiating flow catches a notary exception and ends their flow successfully. Diagnosis.NOT_MY_SPECIALTY } + isEndSessionErrorThrownDuringReceiveFinalityFlow(newError) -> { + // Typically occurs if the initiating flow catches a notary exception and ends their flow successfully. + Diagnosis.NOT_MY_SPECIALTY + } else -> { log.warn( "Flow ${flowFiber.id} failed to be finalised. Manual intervention may be required before retrying " + @@ -530,6 +539,19 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, && strippedStacktrace.first().className.startsWith(ReceiveTransactionFlow::class.qualifiedName!!) } + /** + * This method will return true if [ReceiveFinalityFlow] is at the top of the stack during the error. + * This may happen in the post-notarisation logic of Two Phase Finality upon receiving a notarisation exception + * from the peer running [FinalityFlow]. + */ + private fun isErrorThrownDuringReceiveFinalityFlow(error: Throwable): Boolean { + val strippedStacktrace = error.stackTrace + .filterNot { it?.className?.contains("counter-flow exception from peer") ?: false } + .filterNot { it?.className?.startsWith("net.corda.node.services.statemachine.") ?: false } + return strippedStacktrace.isNotEmpty() + && strippedStacktrace.first().className.startsWith(ReceiveFinalityFlow::class.qualifiedName!!) + } + /** * Checks if an end session error exception was thrown and that it did so within [ReceiveTransactionFlow]. * @@ -542,6 +564,15 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, && error.message?.contains(StartedFlowTransition.UNEXPECTED_SESSION_END_MESSAGE) == true && isErrorThrownDuringReceiveTransactionFlow(error) } + + /** + * Checks if an end session error exception was thrown and that it did so within [ReceiveFinalityFlow]. + */ + private fun isEndSessionErrorThrownDuringReceiveFinalityFlow(error: Throwable): Boolean { + return error is UnexpectedFlowEndException + && error.message?.contains(StartedFlowTransition.UNEXPECTED_SESSION_END_MESSAGE) == true + && isErrorThrownDuringReceiveFinalityFlow(error) + } } /** diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/DeterministicVerifierFactoryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/DeterministicVerifierFactoryService.kt deleted file mode 100644 index 5b016d5734..0000000000 --- a/node/src/main/kotlin/net/corda/node/services/transactions/DeterministicVerifierFactoryService.kt +++ /dev/null @@ -1,117 +0,0 @@ -package net.corda.node.services.transactions - -import net.corda.core.internal.Verifier -import net.corda.core.serialization.ConstructorForDeserialization -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.CordaSerializationTransformEnumDefault -import net.corda.core.serialization.CordaSerializationTransformEnumDefaults -import net.corda.core.serialization.CordaSerializationTransformRename -import net.corda.core.serialization.CordaSerializationTransformRenames -import net.corda.core.serialization.DeprecatedConstructorForDeserialization -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.SingletonSerializeAsToken -import net.corda.core.transactions.LedgerTransaction -import net.corda.djvm.SandboxConfiguration -import net.corda.djvm.analysis.AnalysisConfiguration -import net.corda.djvm.execution.ExecutionProfile -import net.corda.djvm.rewiring.ByteCode -import net.corda.djvm.rewiring.ByteCodeKey -import net.corda.djvm.source.ApiSource -import net.corda.djvm.source.UserPathSource -import net.corda.djvm.source.UserSource -import net.corda.node.internal.djvm.DeterministicVerifier -import java.net.URL -import java.net.URLClassLoader -import java.util.concurrent.ConcurrentHashMap -import java.util.function.Consumer -import java.util.function.UnaryOperator - -interface VerifierFactoryService : UnaryOperator, AutoCloseable - -class DeterministicVerifierFactoryService( - private val bootstrapSource: ApiSource, - private val cordaSource: UserSource -) : SingletonSerializeAsToken(), VerifierFactoryService { - private val baseSandboxConfiguration: SandboxConfiguration - private val cordappByteCodeCache = ConcurrentHashMap() - - init { - val baseAnalysisConfiguration = AnalysisConfiguration.createRoot( - userSource = cordaSource, - visibleAnnotations = setOf( - CordaSerializable::class.java, - CordaSerializationTransformEnumDefault::class.java, - CordaSerializationTransformEnumDefaults::class.java, - CordaSerializationTransformRename::class.java, - CordaSerializationTransformRenames::class.java, - ConstructorForDeserialization::class.java, - DeprecatedConstructorForDeserialization::class.java - ), - bootstrapSource = bootstrapSource, - overrideClasses = setOf( - /** - * These classes are all duplicated into the sandbox - * without the DJVM modifying their byte-code first. - * The goal is to delegate cryptographic operations - * out to the Node rather than perform them inside - * the sandbox, because this is MUCH FASTER. - */ - sandbox.net.corda.core.crypto.Crypto::class.java.name, - "sandbox.net.corda.core.crypto.DJVM", - "sandbox.net.corda.core.crypto.DJVMPublicKey", - "sandbox.net.corda.core.crypto.internal.ProviderMapKt" - ) - ) - - baseSandboxConfiguration = SandboxConfiguration.createFor( - analysisConfiguration = baseAnalysisConfiguration, - profile = NODE_PROFILE - ) - } - - /** - * Generate sandbox classes for every Corda jar with META-INF/DJVM-preload. - */ - fun generateSandbox(): DeterministicVerifierFactoryService { - baseSandboxConfiguration.preload() - return this - } - - override fun apply(ledgerTransaction: LedgerTransaction): LedgerTransaction { - // Specialise the LedgerTransaction here so that - // contracts are verified inside the DJVM! - return ledgerTransaction.specialise(::createDeterministicVerifier) - } - - private fun createDeterministicVerifier(ltx: LedgerTransaction, serializationContext: SerializationContext): Verifier { - return (serializationContext.deserializationClassLoader as? URLClassLoader)?.let { classLoader -> - DeterministicVerifier(ltx, classLoader, createSandbox(classLoader.urLs)) - } ?: throw IllegalStateException("Unsupported deserialization classloader type") - } - - private fun createSandbox(userSource: Array): SandboxConfiguration { - return baseSandboxConfiguration.createChild(UserPathSource(userSource), Consumer { - it.setExternalCache(cordappByteCodeCache) - }) - } - - override fun close() { - bootstrapSource.use { - cordaSource.close() - } - } - - private companion object { - private val NODE_PROFILE = ExecutionProfile( - allocationCostThreshold = 1024 * 1024 * 1024, - invocationCostThreshold = 100_000_000, - jumpCostThreshold = 500_000_000, - throwCostThreshold = 1_000_000 - ) - } -} - -class BasicVerifierFactoryService : VerifierFactoryService { - override fun apply(ledgerTransaction: LedgerTransaction)= ledgerTransaction - override fun close() {} -} diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index 2f5fbd43a6..e0fdce29c0 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -21,6 +21,7 @@ import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.tee import net.corda.core.internal.uncheckedCast +import net.corda.core.internal.warnOnce import net.corda.core.messaging.DataFeed import net.corda.core.node.StatesToRecord import net.corda.core.node.services.KeyManagementService @@ -68,7 +69,8 @@ import java.security.PublicKey import java.sql.SQLException import java.time.Clock import java.time.Instant -import java.util.* +import java.util.Arrays +import java.util.UUID import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArraySet import java.util.stream.Stream @@ -79,8 +81,6 @@ import javax.persistence.criteria.CriteriaQuery import javax.persistence.criteria.CriteriaUpdate import javax.persistence.criteria.Predicate import javax.persistence.criteria.Root -import kotlin.collections.ArrayList -import kotlin.collections.LinkedHashSet import kotlin.collections.component1 import kotlin.collections.component2 @@ -93,6 +93,7 @@ import kotlin.collections.component2 * TODO: keep an audit trail with time stamps of previously unconsumed states "as of" a particular point in time. * TODO: have transaction storage do some caching. */ +@Suppress("LargeClass") class NodeVaultService( private val clock: Clock, private val keyManagementService: KeyManagementService, @@ -106,6 +107,8 @@ class NodeVaultService( const val DEFAULT_SOFT_LOCKING_SQL_IN_CLAUSE_SIZE = 16 + private val IGNORE_TRANSACTION_DESERIALIZATION_ERRORS = java.lang.Boolean.getBoolean("net.corda.vaultupdate.ignore.transaction.deserialization.errors") + /** * Establish whether a given state is relevant to a node, given the node's public keys. * @@ -234,6 +237,7 @@ class NodeVaultService( if (stateStatus != Vault.StateStatus.CONSUMED) { stateStatus = Vault.StateStatus.CONSUMED consumedTime = clock.instant() + consumingTxId = update.consumingTxIds[stateRef]?.toString() // remove lock (if held) if (lockId != null) { lockId = null @@ -279,12 +283,13 @@ class NodeVaultService( internal val publishUpdates get() = mutex.locked { updatesPublisher } /** Groups adjacent transactions into batches to generate separate net updates per transaction type. */ - override fun notifyAll(statesToRecord: StatesToRecord, txns: Iterable, previouslySeenTxns: Iterable) { + override fun notifyAll(statesToRecord: StatesToRecord, txns: Iterable, previouslySeenTxns: Iterable, + disableSoftLocking: Boolean) { if (statesToRecord == StatesToRecord.NONE || (!txns.any() && !previouslySeenTxns.any())) return val batch = mutableListOf() fun flushBatch(previouslySeen: Boolean) { - val updates = makeUpdates(batch, statesToRecord, previouslySeen) + val updates = makeUpdates(batch, statesToRecord, previouslySeen, disableSoftLocking) processAndNotify(updates) batch.clear() } @@ -303,20 +308,34 @@ class NodeVaultService( processTransactions(txns, false) } - private fun makeUpdates(batch: Iterable, statesToRecord: StatesToRecord, previouslySeen: Boolean): List> { + @Suppress("ComplexMethod", "ThrowsCount") + private fun makeUpdates(batch: Iterable, statesToRecord: StatesToRecord, previouslySeen: Boolean, disableSoftLocking: Boolean): List> { - fun withValidDeserialization(list: List, txId: SecureHash): Map = (0 until list.size).mapNotNull { idx -> - try { - idx to list[idx] - } catch (e: TransactionDeserialisationException) { - // When resolving transaction dependencies we might encounter contracts we haven't installed locally. - // This will cause a failure as we can't deserialize such states in the context of the `appClassloader`. - // For now we ignore these states. - // In the future we will use the AttachmentsClassloader to correctly deserialize and asses the relevancy. - log.warn("Could not deserialize state $idx from transaction $txId. Cause: $e") - null - } - }.toMap() + fun withValidDeserialization(list: List, txId: SecureHash): Map { + var error: TransactionDeserialisationException? = null + val map = (0 until list.size).mapNotNull { idx -> + try { + idx to list[idx] + } catch (e: TransactionDeserialisationException) { + // When resolving transaction dependencies we might encounter contracts we haven't installed locally. + // This will cause a failure as we can't deserialize such states in the context of the `appClassloader`. + // For now we ignore these states. + // In the future we will use the AttachmentsClassloader to correctly deserialize and asses the relevancy. + // Disabled if soft locking disabled, as assumes you are in the back chain and that maybe it is less important than top + // level transaction. + if (IGNORE_TRANSACTION_DESERIALIZATION_ERRORS || disableSoftLocking) { + log.warnOnce("The current usage of transaction deserialization for the vault is unsafe." + + "Ignoring vault updates due to failed deserialized states may lead to severe problems with ledger consistency. ") + log.warn("Could not deserialize state $idx from transaction $txId. Cause: $e") + } else { + log.error("Could not deserialize state $idx from transaction $txId. Cause: $e") + if (error == null) error = e + } + null + } + }.toMap() + return error?.let { throw it } ?: map + } // Returns only output states that can be deserialised successfully. fun WireTransaction.deserializableOutputStates(): Map> = withValidDeserialization(this.outputs, this.id) @@ -373,8 +392,8 @@ class NodeVaultService( } } } - - return Vault.Update(consumedStates.toSet(), ourNewStates.toSet(), references = newReferenceStateAndRefs.toSet()) + val consumedTxIds = consumedStates.associate { Pair(it.ref, tx.id) } + return Vault.Update(consumedStates.toSet(), ourNewStates.toSet(), references = newReferenceStateAndRefs.toSet(), consumingTxIds = consumedTxIds) } fun resolveAndMakeUpdate(tx: CoreTransaction): Vault.Update? { @@ -408,7 +427,8 @@ class NodeVaultService( } else { Vault.UpdateType.NOTARY_CHANGE } - return Vault.Update(consumedStateAndRefs.toSet(), producedStateAndRefs.toSet(), null, updateType, referenceStateAndRefs.toSet()) + val consumedTxIds = consumedStateAndRefs.associate { Pair(it.ref, tx.id) } + return Vault.Update(consumedStateAndRefs.toSet(), producedStateAndRefs.toSet(), null, updateType, referenceStateAndRefs.toSet(), consumingTxIds = consumedTxIds) } @@ -713,11 +733,20 @@ class NodeVaultService( val query = getSession().createQuery(criteriaQuery) query.setResultWindow(paging) + var previousPageAnchor: StateRef? = null val statesMetadata: MutableList = mutableListOf() val otherResults: MutableList = mutableListOf() query.resultStream(paging).use { results -> - results.forEach { result -> + val resultsIterator = results.iterator() + + // From page 2 and onwards, the first result is the previous page anchor + if (paging.pageNumber > DEFAULT_PAGE_NUM && resultsIterator.hasNext()) { + val previousVaultState = resultsIterator.next()[0] as VaultSchemaV1.VaultStates + previousPageAnchor = previousVaultState.stateRef!!.toStateRef() + } + + for (result in resultsIterator) { val result0 = result[0] if (result0 is VaultSchemaV1.VaultStates) { statesMetadata.add(result0.toStateMetadata()) @@ -740,7 +769,7 @@ class NodeVaultService( else -> queryTotalStateCount(criteria, contractStateType) } - return Vault.Page(states, statesMetadata, totalStatesAvailable, criteriaParser.stateTypes, otherResults) + return Vault.Page(states, statesMetadata, totalStatesAvailable, criteriaParser.stateTypes, otherResults, previousPageAnchor) } private fun Query.resultStream(paging: PageSpecification): Stream { @@ -758,14 +787,18 @@ class NodeVaultService( } private fun Query<*>.setResultWindow(paging: PageSpecification) { + // For both SQLServer and PostgresSQL, firstResult must be >= 0. + firstResult = 0 if (paging.isDefault) { - // For both SQLServer and PostgresSQL, firstResult must be >= 0. - firstResult = 0 // Peek ahead and see if there are more results in case pagination should be done maxResults = paging.pageSize + 1 - } else { - firstResult = (paging.pageNumber - 1) * paging.pageSize + } else if (paging.pageNumber == DEFAULT_PAGE_NUM) { maxResults = paging.pageSize + } else { + // In addition to aligning the query to the correct result window for the page, also include the previous page's last + // result for the previousPageAnchor value. + firstResult = ((paging.pageNumber - 1) * paging.pageSize) - 1 + maxResults = paging.pageSize + 1 } } diff --git a/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt b/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt index 09c71fe1f7..ee59ee170f 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt @@ -91,7 +91,11 @@ object VaultSchemaV1 : MappedSchema( /** associated constraint type data (if any) */ @Column(name = "constraint_data", length = MAX_CONSTRAINT_DATA_SIZE, nullable = true) @Type(type = "corda-wrapper-binary") - var constraintData: ByteArray? = null + var constraintData: ByteArray? = null, + + /** consuming transaction */ + @Column(name = "consuming_tx_id", length = 144, nullable = true) + var consumingTxId: String? = null ) : PersistentState() @Entity diff --git a/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt b/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt index 570172fa06..98de78ac0c 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt @@ -2,6 +2,7 @@ package net.corda.node.utilities import com.github.benmanes.caffeine.cache.LoadingCache import com.github.benmanes.caffeine.cache.Weigher +import net.corda.core.crypto.SecureHash import net.corda.core.internal.NamedCacheFactory import net.corda.core.utilities.contextLogger import net.corda.nodeapi.internal.persistence.DatabaseTransaction @@ -142,18 +143,22 @@ abstract class AppendOnlyPersistentMapBase( * Associates the specified value with the specified key in this map and persists it. * If the map previously contained a committed mapping for the key, the old value is not replaced. It may throw an error from the * underlying storage if this races with another database transaction to store a value for the same key. + * An optional [forceUpdate] function allows performing additional checks/updates on an existingEntry to determine whether the map + * should be updated. * @return true if added key was unique, otherwise false */ - fun addWithDuplicatesAllowed(key: K, value: V, logWarning: Boolean = true): Boolean { + fun addWithDuplicatesAllowed(key: K, value: V, logWarning: Boolean = true, + forceUpdate: (K, V, E) -> Boolean = { _, _, _ -> false }): Boolean { return set(key, value, logWarning) { k, v -> val session = currentDBSession() val existingEntry = session.find(persistentEntityClass, toPersistentEntityKey(k)) if (existingEntry == null) { session.save(toPersistentEntity(k, v)) null - } else { - fromPersistentEntity(existingEntry).second } + else if (!forceUpdate(key, value, existingEntry)) { + fromPersistentEntity(existingEntry).second + } else null } } @@ -244,6 +249,8 @@ abstract class AppendOnlyPersistentMapBase( cache.invalidateAll() } + fun clear(id: SecureHash) = cache.invalidate(id) + // Helpers to know if transaction(s) are currently writing the given key. private fun weAreWriting(key: K): Boolean = pendingKeys[key]?.transactions?.contains(contextTransaction) ?: false diff --git a/node/src/main/kotlin/net/corda/node/utilities/NodeNamedCache.kt b/node/src/main/kotlin/net/corda/node/utilities/NodeNamedCache.kt index 9c2e2eaf93..fa06c1b25b 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/NodeNamedCache.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/NodeNamedCache.kt @@ -64,6 +64,10 @@ open class DefaultNamedCacheFactory protected constructor(private val metricRegi name == "PublicKeyToOwningIdentityCache_cache" -> caffeine.maximumSize(defaultCacheSize) name == "NodeAttachmentTrustCalculator_trustedKeysCache" -> caffeine.maximumSize(defaultCacheSize) name == "AttachmentsClassLoader_cache" -> caffeine.maximumSize(defaultAttachmentsClassLoaderCacheSize) + name == "RecoveryPartyInfoCache_byCordaX500Name" -> caffeine.maximumSize(defaultCacheSize) + name == "RecoveryPartyInfoCache_byPartyId" -> caffeine.maximumSize(defaultCacheSize) + name == "DBTransactionRecovery_senderDistributionRecords" -> caffeine.maximumSize(defaultCacheSize) + name == "DBTransactionRecovery_receiverDistributionRecords" -> caffeine.maximumSize(defaultCacheSize) else -> throw IllegalArgumentException("Unexpected cache name $name. Did you add a new cache?") } } diff --git a/node/src/main/kotlin/sandbox/net/corda/core/crypto/Crypto.kt b/node/src/main/kotlin/sandbox/net/corda/core/crypto/Crypto.kt deleted file mode 100644 index b51a586c93..0000000000 --- a/node/src/main/kotlin/sandbox/net/corda/core/crypto/Crypto.kt +++ /dev/null @@ -1,261 +0,0 @@ -package sandbox.net.corda.core.crypto - -import sandbox.net.corda.core.crypto.DJVM.fromDJVM -import sandbox.net.corda.core.crypto.DJVM.toDJVM -import sandbox.java.lang.String -import sandbox.java.lang.doCatch -import sandbox.java.math.BigInteger -import sandbox.java.security.KeyPair -import sandbox.java.security.PrivateKey -import sandbox.java.security.PublicKey -import sandbox.java.util.ArrayList -import sandbox.java.util.List -import sandbox.java.lang.Object -import sandbox.org.bouncycastle.asn1.x509.AlgorithmIdentifier -import sandbox.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo -import java.security.GeneralSecurityException -import java.security.SignatureException -import java.security.spec.InvalidKeySpecException - -/** - * This is a hand-written "drop-in" replacement for the version of - * [net.corda.core.crypto.Crypto] found inside core-deterministic. - * This class is used in the DJVM sandbox instead of transforming - * the byte-code for [net.corda.core.crypto.Crypto], with the goal - * of not needing to instantiate some EXPENSIVE elliptic curve - * cryptography classes inside every single sandbox. - * - * The downside is that this class MUST manually be kept consistent - * with the DJVM's byte-code rewriting rules. - */ -@Suppress("unused", "unused_parameter", "TooManyFunctions") -object Crypto : Object() { - @JvmField - val RSA_SHA256: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.RSA_SHA256) - - @JvmField - val ECDSA_SECP256K1_SHA256: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.ECDSA_SECP256K1_SHA256) - - @JvmField - val ECDSA_SECP256R1_SHA256: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.ECDSA_SECP256R1_SHA256) - - @JvmField - val EDDSA_ED25519_SHA512: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512) - - @JvmField - val SPHINCS256_SHA256: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.SPHINCS256_SHA256) - - @JvmField - val COMPOSITE_KEY: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.COMPOSITE_KEY) - - @JvmField - val DEFAULT_SIGNATURE_SCHEME = EDDSA_ED25519_SHA512 - - /** - * We can use the unsandboxed versions of [Map] and [List] here - * because the [underlyingSchemes] and [djvmSchemes] fields are - * private and not exposed to the rest of the sandbox. - */ - private val underlyingSchemes: Map - = net.corda.core.crypto.Crypto.supportedSignatureSchemes() - .associateBy(net.corda.core.crypto.SignatureScheme::schemeCodeName) - private val djvmSchemes: Map = listOf( - RSA_SHA256, - ECDSA_SECP256K1_SHA256, - ECDSA_SECP256R1_SHA256, - EDDSA_ED25519_SHA512, - SPHINCS256_SHA256, - COMPOSITE_KEY - ).associateByTo(LinkedHashMap(), SignatureScheme::schemeCodeName) - - private fun findUnderlyingSignatureScheme(signatureScheme: SignatureScheme): net.corda.core.crypto.SignatureScheme { - return net.corda.core.crypto.Crypto.findSignatureScheme(String.fromDJVM(signatureScheme.schemeCodeName)) - } - - private fun PublicKey.toUnderlyingKey(): java.security.PublicKey { - return (this as? DJVMPublicKey ?: throw sandbox.java.lang.fail("Unsupported key ${this::class.java.name}")).underlying - } - - @JvmStatic - fun supportedSignatureSchemes(): List { - val schemes = ArrayList(djvmSchemes.size) - for (scheme in djvmSchemes.values) { - schemes.add(scheme) - } - return schemes - } - - @JvmStatic - fun isSupportedSignatureScheme(signatureScheme: SignatureScheme): Boolean { - return String.fromDJVM(signatureScheme.schemeCodeName) in underlyingSchemes - } - - @JvmStatic - fun findSignatureScheme(schemeCodeName: String): SignatureScheme { - return djvmSchemes[schemeCodeName] - ?: throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $schemeCodeName") - } - - @JvmStatic - fun findSignatureScheme(schemeNumberID: Int): SignatureScheme { - val underlyingScheme = net.corda.core.crypto.Crypto.findSignatureScheme(schemeNumberID) - return findSignatureScheme(String.toDJVM(underlyingScheme.schemeCodeName)) - } - - @JvmStatic - fun findSignatureScheme(key: PublicKey): SignatureScheme { - val underlyingScheme = net.corda.core.crypto.Crypto.findSignatureScheme(key.toUnderlyingKey()) - return findSignatureScheme(String.toDJVM(underlyingScheme.schemeCodeName)) - } - - @JvmStatic - fun findSignatureScheme(algorithm: AlgorithmIdentifier): SignatureScheme { - val underlyingScheme = net.corda.core.crypto.Crypto.findSignatureScheme(fromDJVM(algorithm)) - return findSignatureScheme(String.toDJVM(underlyingScheme.schemeCodeName)) - } - - @JvmStatic - fun findSignatureScheme(key: PrivateKey): SignatureScheme { - throw sandbox.java.lang.failApi("Crypto.findSignatureScheme(PrivateKey)") - } - - @JvmStatic - fun decodePrivateKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PrivateKey { - throw sandbox.java.lang.failApi("Crypto.decodePrivateKey(SignatureScheme, byte[])") - } - - @JvmStatic - fun decodePublicKey(encodedKey: ByteArray): PublicKey { - val underlying = try { - net.corda.core.crypto.Crypto.decodePublicKey(encodedKey) - } catch (e: InvalidKeySpecException) { - throw sandbox.java.lang.fromDJVM(doCatch(e)) - } - return DJVMPublicKey(underlying) - } - - @JvmStatic - fun decodePublicKey(schemeCodeName: String, encodedKey: ByteArray): PublicKey { - val underlying = try { - net.corda.core.crypto.Crypto.decodePublicKey(String.fromDJVM(schemeCodeName), encodedKey) - } catch (e: InvalidKeySpecException) { - throw sandbox.java.lang.fromDJVM(doCatch(e)) - } - return DJVMPublicKey(underlying) - } - - @JvmStatic - fun decodePublicKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PublicKey { - return decodePublicKey(signatureScheme.schemeCodeName, encodedKey) - } - - @JvmStatic - fun deriveKeyPair(signatureScheme: SignatureScheme, privateKey: PrivateKey, seed: ByteArray): KeyPair { - throw sandbox.java.lang.failApi("Crypto.deriveKeyPair(SignatureScheme, PrivateKey, byte[])") - } - - @JvmStatic - fun deriveKeyPair(privateKey: PrivateKey, seed: ByteArray): KeyPair { - throw sandbox.java.lang.failApi("Crypto.deriveKeyPair(PrivateKey, byte[])") - } - - @JvmStatic - fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair { - throw sandbox.java.lang.failApi("Crypto.deriveKeyPairFromEntropy(SignatureScheme, BigInteger)") - } - - @JvmStatic - fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair { - throw sandbox.java.lang.failApi("Crypto.deriveKeyPairFromEntropy(BigInteger)") - } - - @JvmStatic - fun doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean { - val underlyingKey = publicKey.toUnderlyingKey() - return try { - net.corda.core.crypto.Crypto.doVerify(String.fromDJVM(schemeCodeName), underlyingKey, signatureData, clearData) - } catch (e: GeneralSecurityException) { - throw sandbox.java.lang.fromDJVM(doCatch(e)) - } - } - - @JvmStatic - fun doVerify(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean { - val underlyingKey = publicKey.toUnderlyingKey() - return try { - net.corda.core.crypto.Crypto.doVerify(underlyingKey, signatureData, clearData) - } catch (e: GeneralSecurityException) { - throw sandbox.java.lang.fromDJVM(doCatch(e)) - } - } - - @JvmStatic - fun doVerify(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean { - val underlyingScheme = findUnderlyingSignatureScheme(signatureScheme) - val underlyingKey = publicKey.toUnderlyingKey() - return try { - net.corda.core.crypto.Crypto.doVerify(underlyingScheme, underlyingKey, signatureData, clearData) - } catch (e: GeneralSecurityException) { - throw sandbox.java.lang.fromDJVM(doCatch(e)) - } - } - - @JvmStatic - fun doVerify(txId: SecureHash, transactionSignature: TransactionSignature): Boolean { - throw sandbox.java.lang.failApi("Crypto.doVerify(SecureHash, TransactionSignature)") - } - - @JvmStatic - fun isValid(txId: SecureHash, transactionSignature: TransactionSignature): Boolean { - throw sandbox.java.lang.failApi("Crypto.isValid(SecureHash, TransactionSignature)") - } - - @JvmStatic - fun isValid(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean { - val underlyingKey = publicKey.toUnderlyingKey() - return try { - net.corda.core.crypto.Crypto.isValid(underlyingKey, signatureData, clearData) - } catch (e: SignatureException) { - throw sandbox.java.lang.fromDJVM(doCatch(e)) - } - } - - @JvmStatic - fun isValid(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean { - val underlyingScheme = findUnderlyingSignatureScheme(signatureScheme) - val underlyingKey = publicKey.toUnderlyingKey() - return try { - net.corda.core.crypto.Crypto.isValid(underlyingScheme, underlyingKey, signatureData, clearData) - } catch (e: SignatureException) { - throw sandbox.java.lang.fromDJVM(doCatch(e)) - } - } - - @JvmStatic - fun publicKeyOnCurve(signatureScheme: SignatureScheme, publicKey: PublicKey): Boolean { - val underlyingScheme = findUnderlyingSignatureScheme(signatureScheme) - val underlyingKey = publicKey.toUnderlyingKey() - return net.corda.core.crypto.Crypto.publicKeyOnCurve(underlyingScheme, underlyingKey) - } - - @JvmStatic - fun validatePublicKey(key: PublicKey): Boolean { - return net.corda.core.crypto.Crypto.validatePublicKey(key.toUnderlyingKey()) - } - - @JvmStatic - fun toSupportedPublicKey(key: SubjectPublicKeyInfo): PublicKey { - return decodePublicKey(key.encoded) - } - - @JvmStatic - fun toSupportedPublicKey(key: PublicKey): PublicKey { - val underlyingKey = key.toUnderlyingKey() - val supportedKey = net.corda.core.crypto.Crypto.toSupportedPublicKey(underlyingKey) - return if (supportedKey === underlyingKey) { - key - } else { - DJVMPublicKey(supportedKey) - } - } -} diff --git a/node/src/main/kotlin/sandbox/net/corda/core/crypto/SecureHash.kt b/node/src/main/kotlin/sandbox/net/corda/core/crypto/SecureHash.kt deleted file mode 100644 index ac8f733511..0000000000 --- a/node/src/main/kotlin/sandbox/net/corda/core/crypto/SecureHash.kt +++ /dev/null @@ -1,8 +0,0 @@ -package sandbox.net.corda.core.crypto - -/** - * This is a dummy class that implements just enough of [net.corda.core.crypto.SecureHash] - * to allow us to compile [sandbox.net.corda.core.crypto.Crypto]. - */ -@Suppress("unused_parameter") -sealed class SecureHash(bytes: ByteArray) : sandbox.java.lang.Object() diff --git a/node/src/main/kotlin/sandbox/net/corda/core/crypto/SignatureScheme.kt b/node/src/main/kotlin/sandbox/net/corda/core/crypto/SignatureScheme.kt deleted file mode 100644 index dd315252df..0000000000 --- a/node/src/main/kotlin/sandbox/net/corda/core/crypto/SignatureScheme.kt +++ /dev/null @@ -1,26 +0,0 @@ -package sandbox.net.corda.core.crypto - -import sandbox.java.lang.String -import sandbox.java.lang.Integer -import sandbox.java.lang.Object -import sandbox.java.security.spec.AlgorithmParameterSpec -import sandbox.java.util.List -import sandbox.org.bouncycastle.asn1.x509.AlgorithmIdentifier - -/** - * This is a dummy class that implements just enough of [net.corda.core.crypto.SignatureScheme] - * to allow us to compile [sandbox.net.corda.core.crypto.Crypto]. - */ -@Suppress("unused") -class SignatureScheme( - val schemeNumberID: Int, - val schemeCodeName: String, - val signatureOID: AlgorithmIdentifier, - val alternativeOIDs: List, - val providerName: String, - val algorithmName: String, - val signatureName: String, - val algSpec: AlgorithmParameterSpec?, - val keySize: Integer?, - val desc: String -) : Object() diff --git a/node/src/main/kotlin/sandbox/net/corda/core/crypto/TransactionSignature.kt b/node/src/main/kotlin/sandbox/net/corda/core/crypto/TransactionSignature.kt deleted file mode 100644 index 2d3c3e8b90..0000000000 --- a/node/src/main/kotlin/sandbox/net/corda/core/crypto/TransactionSignature.kt +++ /dev/null @@ -1,7 +0,0 @@ -package sandbox.net.corda.core.crypto - -/** - * This is a dummy class that implements just enough of [net.corda.core.crypto.TransactionSignature] - * to allow us to compile [sandbox.net.corda.core.crypto.Crypto]. - */ -class TransactionSignature : sandbox.java.lang.Object() diff --git a/node/src/main/kotlin/sandbox/net/corda/core/crypto/internal/ProviderMap.kt b/node/src/main/kotlin/sandbox/net/corda/core/crypto/internal/ProviderMap.kt deleted file mode 100644 index 5a0ebb4065..0000000000 --- a/node/src/main/kotlin/sandbox/net/corda/core/crypto/internal/ProviderMap.kt +++ /dev/null @@ -1,8 +0,0 @@ -package sandbox.net.corda.core.crypto.internal - -/** - * THIS FILE IS DELIBERATELY EMPTY, APART FROM A SINGLE DUMMY VALUE. - * KOTLIN WILL NOT CREATE A CLASS IF THIS FILE IS COMPLETELY EMPTY. - */ -@Suppress("unused") -private const val DUMMY_VALUE = 0 diff --git a/node/src/main/resources/migration/node-core.changelog-master.xml b/node/src/main/resources/migration/node-core.changelog-master.xml index fe333ea9df..ef9116aade 100644 --- a/node/src/main/resources/migration/node-core.changelog-master.xml +++ b/node/src/main/resources/migration/node-core.changelog-master.xml @@ -28,7 +28,10 @@ - + + + + diff --git a/node/src/main/resources/migration/node-core.changelog-v23.xml b/node/src/main/resources/migration/node-core.changelog-v23.xml new file mode 100644 index 0000000000..235c65b31b --- /dev/null +++ b/node/src/main/resources/migration/node-core.changelog-v23.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/node/src/main/resources/migration/node-core.changelog-v24.xml b/node/src/main/resources/migration/node-core.changelog-v24.xml new file mode 100644 index 0000000000..041633ddcf --- /dev/null +++ b/node/src/main/resources/migration/node-core.changelog-v24.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/node/src/main/resources/migration/node-core.changelog-v25.xml b/node/src/main/resources/migration/node-core.changelog-v25.xml new file mode 100644 index 0000000000..28ae879bc1 --- /dev/null +++ b/node/src/main/resources/migration/node-core.changelog-v25.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/node/src/main/resources/migration/node-core.changelog-v26.xml b/node/src/main/resources/migration/node-core.changelog-v26.xml new file mode 100644 index 0000000000..f2bdef4833 --- /dev/null +++ b/node/src/main/resources/migration/node-core.changelog-v26.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/node/src/main/resources/migration/node-notary.changelog-committed-transactions-table.xml b/node/src/main/resources/migration/node-notary.changelog-committed-transactions-table.xml deleted file mode 100644 index e4fa2cc68e..0000000000 --- a/node/src/main/resources/migration/node-notary.changelog-committed-transactions-table.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - diff --git a/node/src/main/resources/migration/node-notary.changelog-master.xml b/node/src/main/resources/migration/node-notary.changelog-master.xml index 860fd7ddff..9c342362e1 100644 --- a/node/src/main/resources/migration/node-notary.changelog-master.xml +++ b/node/src/main/resources/migration/node-notary.changelog-master.xml @@ -8,7 +8,6 @@ - diff --git a/node/src/main/resources/migration/vault-schema.changelog-master.xml b/node/src/main/resources/migration/vault-schema.changelog-master.xml index 44684647fa..8fea107366 100644 --- a/node/src/main/resources/migration/vault-schema.changelog-master.xml +++ b/node/src/main/resources/migration/vault-schema.changelog-master.xml @@ -13,4 +13,5 @@ + diff --git a/node/src/main/resources/migration/vault-schema.changelog-v14.xml b/node/src/main/resources/migration/vault-schema.changelog-v14.xml new file mode 100644 index 0000000000..75f576f214 --- /dev/null +++ b/node/src/main/resources/migration/vault-schema.changelog-v14.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 28a5f3b973..2bdfc69e5f 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -2,9 +2,25 @@ package net.corda.node.messaging import co.paralleluniverse.fibers.Suspendable import net.corda.core.concurrent.CordaFuture -import net.corda.core.contracts.* -import net.corda.core.crypto.* -import net.corda.core.flows.* +import net.corda.core.contracts.Amount +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.InsufficientBalanceException +import net.corda.core.contracts.Issued +import net.corda.core.contracts.OwnableState +import net.corda.core.contracts.PartyAndReference +import net.corda.core.contracts.StateAndRef +import net.corda.core.contracts.TransactionVerificationException +import net.corda.core.crypto.Crypto +import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.SignableData +import net.corda.core.crypto.SignatureMetadata +import net.corda.core.crypto.TransactionSignature +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.StateMachineRunId +import net.corda.core.flows.TransactionMetadata import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name @@ -14,6 +30,7 @@ import net.corda.core.internal.concurrent.map import net.corda.core.internal.rootCause import net.corda.core.messaging.DataFeed import net.corda.core.messaging.StateMachineTransactionMapping +import net.corda.core.node.services.SignedTransactionWithStatus import net.corda.core.node.services.Vault import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SingletonSerializeAsToken @@ -27,25 +44,38 @@ import net.corda.core.utilities.toNonEmptySet import net.corda.core.utilities.unwrap import net.corda.coretesting.internal.TEST_TX_TIME import net.corda.finance.DOLLARS -import net.corda.finance.`issued by` import net.corda.finance.contracts.CommercialPaper import net.corda.finance.contracts.asset.CASH import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.TwoPartyTradeFlow.Buyer import net.corda.finance.flows.TwoPartyTradeFlow.Seller +import net.corda.finance.`issued by` import net.corda.node.services.api.CheckpointStorage import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.statemachine.Checkpoint import net.corda.nodeapi.internal.persistence.CordaPersistence -import net.corda.testing.core.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.BOC_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.TestIdentity +import net.corda.testing.core.expect +import net.corda.testing.core.expectEvents +import net.corda.testing.core.sequence +import net.corda.testing.core.singleIdentity import net.corda.testing.dsl.LedgerDSL import net.corda.testing.dsl.TestLedgerDSLInterpreter import net.corda.testing.dsl.TestTransactionDSLInterpreter import net.corda.testing.internal.IS_OPENJ9 import net.corda.testing.internal.LogHelper import net.corda.testing.internal.vault.VaultFiller -import net.corda.testing.node.internal.* +import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP +import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP +import net.corda.testing.node.internal.InternalMockNetwork +import net.corda.testing.node.internal.InternalMockNodeParameters +import net.corda.testing.node.internal.TestStartedNode +import net.corda.testing.node.internal.startFlow import net.corda.testing.node.ledger import org.assertj.core.api.Assertions.assertThat import org.junit.After @@ -56,7 +86,11 @@ import org.junit.runner.RunWith import org.junit.runners.Parameterized import rx.Observable import java.io.ByteArrayOutputStream -import java.util.* +import java.util.ArrayList +import java.util.Collections +import java.util.Currency +import java.util.Random +import java.util.UUID import java.util.jar.JarOutputStream import java.util.zip.ZipEntry import kotlin.streams.toList @@ -107,7 +141,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { // We run this in parallel threads to help catch any race conditions that may exist. The other tests // we run in the unit test thread exclusively to speed things up, ensure deterministic results and // allow interruption half way through. - mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP), threadPerNode = true) + mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, FINANCE_WORKFLOWS_CORDAPP), threadPerNode = true) val notaryNode = mockNet.defaultNotaryNode val notary = mockNet.defaultNotaryIdentity notaryNode.services.ledger(notary) { @@ -139,7 +173,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { // TODO: Verify that the result was inserted into the transaction database. // assertEquals(bobResult.get(), aliceNode.storage.validatedTransactions[aliceResult.get().id]) - assertEquals(aliceResult.getOrThrow(), bobStateMachine.getOrThrow().resultFuture.getOrThrow()) + assertEquals(aliceResult.getOrThrow().id, (bobStateMachine.getOrThrow().resultFuture.getOrThrow() as SignedTransaction).id) aliceNode.dispose() bobNode.dispose() @@ -214,7 +248,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { @Test(timeout=300_000) fun `shutdown and restore`() { Assume.assumeTrue(!IS_OPENJ9) - mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP)) + mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, FINANCE_WORKFLOWS_CORDAPP)) val notaryNode = mockNet.defaultNotaryNode val notary = mockNet.defaultNotaryIdentity notaryNode.services.ledger(notary) { @@ -285,7 +319,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { mockNet.runNetwork() // Bob is now finished and has the same transaction as Alice. - assertThat(bobFuture.getOrThrow()).isEqualTo(aliceFuture.getOrThrow()) + assertThat((bobFuture.getOrThrow() as SignedTransaction).id).isEqualTo((aliceFuture.getOrThrow().id)) assertThat(bobNode.smm.findStateMachines(Buyer::class.java)).isEmpty() bobNode.database.transaction { @@ -768,6 +802,48 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { return true } + override fun addUnnotarisedTransaction(transaction: SignedTransaction): Boolean { + database.transaction { + records.add(TxRecord.Add(transaction)) + delegate.addUnnotarisedTransaction(transaction) + } + return true + } + + override fun addSenderTransactionRecoveryMetadata(txId: SecureHash, metadata: TransactionMetadata): ByteArray? { + return database.transaction { + delegate.addSenderTransactionRecoveryMetadata(txId, metadata) + } + } + + override fun addReceiverTransactionRecoveryMetadata(txId: SecureHash, + sender: CordaX500Name, + metadata: TransactionMetadata) { + database.transaction { + delegate.addReceiverTransactionRecoveryMetadata(txId, sender, metadata) + } + } + + override fun removeUnnotarisedTransaction(id: SecureHash): Boolean { + return database.transaction { + delegate.removeUnnotarisedTransaction(id) + } + } + + override fun finalizeTransaction(transaction: SignedTransaction): Boolean { + database.transaction { + delegate.finalizeTransaction(transaction) + } + return true + } + + override fun finalizeTransactionWithExtraSignatures(transaction: SignedTransaction, signatures: Collection) : Boolean { + database.transaction { + delegate.finalizeTransactionWithExtraSignatures(transaction, signatures) + } + return true + } + override fun addUnverifiedTransaction(transaction: SignedTransaction) { database.transaction { delegate.addUnverifiedTransaction(transaction) @@ -781,9 +857,10 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { } } - override fun getTransactionInternal(id: SecureHash): Pair? { + override fun getTransactionWithStatus(id: SecureHash): SignedTransactionWithStatus? { return database.transaction { - delegate.getTransactionInternal(id) + records.add(TxRecord.Get(id)) + delegate.getTransactionWithStatus(id) } } } diff --git a/node/src/test/kotlin/net/corda/node/migration/IdenityServiceKeyRotationMigrationTest.kt b/node/src/test/kotlin/net/corda/node/migration/IdenityServiceKeyRotationMigrationTest.kt index 327959f466..68a1347db7 100644 --- a/node/src/test/kotlin/net/corda/node/migration/IdenityServiceKeyRotationMigrationTest.kt +++ b/node/src/test/kotlin/net/corda/node/migration/IdenityServiceKeyRotationMigrationTest.kt @@ -8,6 +8,7 @@ import liquibase.database.Database import liquibase.database.core.H2Database import liquibase.database.jvm.JdbcConnection import liquibase.resource.ClassLoaderResourceAccessor +import liquibase.resource.Resource import net.corda.core.crypto.Crypto import net.corda.core.crypto.toStringShort import net.corda.core.identity.CordaX500Name @@ -84,7 +85,9 @@ class IdenityServiceKeyRotationMigrationTest { persist(charlie2.party.dbParty()) Liquibase("migration/node-core.changelog-v20.xml", object : ClassLoaderResourceAccessor() { - override fun getResourcesAsStream(path: String) = super.getResourcesAsStream(path)?.firstOrNull()?.let { setOf(it) } + override fun getAll(path: String?): List { + return if(path != null) super.getAll(path).take(1).toList() else emptyList() + } }, liquibaseDB).update(Contexts().toString()) val dummyKey = Crypto.generateKeyPair().public diff --git a/node/src/test/kotlin/net/corda/node/migration/VaultStateMigrationTest.kt b/node/src/test/kotlin/net/corda/node/migration/VaultStateMigrationTest.kt index da138f9d15..9688afca81 100644 --- a/node/src/test/kotlin/net/corda/node/migration/VaultStateMigrationTest.kt +++ b/node/src/test/kotlin/net/corda/node/migration/VaultStateMigrationTest.kt @@ -212,7 +212,8 @@ class VaultStateMigrationTest { stateMachineRunId = null, transaction = tx.serialize(context = SerializationDefaults.STORAGE_CONTEXT).bytes, status = DBTransactionStorage.TransactionStatus.VERIFIED, - timestamp = Instant.now() + timestamp = Instant.now(), + signatures = null ) session.save(persistentTx) } diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index 883b389072..44b44bcf40 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -10,10 +10,12 @@ import net.corda.core.flows.StateReplacementException import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.ServiceHub +import net.corda.core.node.StatesToRecord import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds +import net.corda.node.services.api.ServiceHubInternal import net.corda.testing.contracts.DummyContract import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME @@ -224,6 +226,6 @@ fun issueInvalidState(services: ServiceHub, identity: Party, notary: Party): Sta val tx = DummyContract.generateInitial(Random().nextInt(), notary, identity.ref(0)) tx.setTimeWindow(Instant.now(), 30.seconds) val stx = services.signInitialTransaction(tx) - services.recordTransactions(stx) + (services as ServiceHubInternal).recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(stx), disableSignatureVerification = true) return stx.tx.outRef(0) } \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/services/ServiceHubConcurrentUsageTest.kt b/node/src/test/kotlin/net/corda/node/services/ServiceHubConcurrentUsageTest.kt index ecba9d248d..9e235cebb1 100644 --- a/node/src/test/kotlin/net/corda/node/services/ServiceHubConcurrentUsageTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/ServiceHubConcurrentUsageTest.kt @@ -13,6 +13,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.issuedBy +import net.corda.testing.node.internal.CustomCordapp import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.startFlow @@ -23,7 +24,8 @@ import rx.schedulers.Schedulers import java.util.concurrent.CountDownLatch class ServiceHubConcurrentUsageTest { - private val mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP)) + private val mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, + CustomCordapp(classes = setOf(TestFlow::class.java)))) @After fun stopNodes() { diff --git a/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt index b0b4d8313d..df1fa5aa64 100644 --- a/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt @@ -75,7 +75,9 @@ class TimedFlowTests { @JvmStatic fun setup() { mockNet = InternalMockNetwork( - cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, enclosedCordapp()), + cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, + CustomCordapp(classes = setOf(FinalityFlow::class.java)), + enclosedCordapp()), defaultParameters = MockNetworkParameters().withServicePeerAllocationStrategy(InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin()), threadPerNode = true ) diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/AesDbEncryptionServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/AesDbEncryptionServiceTest.kt new file mode 100644 index 0000000000..806357627a --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/services/persistence/AesDbEncryptionServiceTest.kt @@ -0,0 +1,134 @@ +package net.corda.node.services.persistence + +import net.corda.node.services.persistence.AesDbEncryptionService.EncryptionKeyRecord +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig +import net.corda.testing.core.TestIdentity +import net.corda.testing.internal.configureDatabase +import net.corda.testing.node.MockServices +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.assertj.core.api.Assertions.assertThatIllegalArgumentException +import org.junit.After +import org.junit.Before +import org.junit.Test +import java.nio.ByteBuffer +import java.security.GeneralSecurityException +import java.util.UUID + +class AesDbEncryptionServiceTest { + private val identity = TestIdentity.fresh("me").party + private lateinit var database: CordaPersistence + private lateinit var encryptionService: AesDbEncryptionService + + @Before + fun setUp() { + val dataSourceProps = MockServices.makeTestDataSourceProperties() + database = configureDatabase(dataSourceProps, DatabaseConfig(), { null }, { null }) + encryptionService = AesDbEncryptionService(database) + encryptionService.start(identity) + } + + @After + fun cleanUp() { + database.close() + } + + @Test(timeout = 300_000) + fun `same instance can decrypt ciphertext`() { + val ciphertext = encryptionService.encrypt("Hello World".toByteArray()) + val (plaintext, authenticatedData) = encryptionService.decrypt(ciphertext) + assertThat(String(plaintext)).isEqualTo("Hello World") + assertThat(authenticatedData).isNull() + } + + @Test(timeout = 300_000) + fun `encypting twice produces different ciphertext`() { + val plaintext = "Hello".toByteArray() + assertThat(encryptionService.encrypt(plaintext)).isNotEqualTo(encryptionService.encrypt(plaintext)) + } + + @Test(timeout = 300_000) + fun `ciphertext can be decrypted after restart`() { + val ciphertext = encryptionService.encrypt("Hello World".toByteArray()) + encryptionService = AesDbEncryptionService(database) + encryptionService.start(identity) + val plaintext = encryptionService.decrypt(ciphertext).plaintext + assertThat(String(plaintext)).isEqualTo("Hello World") + } + + @Test(timeout = 300_000) + fun `encrypting with authenticated data`() { + val ciphertext = encryptionService.encrypt("Hello World".toByteArray(), "Additional data".toByteArray()) + val (plaintext, authenticatedData) = encryptionService.decrypt(ciphertext) + assertThat(String(plaintext)).isEqualTo("Hello World") + assertThat(authenticatedData?.let { String(it) }).isEqualTo("Additional data") + } + + @Test(timeout = 300_000) + fun extractUnauthenticatedAdditionalData() { + val ciphertext = encryptionService.encrypt("Hello World".toByteArray(), "Additional data".toByteArray()) + val additionalData = encryptionService.extractUnauthenticatedAdditionalData(ciphertext) + assertThat(additionalData?.let { String(it) }).isEqualTo("Additional data") + } + + @Test(timeout = 300_000) + fun `ciphertext cannot be decrypted if the authenticated data is modified`() { + val ciphertext = ByteBuffer.wrap(encryptionService.encrypt("Hello World".toByteArray(), "1234".toByteArray())) + + ciphertext.position(21) + ciphertext.put("4321".toByteArray()) // Use same length for the modified AAD + + assertThatExceptionOfType(GeneralSecurityException::class.java).isThrownBy { + encryptionService.decrypt(ciphertext.array()) + } + } + + @Test(timeout = 300_000) + fun `ciphertext cannot be decrypted if the key used is deleted`() { + val ciphertext = encryptionService.encrypt("Hello World".toByteArray()) + val keyId = ByteBuffer.wrap(ciphertext).getKeyId() + val deletedCount = database.transaction { + session.createQuery("DELETE FROM ${EncryptionKeyRecord::class.java.name} k WHERE k.keyId = :keyId") + .setParameter("keyId", keyId) + .executeUpdate() + } + assertThat(deletedCount).isEqualTo(1) + + encryptionService = AesDbEncryptionService(database) + encryptionService.start(identity) + assertThatIllegalArgumentException().isThrownBy { + encryptionService.decrypt(ciphertext) + } + } + + @Test(timeout = 300_000) + fun `ciphertext cannot be decrypted if forced to use a different key`() { + val ciphertext = ByteBuffer.wrap(encryptionService.encrypt("Hello World".toByteArray())) + val keyId = ciphertext.getKeyId() + val anotherKeyId = database.transaction { + session.createQuery("SELECT keyId FROM ${EncryptionKeyRecord::class.java.name} k WHERE k.keyId != :keyId", UUID::class.java) + .setParameter("keyId", keyId) + .setMaxResults(1) + .singleResult + } + + ciphertext.putKeyId(anotherKeyId) + + encryptionService = AesDbEncryptionService(database) + encryptionService.start(identity) + assertThatExceptionOfType(GeneralSecurityException::class.java).isThrownBy { + encryptionService.decrypt(ciphertext.array()) + } + } + + private fun ByteBuffer.getKeyId(): UUID { + position(1) + return getUUID() + } + + private fun ByteBuffer.putKeyId(keyId: UUID) { + position(1) + putUUID(keyId) + } +} diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageLedgerRecoveryTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageLedgerRecoveryTests.kt new file mode 100644 index 0000000000..0df955e0f1 --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageLedgerRecoveryTests.kt @@ -0,0 +1,275 @@ +package net.corda.node.services.persistence + +import net.corda.core.contracts.StateRef +import net.corda.core.crypto.Crypto +import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.SignableData +import net.corda.core.crypto.SignatureMetadata +import net.corda.core.crypto.sign +import net.corda.core.flows.DistributionList.ReceiverDistributionList +import net.corda.core.flows.DistributionList.SenderDistributionList +import net.corda.core.flows.ReceiverDistributionRecord +import net.corda.core.flows.SenderDistributionRecord +import net.corda.core.flows.TransactionMetadata +import net.corda.core.node.NodeInfo +import net.corda.core.node.StatesToRecord.ALL_VISIBLE +import net.corda.core.node.StatesToRecord.NONE +import net.corda.core.node.StatesToRecord.ONLY_RELEVANT +import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.WireTransaction +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.node.CordaClock +import net.corda.node.services.identity.InMemoryIdentityService +import net.corda.node.services.network.PersistentNetworkMapCache +import net.corda.node.services.network.PersistentPartyInfoCache +import net.corda.node.services.persistence.DBTransactionStorage.TransactionStatus.IN_FLIGHT +import net.corda.node.services.persistence.DBTransactionStorage.TransactionStatus.VERIFIED +import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery.DBReceiverDistributionRecord +import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery.DBSenderDistributionRecord +import net.corda.nodeapi.internal.DEV_ROOT_CA +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.CHARLIE_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity +import net.corda.testing.core.dummyCommand +import net.corda.testing.internal.TestingNamedCacheFactory +import net.corda.testing.internal.configureDatabase +import net.corda.testing.internal.createWireTransaction +import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties +import net.corda.testing.node.TestClock +import net.corda.testing.node.internal.MockEncryptionService +import org.assertj.core.api.Assertions.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import java.security.KeyPair +import java.time.Clock +import java.time.Instant +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNull + +class DBTransactionStorageLedgerRecoveryTests { + private companion object { + val ALICE = TestIdentity(ALICE_NAME, 70) + val BOB = TestIdentity(BOB_NAME, 80) + val CHARLIE = TestIdentity(CHARLIE_NAME, 90) + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20) + } + + @Rule + @JvmField + val testSerialization = SerializationEnvironmentRule(inheritable = true) + + private lateinit var database: CordaPersistence + private lateinit var transactionRecovery: DBTransactionStorageLedgerRecovery + private lateinit var partyInfoCache: PersistentPartyInfoCache + + private val encryptionService = MockEncryptionService() + + @Before + fun setUp() { + val dataSourceProps = makeTestDataSourceProperties() + database = configureDatabase(dataSourceProps, DatabaseConfig(), { null }, { null }) + newTransactionRecovery() + } + + @After + fun cleanUp() { + database.close() + } + + fun now(): Instant { + return transactionRecovery.clock.instant() + } + + @Test(timeout = 300_000) + fun `transaction without peers does not store recovery metadata in database`() { + val senderTransaction = newTransaction() + transactionRecovery.addUnnotarisedTransaction(senderTransaction) + transactionRecovery.addSenderTransactionRecoveryMetadata(senderTransaction.id, TransactionMetadata(ALICE_NAME, SenderDistributionList(ONLY_RELEVANT, emptyMap()))) + assertEquals(IN_FLIGHT, readTransactionFromDB(senderTransaction.id).status) + assertEquals(0, readSenderDistributionRecordFromDB(senderTransaction.id).size) + } + + @Test(timeout = 300_000) + fun `create un-notarised transaction with flow metadata and validate status in db`() { + val senderTransaction = newTransaction() + transactionRecovery.addUnnotarisedTransaction(senderTransaction) + transactionRecovery.addSenderTransactionRecoveryMetadata(senderTransaction.id, + TransactionMetadata(ALICE_NAME, SenderDistributionList(ALL_VISIBLE, mapOf(BOB_NAME to ALL_VISIBLE)))) + assertEquals(IN_FLIGHT, readTransactionFromDB(senderTransaction.id).status) + readSenderDistributionRecordFromDB(senderTransaction.id).let { + assertEquals(1, it.size) + assertEquals(ALL_VISIBLE, it[0].senderStatesToRecord) + assertEquals(BOB_NAME, partyInfoCache.getCordaX500NameByPartyId(it[0].peerPartyId)) + } + + val receiverTransaction = newTransaction() + transactionRecovery.addUnnotarisedTransaction(receiverTransaction) + val encryptedDL = transactionRecovery.addSenderTransactionRecoveryMetadata(receiverTransaction.id, + TransactionMetadata(ALICE_NAME, SenderDistributionList(ONLY_RELEVANT, mapOf(BOB_NAME to ALL_VISIBLE)))) + transactionRecovery.addReceiverTransactionRecoveryMetadata(receiverTransaction.id, ALICE_NAME, + TransactionMetadata(ALICE_NAME, ReceiverDistributionList(encryptedDL, ALL_VISIBLE))) + assertEquals(IN_FLIGHT, readTransactionFromDB(receiverTransaction.id).status) + readReceiverDistributionRecordFromDB(receiverTransaction.id).let { record -> + val distList = transactionRecovery.decryptHashedDistributionList(record.encryptedDistributionList.bytes) + assertEquals(ONLY_RELEVANT, distList.senderStatesToRecord) + assertEquals(ALL_VISIBLE, distList.peerHashToStatesToRecord.values.first()) + assertEquals(ALICE_NAME, partyInfoCache.getCordaX500NameByPartyId(record.peerPartyId)) + assertEquals(setOf(BOB_NAME), distList.peerHashToStatesToRecord.map { (peer) -> partyInfoCache.getCordaX500NameByPartyId(peer) }.toSet() ) + } + } + + @Test(timeout = 300_000) + fun `finalize transaction with recovery metadata`() { + val transaction = newTransaction(notarySig = false) + transactionRecovery.finalizeTransaction(transaction) + transactionRecovery.addSenderTransactionRecoveryMetadata(transaction.id, + TransactionMetadata(ALICE_NAME, SenderDistributionList(ONLY_RELEVANT, mapOf(CHARLIE_NAME to ALL_VISIBLE)))) + assertEquals(VERIFIED, readTransactionFromDB(transaction.id).status) + readSenderDistributionRecordFromDB(transaction.id).apply { + assertEquals(1, this.size) + assertEquals(ONLY_RELEVANT, this[0].senderStatesToRecord) + assertEquals(ALL_VISIBLE, this[0].receiverStatesToRecord) + } + } + + @Test(timeout = 300_000) + fun `remove un-notarised transaction and associated recovery metadata`() { + val senderTransaction = newTransaction(notarySig = false) + transactionRecovery.addUnnotarisedTransaction(senderTransaction) + val encryptedDL1 = transactionRecovery.addSenderTransactionRecoveryMetadata(senderTransaction.id, + TransactionMetadata(ALICE.name, SenderDistributionList(ONLY_RELEVANT, mapOf(BOB.name to ONLY_RELEVANT, CHARLIE_NAME to ONLY_RELEVANT)))) + transactionRecovery.addReceiverTransactionRecoveryMetadata(senderTransaction.id, BOB.name, + TransactionMetadata(ALICE.name, ReceiverDistributionList(encryptedDL1, ONLY_RELEVANT))) + assertNull(transactionRecovery.getTransaction(senderTransaction.id)) + assertEquals(IN_FLIGHT, readTransactionFromDB(senderTransaction.id).status) + + assertEquals(true, transactionRecovery.removeUnnotarisedTransaction(senderTransaction.id)) + assertFailsWith { readTransactionFromDB(senderTransaction.id).status } + assertEquals(0, readSenderDistributionRecordFromDB(senderTransaction.id).size) + assertNull(transactionRecovery.getTransactionWithStatus(senderTransaction.id)) + + val receiverTransaction = newTransaction(notarySig = false) + transactionRecovery.addUnnotarisedTransaction(receiverTransaction) + val encryptedDL2 = transactionRecovery.addSenderTransactionRecoveryMetadata(receiverTransaction.id, + TransactionMetadata(ALICE.name, SenderDistributionList(ONLY_RELEVANT, mapOf(BOB.name to ONLY_RELEVANT)))) + transactionRecovery.addReceiverTransactionRecoveryMetadata(receiverTransaction.id, BOB.name, + TransactionMetadata(ALICE.name, ReceiverDistributionList(encryptedDL2, ONLY_RELEVANT))) + assertNull(transactionRecovery.getTransaction(receiverTransaction.id)) + assertEquals(IN_FLIGHT, readTransactionFromDB(receiverTransaction.id).status) + + assertEquals(true, transactionRecovery.removeUnnotarisedTransaction(receiverTransaction.id)) + assertFailsWith { readTransactionFromDB(receiverTransaction.id).status } + assertFailsWith { readReceiverDistributionRecordFromDB(receiverTransaction.id) } + assertNull(transactionRecovery.getTransactionWithStatus(receiverTransaction.id)) + } + + @Test(timeout = 300_000) + fun `test lightweight serialization and deserialization of hashed distribution list payload`() { + val hashedDistList = HashedDistributionList( + ALL_VISIBLE, + mapOf(SecureHash.sha256(BOB.name.toString()) to NONE, SecureHash.sha256(CHARLIE_NAME.toString()) to ONLY_RELEVANT), + HashedDistributionList.PublicHeader(now(), 1) + ) + val roundtrip = HashedDistributionList.decrypt(hashedDistList.encrypt(encryptionService), encryptionService) + assertThat(roundtrip).isEqualTo(hashedDistList) + } + + private fun readTransactionFromDB(txId: SecureHash): DBTransactionStorage.DBTransaction { + val fromDb = database.transaction { + session.createQuery( + "from ${DBTransactionStorage.DBTransaction::class.java.name} where tx_id = :transactionId", + DBTransactionStorage.DBTransaction::class.java + ).setParameter("transactionId", txId.toString()).resultList + } + assertEquals(1, fromDb.size) + return fromDb[0] + } + + private fun readSenderDistributionRecordFromDB(txId: SecureHash? = null): List { + return database.transaction { + if (txId != null) + session.createQuery( + "from ${DBSenderDistributionRecord::class.java.name} where transaction_id = :transactionId", + DBSenderDistributionRecord::class.java + ).setParameter("transactionId", txId.toString()).resultList.map { it.toSenderDistributionRecord() } + else + session.createQuery( + "from ${DBSenderDistributionRecord::class.java.name}", + DBSenderDistributionRecord::class.java + ).resultList.map { it.toSenderDistributionRecord() } + } + } + + private fun readReceiverDistributionRecordFromDB(txId: SecureHash): ReceiverDistributionRecord { + val fromDb = database.transaction { + session.createQuery( + "from ${DBReceiverDistributionRecord::class.java.name} where transaction_id = :transactionId", + DBReceiverDistributionRecord::class.java + ).setParameter("transactionId", txId.toString()).resultList + } + assertEquals(1, fromDb.size) + return fromDb[0].toReceiverDistributionRecord() + } + + private fun newTransactionRecovery(cacheSizeBytesOverride: Long? = null, clock: CordaClock = TestClock(Clock.systemUTC())) { + val networkMapCache = PersistentNetworkMapCache(TestingNamedCacheFactory(), database, InMemoryIdentityService(trustRoot = DEV_ROOT_CA.certificate)) + val alice = createNodeInfo(listOf(ALICE)) + val bob = createNodeInfo(listOf(BOB)) + val charlie = createNodeInfo(listOf(CHARLIE)) + networkMapCache.addOrUpdateNodes(listOf(alice, bob, charlie)) + partyInfoCache = PersistentPartyInfoCache(networkMapCache, TestingNamedCacheFactory(), database) + partyInfoCache.start() + transactionRecovery = DBTransactionStorageLedgerRecovery( + database, + TestingNamedCacheFactory(cacheSizeBytesOverride ?: 1024), + clock, + encryptionService, + partyInfoCache + ) + } + + private var portCounter = 1000 + private fun createNodeInfo(identities: List, + address: NetworkHostAndPort = NetworkHostAndPort("localhost", portCounter++)): NodeInfo { + return NodeInfo( + addresses = listOf(address), + legalIdentitiesAndCerts = identities.map { it.identity }, + platformVersion = 3, + serial = 1 + ) + } + + private fun newTransaction(notarySig: Boolean = true): SignedTransaction { + val wtx = createWireTransaction( + inputs = listOf(StateRef(SecureHash.randomSHA256(), 0)), + attachments = emptyList(), + outputs = emptyList(), + commands = listOf(dummyCommand(ALICE.publicKey)), + notary = DUMMY_NOTARY.party, + timeWindow = null + ) + return makeSigned(wtx, ALICE.keyPair, notarySig = notarySig) + } + + private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair, notarySig: Boolean = true): SignedTransaction { + val keySigs = keys.map { it.sign(SignableData(wtx.id, SignatureMetadata(1, Crypto.findSignatureScheme(it.public).schemeNumberID))) } + val sigs = if (notarySig) { + keySigs + notarySig(wtx.id) + } else { + keySigs + } + return SignedTransaction(wtx, sigs) + } + + private fun notarySig(txId: SecureHash) = + DUMMY_NOTARY.keyPair.sign(SignableData(txId, SignatureMetadata(1, Crypto.findSignatureScheme(DUMMY_NOTARY.publicKey).schemeNumberID))) +} + diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt index 51b400c321..2d54442556 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt @@ -1,17 +1,25 @@ package net.corda.node.services.persistence +import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertTrue import net.corda.core.concurrent.CordaFuture import net.corda.core.contracts.StateRef import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.SignableData import net.corda.core.crypto.SignatureMetadata import net.corda.core.crypto.TransactionSignature +import net.corda.core.crypto.sign +import net.corda.core.serialization.deserialize import net.corda.core.toFuture import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.WireTransaction import net.corda.node.CordaClock import net.corda.node.MutableClock import net.corda.node.SimpleClock +import net.corda.node.services.persistence.DBTransactionStorage.TransactionStatus.IN_FLIGHT +import net.corda.node.services.persistence.DBTransactionStorage.TransactionStatus.UNVERIFIED +import net.corda.node.services.persistence.DBTransactionStorage.TransactionStatus.VERIFIED import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig @@ -32,17 +40,21 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import rx.plugins.RxJavaHooks +import java.security.KeyPair import java.time.Clock import java.time.Instant import java.util.concurrent.Semaphore import java.util.concurrent.TimeUnit import kotlin.concurrent.thread import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertFalse +import kotlin.test.assertNull class DBTransactionStorageTests { private companion object { - val ALICE_PUBKEY = TestIdentity(ALICE_NAME, 70).publicKey - val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + val ALICE = TestIdentity(ALICE_NAME, 70) + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20) } @Rule @@ -90,6 +102,157 @@ class DBTransactionStorageTests { assertEquals(now, readTransactionTimestampFromDB(transaction.id)) } + @Test(timeout = 300_000) + fun `create transaction missing notary signature and validate status in db`() { + val now = Instant.ofEpochSecond(333444555L) + val transactionClock = TransactionClock(now) + newTransactionStorage(clock = transactionClock) + val transaction = newTransaction() + transactionStorage.addUnnotarisedTransaction(transaction) + assertEquals(IN_FLIGHT, readTransactionFromDB(transaction.id).status) + } + + @Test(timeout = 300_000) + fun `finalize transaction with no prior recording of un-notarised transaction`() { + val now = Instant.ofEpochSecond(333444555L) + val transactionClock = TransactionClock(now) + newTransactionStorage(clock = transactionClock) + val transaction = newTransaction() + transactionStorage.finalizeTransactionWithExtraSignatures(transaction, listOf(notarySig(transaction.id))) + readTransactionFromDB(transaction.id).let { + assertSignatures(it.transaction, it.signatures, transaction.sigs) + assertEquals(VERIFIED, it.status) + } + } + + @Test(timeout = 300_000) + fun `finalize transaction after recording transaction as un-notarised`() { + val now = Instant.ofEpochSecond(333444555L) + val transactionClock = TransactionClock(now) + newTransactionStorage(clock = transactionClock) + val transaction = newTransaction(notarySig = false) + transactionStorage.addUnnotarisedTransaction(transaction) + assertNull(transactionStorage.getTransaction(transaction.id)) + assertEquals(IN_FLIGHT, readTransactionFromDB(transaction.id).status) + transactionStorage.finalizeTransactionWithExtraSignatures(transaction, emptyList()) + readTransactionFromDB(transaction.id).let { + assertSignatures(it.transaction, it.signatures, transaction.sigs) + assertEquals(VERIFIED, it.status) + } + } + + @Test(timeout = 300_000) + fun `finalize transaction with extra signatures after recording transaction as un-notarised`() { + val now = Instant.ofEpochSecond(333444555L) + val transactionClock = TransactionClock(now) + newTransactionStorage(clock = transactionClock) + val transaction = newTransaction(notarySig = false) + transactionStorage.addUnnotarisedTransaction(transaction) + assertNull(transactionStorage.getTransaction(transaction.id)) + assertEquals(IN_FLIGHT, readTransactionFromDB(transaction.id).status) + val notarySig = notarySig(transaction.id) + transactionStorage.finalizeTransactionWithExtraSignatures(transaction, listOf(notarySig)) + readTransactionFromDB(transaction.id).let { + assertSignatures(it.transaction, it.signatures, transaction.sigs + notarySig) + assertEquals(VERIFIED, it.status) + } + } + + @Test(timeout = 300_000) + fun `remove un-notarised transaction`() { + val now = Instant.ofEpochSecond(333444555L) + val transactionClock = TransactionClock(now) + newTransactionStorage(clock = transactionClock) + val transaction = newTransaction(notarySig = false) + transactionStorage.addUnnotarisedTransaction(transaction) + assertNull(transactionStorage.getTransaction(transaction.id)) + assertEquals(IN_FLIGHT, readTransactionFromDB(transaction.id).status) + + assertEquals(true, transactionStorage.removeUnnotarisedTransaction(transaction.id)) + assertFailsWith { readTransactionFromDB(transaction.id).status } + assertNull(transactionStorage.getTransactionWithStatus(transaction.id)) + } + + @Test(timeout = 300_000) + fun `finalize unverified transaction and verify no additional signatures are added`() { + val now = Instant.ofEpochSecond(333444555L) + val transactionClock = TransactionClock(now) + newTransactionStorage(clock = transactionClock) + val transaction = newTransaction() + transactionStorage.addUnverifiedTransaction(transaction) + assertNull(transactionStorage.getTransaction(transaction.id)) + assertEquals(UNVERIFIED, readTransactionFromDB(transaction.id).status) + // attempt to finalise with another notary signature + transactionStorage.finalizeTransactionWithExtraSignatures(transaction, listOf(notarySig(transaction.id))) + readTransactionFromDB(transaction.id).let { + assertSignatures(it.transaction, it.signatures, transaction.sigs) + assertEquals(VERIFIED, it.status) + } + } + + @Test(timeout = 300_000) + fun `simulate finalize race condition where first transaction trumps follow-up transaction`() { + val now = Instant.ofEpochSecond(333444555L) + val transactionClock = TransactionClock(now) + newTransactionStorage(clock = transactionClock) + val transactionWithoutNotarySig = newTransaction(notarySig = false) + + // txn recorded as un-notarised (simulate ReceiverFinalityFlow in initial flow) + transactionStorage.addUnnotarisedTransaction(transactionWithoutNotarySig) + assertEquals(IN_FLIGHT, readTransactionFromDB(transactionWithoutNotarySig.id).status) + + // txn then recorded as unverified (simulate ResolveTransactionFlow in follow-up flow) + val notarySig = notarySig(transactionWithoutNotarySig.id) + transactionStorage.addUnverifiedTransaction(transactionWithoutNotarySig + notarySig) + assertEquals(UNVERIFIED, readTransactionFromDB(transactionWithoutNotarySig.id).status) + + // txn finalised with notary signatures (even though in UNVERIFIED state) + assertTrue(transactionStorage.finalizeTransactionWithExtraSignatures(transactionWithoutNotarySig, listOf(notarySig))) + readTransactionFromDB(transactionWithoutNotarySig.id).let { + assertSignatures(it.transaction, it.signatures, transactionWithoutNotarySig.sigs + notarySig) + assertEquals(VERIFIED, it.status) + } + + // attempt to record follow-up txn + assertFalse(transactionStorage.addTransaction(transactionWithoutNotarySig + notarySig)) + readTransactionFromDB(transactionWithoutNotarySig.id).let { + assertSignatures(it.transaction, it.signatures, transactionWithoutNotarySig.sigs + notarySig) + assertEquals(VERIFIED, it.status) + } + } + + @Test(timeout = 300_000) + fun `simulate finalize race condition where follow-up transaction races ahead of initial transaction`() { + val now = Instant.ofEpochSecond(333444555L) + val transactionClock = TransactionClock(now) + newTransactionStorage(clock = transactionClock) + val transactionWithoutNotarySigs = newTransaction(notarySig = false) + + // txn recorded as un-notarised (simulate ReceiverFinalityFlow in initial flow) + transactionStorage.addUnnotarisedTransaction(transactionWithoutNotarySigs) + assertEquals(IN_FLIGHT, readTransactionFromDB(transactionWithoutNotarySigs.id).status) + + // txn then recorded as unverified (simulate ResolveTransactionFlow in follow-up flow) + val notarySig = notarySig(transactionWithoutNotarySigs.id) + val transactionWithNotarySigs = transactionWithoutNotarySigs + notarySig + transactionStorage.addUnverifiedTransaction(transactionWithNotarySigs) + assertEquals(UNVERIFIED, readTransactionFromDB(transactionWithoutNotarySigs.id).status) + + // txn then recorded as verified (simulate ResolveTransactions recording in follow-up flow) + assertTrue(transactionStorage.addTransaction(transactionWithNotarySigs)) + readTransactionFromDB(transactionWithoutNotarySigs.id).let { + assertSignatures(it.transaction, it.signatures, expectedSigs = transactionWithNotarySigs.sigs) + assertEquals(VERIFIED, it.status) + } + + // attempt to finalise original txn + assertFalse(transactionStorage.finalizeTransactionWithExtraSignatures(transactionWithoutNotarySigs, listOf(notarySig))) + readTransactionFromDB(transactionWithoutNotarySigs.id).let { + assertSignatures(it.transaction, it.signatures, expectedSigs = transactionWithNotarySigs.sigs) + assertEquals(VERIFIED, it.status) + } + } + @Test(timeout = 300_000) fun `create unverified then verified transaction and validate timestamps in db`() { val unverifiedTime = Instant.ofEpochSecond(555666777L) @@ -175,6 +338,17 @@ class DBTransactionStorageTests { return fromDb[0].timestamp } + private fun readTransactionFromDB(id: SecureHash): DBTransactionStorage.DBTransaction { + val fromDb = database.transaction { + session.createQuery( + "from ${DBTransactionStorage.DBTransaction::class.java.name} where tx_id = :transactionId", + DBTransactionStorage.DBTransaction::class.java + ).setParameter("transactionId", id.toString()).resultList.map { it } + } + assertEquals(1, fromDb.size) + return fromDb[0] + } + @Test(timeout = 300_000) fun `empty store`() { assertThat(transactionStorage.getTransaction(newTransaction().id)).isNull() @@ -369,7 +543,7 @@ class DBTransactionStorageTests { // Assert - assertThat(result).isNotNull() + assertThat(result).isNotNull assertThat(result?.get(20, TimeUnit.SECONDS)?.id).isEqualTo(signedTransaction.id) } @@ -399,18 +573,36 @@ class DBTransactionStorageTests { assertThat(transactionStorage.getTransaction(transaction.id)).isEqualTo(transaction) } - private fun newTransaction(): SignedTransaction { + private fun newTransaction(notarySig: Boolean = true): SignedTransaction { val wtx = createWireTransaction( inputs = listOf(StateRef(SecureHash.randomSHA256(), 0)), attachments = emptyList(), outputs = emptyList(), - commands = listOf(dummyCommand()), - notary = DUMMY_NOTARY, + commands = listOf(dummyCommand(ALICE.publicKey)), + notary = DUMMY_NOTARY.party, timeWindow = null ) - return SignedTransaction( - wtx, - listOf(TransactionSignature(ByteArray(1), ALICE_PUBKEY, SignatureMetadata(1, Crypto.findSignatureScheme(ALICE_PUBKEY).schemeNumberID))) - ) + return makeSigned(wtx, ALICE.keyPair, notarySig = notarySig) + } + + private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair, notarySig: Boolean = true): SignedTransaction { + val keySigs = keys.map { it.sign(SignableData(wtx.id, SignatureMetadata(1, Crypto.findSignatureScheme(it.public).schemeNumberID))) } + val sigs = if (notarySig) { + keySigs + notarySig(wtx.id) + } else { + keySigs + } + return SignedTransaction(wtx, sigs) + } + + private fun notarySig(txId: SecureHash) = + DUMMY_NOTARY.keyPair.sign(SignableData(txId, SignatureMetadata(1, Crypto.findSignatureScheme(DUMMY_NOTARY.publicKey).schemeNumberID))) + + private fun assertSignatures(transaction: ByteArray, extraSigs: ByteArray?, + expectedSigs: List) { + assertNotNull(extraSigs) + assertEquals(expectedSigs, + (transaction.deserialize(context = DBTransactionStorage.contextToUse()).sigs + + extraSigs!!.deserialize>()).distinct()) } } diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index def6530e5e..9c2628ff45 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -58,6 +58,7 @@ import net.corda.testing.internal.IS_OPENJ9 import net.corda.testing.internal.LogHelper import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin +import net.corda.testing.node.internal.CustomCordapp import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP import net.corda.testing.node.internal.InternalMockNetwork @@ -124,7 +125,7 @@ class FlowFrameworkTests { @Before fun setUpMockNet() { mockNet = InternalMockNetwork( - cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, FINANCE_CONTRACTS_CORDAPP), + cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, FINANCE_CONTRACTS_CORDAPP, CustomCordapp(setOf("net.corda.node.services.statemachine"))), servicePeerAllocationStrategy = RoundRobin() ) diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt index 2e4ccdecc2..608d1ad7f4 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt @@ -319,7 +319,7 @@ class RetryFlowMockTest { private fun doInsert() { val tx = DBTransactionStorage.DBTransaction("Foo", null, Utils.EMPTY_BYTES, - DBTransactionStorage.TransactionStatus.VERIFIED, Instant.now()) + DBTransactionStorage.TransactionStatus.VERIFIED, Instant.now(), null) contextTransaction.session.save(tx) } } diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index 682af85bd8..35691970c7 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -664,14 +664,15 @@ class NodeVaultServiceTest { database.transaction { vaultService.notify(StatesToRecord.ONLY_RELEVANT, issueTx) } val expectedIssueUpdate = Vault.Update(emptySet(), setOf(cashState), null) - database.transaction { + val moveTx = database.transaction { val moveBuilder = TransactionBuilder(notary).apply { CashUtils.generateSpend(services, this, Amount(1000, GBP), identity, thirdPartyIdentity) } val moveTx = moveBuilder.toWireTransaction(services) vaultService.notify(StatesToRecord.ONLY_RELEVANT, moveTx) + moveTx } - val expectedMoveUpdate = Vault.Update(setOf(cashState), emptySet(), null) + val expectedMoveUpdate = Vault.Update(setOf(cashState), emptySet(), null, consumingTxIds = mapOf(cashState.ref to moveTx.id)) // ensure transaction contract state is persisted in DBStorage val signedMoveTx = services.signInitialTransaction(issueBuilder) @@ -739,8 +740,8 @@ class NodeVaultServiceTest { } val expectedIssueUpdate = Vault.Update(emptySet(), setOf(initialCashState), null) - val expectedNotaryChangeUpdate = Vault.Update(setOf(initialCashState), setOf(cashStateWithNewNotary), null, Vault.UpdateType.NOTARY_CHANGE) - val expectedMoveUpdate = Vault.Update(setOf(cashStateWithNewNotary), emptySet(), null) + val expectedNotaryChangeUpdate = Vault.Update(setOf(initialCashState), setOf(cashStateWithNewNotary), null, Vault.UpdateType.NOTARY_CHANGE, consumingTxIds = mapOf(initialCashState.ref to changeNotaryTx.id)) + val expectedMoveUpdate = Vault.Update(setOf(cashStateWithNewNotary), emptySet(), null, consumingTxIds = mapOf(cashStateWithNewNotary.ref to moveTx.id)) val observedUpdates = vaultSubscriber.onNextEvents assertEquals(observedUpdates, listOf(expectedIssueUpdate, expectedNotaryChangeUpdate, expectedMoveUpdate)) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 94a6eda019..b256651f00 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -1681,6 +1681,7 @@ abstract class VaultQueryTestsBase : VaultQueryParties { // DOCEND VaultQueryExample7 assertThat(results.states).hasSize(10) assertThat(results.totalStatesAvailable).isEqualTo(100) + assertThat(results.previousPageAnchor).isNull() } } @@ -1689,14 +1690,19 @@ abstract class VaultQueryTestsBase : VaultQueryParties { fun `all states with paging specification - last`() { database.transaction { vaultFiller.fillWithSomeTestCash(95.DOLLARS, notaryServices, 95, DUMMY_CASH_ISSUER) + val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) + // Last page implies we need to perform a row count for the Query first, // and then re-query for a given offset defined by (count - pageSize) - val pagingSpec = PageSpecification(10, 10) + val lastPage = PageSpecification(10, 10) + val lastPageResults = vaultService.queryBy(criteria, paging = lastPage) + assertThat(lastPageResults.states).hasSize(5) // should retrieve states 90..94 + assertThat(lastPageResults.totalStatesAvailable).isEqualTo(95) - val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) - val results = vaultService.queryBy(criteria, paging = pagingSpec) - assertThat(results.states).hasSize(5) // should retrieve states 90..94 - assertThat(results.totalStatesAvailable).isEqualTo(95) + // Make sure the previousPageAnchor points to the previous page's last result + val penultimatePage = lastPage.copy(pageNumber = lastPage.pageNumber - 1) + val penultimatePageResults = vaultService.queryBy(criteria, paging = penultimatePage) + assertThat(lastPageResults.previousPageAnchor).isEqualTo(penultimatePageResults.statesMetadata.last().ref) } } @@ -1747,16 +1753,23 @@ abstract class VaultQueryTestsBase : VaultQueryParties { // example of querying states with paging using totalStatesAvailable private fun queryStatesWithPaging(vaultService: VaultService, pageSize: Int): List> { // DOCSTART VaultQueryExample24 - var pageNumber = DEFAULT_PAGE_NUM - val states = mutableListOf>() - do { - val pageSpec = PageSpecification(pageNumber = pageNumber, pageSize = pageSize) - val results = vaultService.queryBy(VaultQueryCriteria(), pageSpec) - states.addAll(results.states) - pageNumber++ - } while ((pageSpec.pageSize * (pageNumber - 1)) <= results.totalStatesAvailable) + retry@ + while (true) { + var pageNumber = DEFAULT_PAGE_NUM + val states = mutableListOf>() + do { + val pageSpec = PageSpecification(pageNumber = pageNumber, pageSize = pageSize) + val results = vaultService.queryBy(VaultQueryCriteria(), pageSpec) + if (results.previousPageAnchor != states.lastOrNull()?.ref) { + // Start querying from the 1st page again if we find the vault has changed from underneath us. + continue@retry + } + states.addAll(results.states) + pageNumber++ + } while ((pageSpec.pageSize * (pageNumber - 1)) <= results.totalStatesAvailable) + return states + } // DOCEND VaultQueryExample24 - return states.toList() } // test paging query example works @@ -1772,6 +1785,51 @@ abstract class VaultQueryTestsBase : VaultQueryParties { } } + @Test(timeout = 300_000) + fun `detecting changes to the database whilst pages are loaded`() { + val criteria = VaultQueryCriteria() + val sorting = Sort(setOf(Sort.SortColumn(SortAttribute.Standard(Sort.LinearStateAttribute.EXTERNAL_ID)))) + + fun loadPagesAndCheckAnchors(): List> { + val pages = (1..3).map { vaultService.queryBy(criteria, PageSpecification(it, 10), sorting) } + assertThat(pages[0].previousPageAnchor).isNull() + assertThat(pages[1].previousPageAnchor).isEqualTo(pages[0].states.last().ref) + assertThat(pages[2].previousPageAnchor).isEqualTo(pages[1].states.last().ref) + return pages + } + + database.transaction { + vaultFiller.fillWithSomeTestDeals(dealIds = (10..30).map(Int::toString)) + val pagesV1 = loadPagesAndCheckAnchors() + + vaultFiller.fillWithSomeTestDeals(dealIds = listOf("25.5")) // Insert a state into the middle of the second page + val pagesV2 = loadPagesAndCheckAnchors() + + assertThat(pagesV2[2].previousPageAnchor) + .isNotEqualTo(pagesV1[2].previousPageAnchor) // The previously loaded page is no longer in-sync + .isEqualTo(pagesV1[1].states.let { it[it.lastIndex - 1].ref }) // The anchor now points to the second to last entry + assertThat(pagesV2[1].previousPageAnchor).isEqualTo(pagesV1[1].previousPageAnchor) // The first page is unaffected + + vaultFiller.consumeDeals(pagesV1[0].states.take(1)) // Consume the first state + val pagesV3 = loadPagesAndCheckAnchors() + + assertThat(pagesV3[1].previousPageAnchor) + .isNotEqualTo(pagesV1[1].previousPageAnchor) // Now the first page is no longer in-sync + .isEqualTo(pagesV1[1].states[0].ref) // The top of the second page has now moved into the first page + + vaultFiller.consumeDeals(pagesV3[2].states) // Consume the entire third page + val pagesV4 = loadPagesAndCheckAnchors() + // There are now no states for the third page, but it will still have an anchor + assertThat(pagesV4[2].states).isEmpty() + + vaultFiller.consumeDeals(pagesV3[1].states.takeLast(1)) // Consume the third page anchor + + val thirdPageV5 = vaultService.queryBy(criteria, PageSpecification(3, 10), sorting) + assertThat(thirdPageV5.states).isEmpty() + assertThat(thirdPageV5.previousPageAnchor).isNull() + } + } + // test paging with aggregate function and group by clause @Test(timeout=300_000) fun `test paging with aggregate function and group by clause`() { diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt index 24c2f89e36..6e7dabd747 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt @@ -382,8 +382,10 @@ class VaultWithCashTest { linearStates.forEach { println(it.state.data.linearId) } //copy transactions to notary - simulates transaction resolution - services.validatedTransactions.getTransaction(deals.first().ref.txhash)?.apply { notaryServices.recordTransactions(this) } - services.validatedTransactions.getTransaction(linearStates.first().ref.txhash)?.apply { notaryServices.recordTransactions(this) } + services.validatedTransactions.getTransaction(deals.first().ref.txhash)?.apply { + notaryServices.recordTransactions(this, disableSignatureVerification = true) } + services.validatedTransactions.getTransaction(linearStates.first().ref.txhash)?.apply { + notaryServices.recordTransactions(this, disableSignatureVerification = true) } // Create a txn consuming different contract types val dummyMoveBuilder = TransactionBuilder(notary = notary).apply { diff --git a/release-tools/testing/requirements.txt b/release-tools/testing/requirements.txt index b4bc32760d..c6129d0ca0 100644 --- a/release-tools/testing/requirements.txt +++ b/release-tools/testing/requirements.txt @@ -1,3 +1,6 @@ jira==2.0.0 keyring==13.1.0 termcolor==1.1.0 +urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability +requests>=2.32.2 # not directly required, pinned by Snyk to avoid a vulnerability +setuptools>=70.0.0 # not directly required, pinned by Snyk to avoid a vulnerability diff --git a/samples/notary-demo/build.gradle b/samples/notary-demo/build.gradle index 6f87b55b35..e448345573 100644 --- a/samples/notary-demo/build.gradle +++ b/samples/notary-demo/build.gradle @@ -58,6 +58,15 @@ task deployNodesSingle(type: Cordform, dependsOn: ['jar', nodeTask, webTask]) { } rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]] } + node { + name "O=Bob Plc,L=Rome,C=IT" + p2pPort 10005 + rpcSettings { + address "localhost:10006" + adminAddress "localhost:10007" + } + rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]] + } node { name "O=Notary Node,L=Zurich,C=CH" p2pPort 10009 @@ -90,6 +99,15 @@ task deployNodesCustom(type: Cordform, dependsOn: ['jar', nodeTask, webTask]) { } rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]] } + node { + name "O=Bob Plc,L=Rome,C=IT" + p2pPort 10005 + rpcSettings { + address "localhost:10006" + adminAddress "localhost:10007" + } + rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]] + } node { name "O=Notary Node,L=Zurich,C=CH" p2pPort 10009 @@ -124,6 +142,15 @@ task deployNodesRaft(type: Cordform, dependsOn: ['jar', nodeTask, webTask]) { } rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]] } + node { + name "O=Bob Plc,L=Rome,C=IT" + p2pPort 10005 + rpcSettings { + address "localhost:10006" + adminAddress "localhost:10007" + } + rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]] + } node { name "O=Notary Service 0,L=Zurich,C=CH" p2pPort 10009 @@ -193,6 +220,15 @@ task deployNodesBFT(type: Cordform, dependsOn: ['jar', nodeTask, webTask]) { } rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]] } + node { + name "O=Bob Plc,L=Rome,C=IT" + p2pPort 10005 + rpcSettings { + address "localhost:10006" + adminAddress "localhost:10007" + } + rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]] + } node { name "O=Notary Service 0,L=Zurich,C=CH" p2pPort 10009 diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/client/Notarise.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/client/Notarise.kt index 0f1648752d..711f7042fd 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/client/Notarise.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/client/Notarise.kt @@ -2,10 +2,8 @@ package net.corda.notarydemo.client import net.corda.client.rpc.CordaRPCClient import net.corda.core.crypto.CompositeKey -import net.corda.core.crypto.Crypto import net.corda.core.crypto.toStringShort import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.Party import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow import net.corda.core.transactions.SignedTransaction @@ -32,7 +30,7 @@ private class NotaryDemoClientApi(val rpc: CordaRPCOps) { /** A dummy identity. */ private val BOB_NAME = CordaX500Name("Bob Plc", "Rome", "IT") - private val counterparty = Party(BOB_NAME, Crypto.generateKeyPair(Crypto.DEFAULT_SIGNATURE_SCHEME).public) + private val counterparty = rpc.wellKnownPartyFromX500Name(BOB_NAME) ?: throw IllegalArgumentException("Couldn't find Bob Plc party") /** Makes calls to the node rpc to start transaction notarisation. */ fun notarise(count: Int) { diff --git a/samples/notary-demo/workflows/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt b/samples/notary-demo/workflows/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt index dfa7c02ae0..a73a805e31 100644 --- a/samples/notary-demo/workflows/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt +++ b/samples/notary-demo/workflows/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt @@ -2,15 +2,22 @@ package net.corda.notarydemo.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.ContractState +import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.ReceiveFinalityFlow import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party +import net.corda.core.node.StatesToRecord import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.notarydemo.contracts.DO_NOTHING_PROGRAM_ID import net.corda.notarydemo.contracts.DummyCommand import net.corda.notarydemo.contracts.DummyState +@InitiatingFlow @StartableByRPC class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: Party, private val discriminator: Int) : FlowLogic() { @Suspendable @@ -22,12 +29,22 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: addCommand(DummyCommand(), listOf(ourIdentity.owningKey)) }) serviceHub.recordTransactions(issueTx) - // Move ownership of the asset to the counterparty - // We don't check signatures because we know that the notary's signature is missing - return serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { + + val stx = serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { addInputState(issueTx.tx.outRef(0)) addOutputState(state.copy(participants = listOf(counterpartyNode)), DO_NOTHING_PROGRAM_ID) addCommand(DummyCommand(), listOf(ourIdentity.owningKey)) }) + + return subFlow(FinalityFlow(stx, initiateFlow(counterpartyNode))) } } + +@InitiatedBy(DummyIssueAndMove::class) +class DummyIssueAndMoveResponder(private val otherSide: FlowSession) : FlowLogic() { + @Suspendable + override fun call() { + // As a non-participant to the transaction we need to record all states + subFlow(ReceiveFinalityFlow(otherSide, statesToRecord = StatesToRecord.ALL_VISIBLE)) + } +} \ No newline at end of file diff --git a/serialization-deterministic/README.md b/serialization-deterministic/README.md deleted file mode 100644 index abd4a19f0c..0000000000 --- a/serialization-deterministic/README.md +++ /dev/null @@ -1,2 +0,0 @@ -## corda-serialization-deterministic. -This artifact is a deterministic subset of the binary contents of `corda-serialization`. diff --git a/serialization-deterministic/build.gradle b/serialization-deterministic/build.gradle deleted file mode 100644 index 7822eb3b23..0000000000 --- a/serialization-deterministic/build.gradle +++ /dev/null @@ -1,228 +0,0 @@ -import net.corda.gradle.jarfilter.JarFilterTask -import net.corda.gradle.jarfilter.MetaFixerTask -import proguard.gradle.ProGuardTask -import static org.gradle.api.JavaVersion.VERSION_1_8 - -plugins { - id 'org.jetbrains.kotlin.jvm' - id 'net.corda.plugins.publish-utils' - id 'com.jfrog.artifactory' - id 'java-library' - id 'idea' -} -apply from: "${rootProject.projectDir}/deterministic.gradle" - -description 'Corda serialization (deterministic)' - -evaluationDependsOn(":serialization") - -// required by DJVM and Avian JVM (for running inside the SGX enclave) which only supports Java 8. -targetCompatibility = VERSION_1_8 - -def javaHome = System.getProperty('java.home') -def jarBaseName = "corda-${project.name}".toString() - -configurations { - deterministicLibraries { - canBeConsumed = false - extendsFrom implementation - } - deterministicArtifacts.extendsFrom deterministicLibraries -} - -dependencies { - compileOnly project(':serialization') - - // Configure these by hand. It should be a minimal subset of dependencies, - // and without any obviously non-deterministic ones such as Hibernate. - - // These dependencies will become "compile" scoped in our published POM. - // See publish.dependenciesFrom.defaultScope. - deterministicLibraries project(path: ':core-deterministic', configuration: 'deterministicArtifacts') - deterministicLibraries "org.apache.qpid:proton-j:$protonj_version" - - // These "implementation" dependencies will become "runtime" scoped in our published POM. - implementation "org.iq80.snappy:snappy:$snappy_version" - implementation "com.google.guava:guava:$guava_version" -} - -tasks.named('jar', Jar) { - archiveBaseName = 'DOES-NOT-EXIST' - // Don't build a jar here because it would be the wrong one. - // The jar we really want will be built by the metafix task. - enabled = false -} - -def serializationJarTask = project(':serialization').tasks.named('jar', Jar) -def originalJar = serializationJarTask.map { it.outputs.files.singleFile } - -def patchSerialization = tasks.register('patchSerialization', Zip) { - dependsOn serializationJarTask - destinationDirectory = layout.buildDirectory.dir('source-libs') - metadataCharset 'UTF-8' - archiveClassifier = 'transient' - archiveExtension = 'jar' - - from(compileKotlin) - from(processResources) - from(zipTree(originalJar)) { - exclude 'net/corda/serialization/internal/AttachmentsClassLoaderBuilder*' - exclude 'net/corda/serialization/internal/ByteBufferStreams*' - exclude 'net/corda/serialization/internal/DefaultWhitelist*' - exclude 'net/corda/serialization/internal/amqp/AMQPSerializerFactories*' - exclude 'net/corda/serialization/internal/amqp/AMQPStreams*' - exclude 'net/corda/serialization/internal/amqp/AMQPSerializationThreadContext*' - exclude 'net/corda/serialization/internal/model/DefaultCacheProvider*' - } - - reproducibleFileOrder = true - includeEmptyDirs = false -} - -def predeterminise = tasks.register('predeterminise', ProGuardTask) { - dependsOn project(':core-deterministic').tasks.named('assemble') - injars patchSerialization - outjars file("$buildDir/proguard/pre-deterministic-${project.version}.jar") - - if (JavaVersion.current().isJava9Compatible()) { - libraryjars "$javaHome/jmods" - } else { - libraryjars file("$javaHome/lib/rt.jar") - libraryjars file("$javaHome/lib/jce.jar") - libraryjars file("$javaHome/lib/ext/sunec.jar") - } - configurations.compileClasspath.forEach { - if (originalJar != it) { - libraryjars it, filter: '!META-INF/versions/**' - } - } - - keepattributes '*' - keepdirectories - dontpreverify - dontobfuscate - dontoptimize - dontnote - printseeds - verbose - - keep '@net.corda.core.KeepForDJVM class * { *; }', includedescriptorclasses:true - keepclassmembers 'class net.corda.serialization.** { public synthetic ; }' -} - -def jarFilter = tasks.register('jarFilter', JarFilterTask) { - jars predeterminise - annotations { - forDelete = [ - "net.corda.core.DeleteForDJVM" - ] - forStub = [ - "net.corda.core.StubOutForDJVM" - ] - forRemove = [ - "co.paralleluniverse.fibers.Suspendable" - ] - forSanitise = [ - "net.corda.core.DeleteForDJVM" - ] - } -} - -def determinise = tasks.register('determinise', ProGuardTask) { - injars jarFilter - outjars file("$buildDir/proguard/$jarBaseName-${project.version}.jar") - - if (JavaVersion.current().isJava9Compatible()) { - libraryjars "$javaHome/jmods" - } else { - libraryjars file("$javaHome/lib/rt.jar") - libraryjars file("$javaHome/lib/jce.jar") - } - configurations.deterministicLibraries.forEach { - libraryjars it, filter: '!META-INF/versions/**' - } - - // Analyse the JAR for dead code, and remove (some of) it. - optimizations 'code/removal/simple,code/removal/advanced' - printconfiguration - - keepattributes '*' - keepdirectories - dontobfuscate - dontnote - printseeds - verbose - - keep '@net.corda.core.KeepForDJVM class * { *; }', includedescriptorclasses:true - keepclassmembers 'class net.corda.serialization.** { public synthetic ; }' -} - -def checkDeterminism = tasks.register('checkDeterminism', ProGuardTask) - -def metafix = tasks.register('metafix', MetaFixerTask) { - outputDir = layout.buildDirectory.dir('libs') - jars determinise - suffix "" - - // Strip timestamps from the JAR to make it reproducible. - preserveTimestamps = false - finalizedBy checkDeterminism -} - -checkDeterminism.configure { - dependsOn jdkTask - injars metafix - - libraryjars deterministic_rt_jar - - configurations.deterministicLibraries.forEach { - libraryjars it, filter: '!META-INF/versions/**' - } - - keepattributes '*' - dontpreverify - dontobfuscate - dontoptimize - verbose - - keep 'class *' -} - -defaultTasks "determinise" -determinise.configure { - finalizedBy metafix -} -tasks.named('assemble') { - dependsOn checkDeterminism -} - -def deterministicJar = metafix.map { it.outputs.files.singleFile } -artifacts { - deterministicArtifacts deterministicJar - publish deterministicJar -} - -tasks.named('sourceJar', Jar) { - from 'README.md' - include 'README.md' -} - -tasks.named('javadocJar', Jar) { - from 'README.md' - include 'README.md' -} - -publish { - dependenciesFrom(configurations.deterministicArtifacts) { - defaultScope = 'compile' - } - name jarBaseName -} - -idea { - module { - if (project.hasProperty("deterministic_idea_sdk")) { - jdkName project.property("deterministic_idea_sdk") as String - } - } -} diff --git a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/ByteBufferStreams.kt b/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/ByteBufferStreams.kt deleted file mode 100644 index 4a93bcb5f8..0000000000 --- a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/ByteBufferStreams.kt +++ /dev/null @@ -1,15 +0,0 @@ -@file:JvmName("ByteBufferStreams") -package net.corda.serialization.internal - -const val DEFAULT_BYTEBUFFER_SIZE = 64 * 1024 - -/** - * Drop-in replacement for [byteArrayOutput] in the serialization module. - * This version does not use a [net.corda.core.internal.LazyPool]. - */ -internal fun byteArrayOutput(task: (ByteBufferOutputStream) -> T): ByteArray { - return ByteBufferOutputStream(DEFAULT_BYTEBUFFER_SIZE).let { underlying -> - task(underlying) - underlying.toByteArray() // Must happen after close, to allow ZIP footer to be written for example. - } -} diff --git a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt b/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt deleted file mode 100644 index b1435f6c2f..0000000000 --- a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.internal - -import net.corda.core.serialization.SerializationWhitelist - -/** - * The DJVM does not need whitelisting, by definition. - */ -object DefaultWhitelist : SerializationWhitelist { - override val whitelist: List> get() = emptyList() -} diff --git a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationThreadContext.kt b/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationThreadContext.kt deleted file mode 100644 index 723dda762d..0000000000 --- a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationThreadContext.kt +++ /dev/null @@ -1,6 +0,0 @@ -@file:JvmName("AMQPSerializationThreadContext") -package net.corda.serialization.internal.amqp - -fun getContextClassLoader(): ClassLoader { - return ClassLoader.getSystemClassLoader() -} diff --git a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializerFactories.kt b/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializerFactories.kt deleted file mode 100644 index 19a59bc9ba..0000000000 --- a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializerFactories.kt +++ /dev/null @@ -1,34 +0,0 @@ -@file:JvmName("AMQPSerializerFactories") -package net.corda.serialization.internal.amqp - -import net.corda.core.serialization.ClassWhitelist -import net.corda.core.serialization.SerializationContext -import net.corda.serialization.internal.carpenter.ClassCarpenter -import net.corda.serialization.internal.carpenter.Schema - -/** - * Creates a [SerializerFactoryFactory] suitable for the DJVM, - * i.e. one without a [ClassCarpenter] implementation. - */ -@Suppress("UNUSED") -fun createSerializerFactoryFactory(): SerializerFactoryFactory = DeterministicSerializerFactoryFactory() - -/** - * Creates a [ClassCarpenter] suitable for the DJVM, i.e. one that doesn't work. - */ -fun createClassCarpenter(context: SerializationContext): ClassCarpenter = DummyClassCarpenter(context.whitelist, context.deserializationClassLoader) - -private class DeterministicSerializerFactoryFactory : SerializerFactoryFactory { - override fun make(context: SerializationContext) = - SerializerFactoryBuilder.build( - whitelist = context.whitelist, - classCarpenter = createClassCarpenter(context)) -} - -private class DummyClassCarpenter( - override val whitelist: ClassWhitelist, - override val classloader: ClassLoader -) : ClassCarpenter { - override fun build(schema: Schema): Class<*> - = throw UnsupportedOperationException("ClassCarpentry not supported") -} \ No newline at end of file diff --git a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPStreams.kt b/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPStreams.kt deleted file mode 100644 index 1f4d9af591..0000000000 --- a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPStreams.kt +++ /dev/null @@ -1,40 +0,0 @@ -@file:JvmName("AMQPStreams") -package net.corda.serialization.internal.amqp - -import net.corda.serialization.internal.ByteBufferInputStream -import net.corda.serialization.internal.ByteBufferOutputStream -import net.corda.serialization.internal.DEFAULT_BYTEBUFFER_SIZE -import java.io.InputStream -import java.io.OutputStream -import java.nio.ByteBuffer - -/** - * Drop-in replacement for [InputStream.asByteBuffer] in the serialization module. - * This version does not use a [net.corda.core.internal.LazyPool]. - */ -fun InputStream.asByteBuffer(): ByteBuffer { - return if (this is ByteBufferInputStream) { - byteBuffer // BBIS has no other state, so this is perfectly safe. - } else { - ByteBuffer.wrap(ByteBufferOutputStream(DEFAULT_BYTEBUFFER_SIZE).let { - copyTo(it) - it.toByteArray() - }) - } -} - -/** - * Drop-in replacement for [OutputStream.alsoAsByteBuffer] in the serialization module. - * This version does not use a [net.corda.core.internal.LazyPool]. - */ -fun OutputStream.alsoAsByteBuffer(remaining: Int, task: (ByteBuffer) -> T): T { - return if (this is ByteBufferOutputStream) { - alsoAsByteBuffer(remaining, task) - } else { - ByteBufferOutputStream(DEFAULT_BYTEBUFFER_SIZE).let { - val result = it.alsoAsByteBuffer(remaining, task) - it.copyTo(this) - result - } - } -} diff --git a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/model/DefaultCacheProvider.kt b/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/model/DefaultCacheProvider.kt deleted file mode 100644 index 022c045e87..0000000000 --- a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/model/DefaultCacheProvider.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.corda.serialization.internal.model - -/** - * We can't have [ConcurrentHashMap]s in the DJVM, so it must supply its own version of this object which returns - * plain old [MutableMap]s instead. - */ -object DefaultCacheProvider { - fun createCache(): MutableMap = mutableMapOf() -} \ No newline at end of file diff --git a/serialization-deterministic/src/main/resources/META-INF/DJVM-preload b/serialization-deterministic/src/main/resources/META-INF/DJVM-preload deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/serialization-djvm/build.gradle b/serialization-djvm/build.gradle deleted file mode 100644 index 8e8870398e..0000000000 --- a/serialization-djvm/build.gradle +++ /dev/null @@ -1,83 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' - id 'net.corda.plugins.publish-utils' - id 'com.jfrog.artifactory' - id 'java-library' - id 'idea' -} - -// The DJVM only supports Java 8 byte-code, so the tests must -// be compiled for Java 8. The main artifact is only compiled -// for Java 8 because it belongs to "Open Core". -apply from: "${rootProject.projectDir}/java8.gradle" - -description 'Serialization support for the DJVM' - -configurations { - sandboxTesting { - canBeConsumed = false - } - jdkRt { - canBeConsumed = false - } -} - -dependencies { - api project(':core') - api project(':serialization') - api "net.corda.djvm:corda-djvm:$djvm_version" - api 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' - implementation 'org.jetbrains.kotlin:kotlin-reflect' - implementation(project(':serialization-djvm:deserializers')) { - transitive = false - } - - testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_jupiter_version" - testImplementation "org.junit.jupiter:junit-jupiter-params:$junit_jupiter_version" - testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit_jupiter_version" - - // Test utilities - testImplementation "org.assertj:assertj-core:$assertj_version" - testRuntimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" - jdkRt "net.corda:deterministic-rt:$deterministic_rt_version" - - // The DJVM will need this classpath to run the unit tests. - sandboxTesting files(sourceSets.getByName("test").output) - sandboxTesting project(':serialization-djvm:deserializers') - sandboxTesting project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts') - sandboxTesting "org.slf4j:slf4j-nop:$slf4j_version" -} - -jar { - archiveBaseName = 'corda-serialization-djvm' - archiveClassifier = '' - manifest { - attributes('Automatic-Module-Name': 'net.corda.serialization.djvm') - attributes('Sealed': true) - } -} - -tasks.withType(Javadoc).configureEach { - // We have no public or protected Java classes to document. - enabled = false -} - -tasks.withType(Test).configureEach { - useJUnitPlatform() - systemProperty 'deterministic-rt.path', configurations.jdkRt.asPath - systemProperty 'sandbox-libraries.path', configurations.sandboxTesting.asPath - - // Configure the host timezone to match the DJVM's. - systemProperty 'user.timezone', 'UTC' -} - -publish { - name jar.archiveBaseName -} - -idea { - module { - downloadJavadoc = true - downloadSources = true - } -} diff --git a/serialization-djvm/deserializers/build.gradle b/serialization-djvm/deserializers/build.gradle deleted file mode 100644 index d98513c8d5..0000000000 --- a/serialization-djvm/deserializers/build.gradle +++ /dev/null @@ -1,40 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' - id 'net.corda.plugins.publish-utils' - id 'com.jfrog.artifactory' - id 'java-library' - id 'idea' -} -apply from: "${rootProject.projectDir}/deterministic.gradle" - -description 'Deserializers for the DJVM' - -dependencies { - api project(path: ':core-deterministic', configuration: 'deterministicArtifacts') - api project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts') - api 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' -} - -jar { - archiveBaseName = 'corda-deserializers-djvm' - archiveClassifier = '' - manifest { - attributes('Automatic-Module-Name': 'net.corda.serialization.djvm.deserializers') - attributes('Sealed': true) - } -} - -publish { - name jar.archiveBaseName.get() -} - -idea { - module { - downloadJavadoc = true - downloadSources = true - - if (project.hasProperty("deterministic_idea_sdk")) { - jdkName project.property("deterministic_idea_sdk") as String - } - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/BitSetDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/BitSetDeserializer.kt deleted file mode 100644 index 718018e8b5..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/BitSetDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.BitSetSerializer.BitSetProxy -import java.util.BitSet -import java.util.function.Function - -class BitSetDeserializer : Function { - override fun apply(proxy: BitSetProxy): BitSet { - return BitSet.valueOf(proxy.bytes) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CertPathDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CertPathDeserializer.kt deleted file mode 100644 index 04d03a2109..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CertPathDeserializer.kt +++ /dev/null @@ -1,13 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.CertPathSerializer.CertPathProxy -import java.security.cert.CertPath -import java.security.cert.CertificateFactory -import java.util.function.Function - -class CertPathDeserializer : Function { - override fun apply(proxy: CertPathProxy): CertPath { - val factory = CertificateFactory.getInstance(proxy.type) - return factory.generateCertPath(proxy.encoded.inputStream()) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CheckEnum.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CheckEnum.kt deleted file mode 100644 index 5734e1b3ba..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CheckEnum.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import java.util.function.Predicate - -class CheckEnum : Predicate> { - override fun test(clazz: Class<*>): Boolean { - return clazz.isEnum - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/ClassDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/ClassDeserializer.kt deleted file mode 100644 index 6f8c03a33c..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/ClassDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.ClassSerializer.ClassProxy - -import java.util.function.Function - -class ClassDeserializer : Function> { - override fun apply(proxy: ClassProxy): Class<*> { - return Class.forName(proxy.className) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CorDappCustomDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CorDappCustomDeserializer.kt deleted file mode 100644 index e32ef4d52c..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CorDappCustomDeserializer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.core.serialization.SerializationCustomSerializer -import java.util.function.Function - -class CorDappCustomDeserializer(private val serializer: SerializationCustomSerializer) : Function { - override fun apply(input: Any?): Any? { - return serializer.fromProxy(input) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateCollection.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateCollection.kt deleted file mode 100644 index be69a73970..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateCollection.kt +++ /dev/null @@ -1,54 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.core.utilities.NonEmptySet -import java.util.Collections.unmodifiableCollection -import java.util.Collections.unmodifiableList -import java.util.Collections.unmodifiableNavigableSet -import java.util.Collections.unmodifiableSet -import java.util.Collections.unmodifiableSortedSet -import java.util.NavigableSet -import java.util.SortedSet -import java.util.TreeSet -import java.util.function.Function - -class CreateCollection : Function, Collection> { - private val concreteConstructors: Map>, (Array) -> Collection> = mapOf( - List::class.java to ::createList, - Set::class.java to ::createSet, - SortedSet::class.java to ::createSortedSet, - NavigableSet::class.java to ::createNavigableSet, - Collection::class.java to ::createCollection, - NonEmptySet::class.java to ::createNonEmptySet - ) - - private fun createList(values: Array): List { - return unmodifiableList(values.toCollection(ArrayList())) - } - - private fun createSet(values: Array): Set { - return unmodifiableSet(values.toCollection(LinkedHashSet())) - } - - private fun createSortedSet(values: Array): SortedSet { - return unmodifiableSortedSet(values.toCollection(TreeSet())) - } - - private fun createNavigableSet(values: Array): NavigableSet { - return unmodifiableNavigableSet(values.toCollection(TreeSet())) - } - - private fun createCollection(values: Array): Collection { - return unmodifiableCollection(values.toCollection(ArrayList())) - } - - private fun createNonEmptySet(values: Array): NonEmptySet { - return NonEmptySet.copyOf(values.toCollection(ArrayList())) - } - - @Suppress("unchecked_cast") - override fun apply(inputs: Array): Collection { - val collectionClass = inputs[0] as Class> - val args = inputs[1] as Array - return concreteConstructors[collectionClass]?.invoke(args)!! - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateCurrency.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateCurrency.kt deleted file mode 100644 index 0fc21d8750..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateCurrency.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import java.util.Currency -import java.util.function.Function - -class CreateCurrency : Function { - override fun apply(currencyCode: String): Currency { - return Currency.getInstance(currencyCode) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateMap.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateMap.kt deleted file mode 100644 index 6759bc8d2c..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateMap.kt +++ /dev/null @@ -1,54 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import java.util.Collections.unmodifiableMap -import java.util.Collections.unmodifiableNavigableMap -import java.util.Collections.unmodifiableSortedMap -import java.util.EnumMap -import java.util.NavigableMap -import java.util.SortedMap -import java.util.TreeMap -import java.util.function.Function - -class CreateMap : Function, Map> { - private val concreteConstructors: Map>, (Array>) -> Map> = mapOf( - Map::class.java to ::createMap, - SortedMap::class.java to ::createSortedMap, - LinkedHashMap::class.java to ::createLinkedHashMap, - NavigableMap::class.java to ::createNavigableMap, - TreeMap::class.java to ::createTreeMap, - EnumMap::class.java to ::createEnumMap - ) - - private fun createMap(values: Array>): Map { - return unmodifiableMap(values.associate { it[0] to it[1] }) - } - - private fun createSortedMap(values: Array>): SortedMap { - return unmodifiableSortedMap(createTreeMap(values)) - } - - private fun createNavigableMap(values: Array>): NavigableMap { - return unmodifiableNavigableMap(createTreeMap(values)) - } - - private fun createLinkedHashMap(values: Array>): LinkedHashMap { - return values.associateTo(LinkedHashMap()) { it[0] to it[1] } - } - - private fun createTreeMap(values: Array>): TreeMap { - return values.associateTo(TreeMap()) { it[0] to it[1] } - } - - private fun createEnumMap(values: Array>): Map { - val map = values.associate { it[0] to it[1] } - @Suppress("unchecked_cast") - return EnumMap(map as Map) as Map - } - - @Suppress("unchecked_cast") - override fun apply(inputs: Array): Map { - val mapClass = inputs[0] as Class> - val args = inputs[1] as Array> - return concreteConstructors[mapClass]?.invoke(args)!! - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/Decimal128Deserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/Decimal128Deserializer.kt deleted file mode 100644 index 6fec4a512f..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/Decimal128Deserializer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import org.apache.qpid.proton.amqp.Decimal128 -import java.util.function.Function - -class Decimal128Deserializer : Function { - override fun apply(underlying: LongArray): Decimal128 { - return Decimal128(underlying[0], underlying[1]) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/Decimal32Deserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/Decimal32Deserializer.kt deleted file mode 100644 index cdf0ac17d9..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/Decimal32Deserializer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import org.apache.qpid.proton.amqp.Decimal32 -import java.util.function.Function - -class Decimal32Deserializer : Function { - override fun apply(underlying: IntArray): Decimal32 { - return Decimal32(underlying[0]) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/Decimal64Deserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/Decimal64Deserializer.kt deleted file mode 100644 index b481278218..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/Decimal64Deserializer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import org.apache.qpid.proton.amqp.Decimal64 -import java.util.function.Function - -class Decimal64Deserializer : Function { - override fun apply(underlying: LongArray): Decimal64 { - return Decimal64(underlying[0]) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/DescribeEnum.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/DescribeEnum.kt deleted file mode 100644 index 4e0303f897..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/DescribeEnum.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import java.util.function.Function - -class DescribeEnum : Function, Array> { - override fun apply(enumClass: Class<*>): Array { - return enumClass.enumConstants - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/DurationDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/DurationDeserializer.kt deleted file mode 100644 index e75d197a76..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/DurationDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.DurationSerializer.DurationProxy -import java.time.Duration -import java.util.function.Function - -class DurationDeserializer : Function { - override fun apply(proxy: DurationProxy): Duration { - return Duration.ofSeconds(proxy.seconds, proxy.nanos.toLong()) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/EnumSetDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/EnumSetDeserializer.kt deleted file mode 100644 index 4020c3cd3b..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/EnumSetDeserializer.kt +++ /dev/null @@ -1,16 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.core.internal.uncheckedCast -import net.corda.serialization.internal.amqp.custom.EnumSetSerializer.EnumSetProxy -import java.util.EnumSet -import java.util.function.Function - -class EnumSetDeserializer : Function> { - override fun apply(proxy: EnumSetProxy): EnumSet<*> { - return if (proxy.elements.isEmpty()) { - EnumSet.noneOf(uncheckedCast, Class>(proxy.clazz)) - } else { - EnumSet.copyOf(uncheckedCast, List>(proxy.elements)) - } - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/GetEnumNames.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/GetEnumNames.kt deleted file mode 100644 index 5e60f530b4..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/GetEnumNames.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import java.util.function.Function - -class GetEnumNames : Function>, Array> { - override fun apply(enumValues: Array>): Array { - return enumValues.map(Enum<*>::name).toTypedArray() - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/InputStreamDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/InputStreamDeserializer.kt deleted file mode 100644 index 1aabb9fbe4..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/InputStreamDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import java.io.ByteArrayInputStream -import java.io.InputStream -import java.util.function.Function - -class InputStreamDeserializer : Function { - override fun apply(bytes: ByteArray): InputStream? { - return ByteArrayInputStream(bytes) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/InstantDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/InstantDeserializer.kt deleted file mode 100644 index bd5e61810b..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/InstantDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.InstantSerializer.InstantProxy -import java.time.Instant -import java.util.function.Function - -class InstantDeserializer : Function { - override fun apply(proxy: InstantProxy): Instant { - return Instant.ofEpochSecond(proxy.epochSeconds, proxy.nanos.toLong()) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/JustForCasting.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/JustForCasting.kt deleted file mode 100644 index 2eb1b07acf..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/JustForCasting.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -@Suppress("unused") -enum class JustForCasting { - UNUSED -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/LocalDateDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/LocalDateDeserializer.kt deleted file mode 100644 index 5da4fb3b83..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/LocalDateDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.LocalDateSerializer.LocalDateProxy -import java.time.LocalDate -import java.util.function.Function - -class LocalDateDeserializer : Function { - override fun apply(proxy: LocalDateProxy): LocalDate { - return LocalDate.of(proxy.year, proxy.month.toInt(), proxy.day.toInt()) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/LocalDateTimeDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/LocalDateTimeDeserializer.kt deleted file mode 100644 index 2123dc81e4..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/LocalDateTimeDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.LocalDateTimeSerializer.LocalDateTimeProxy -import java.time.LocalDateTime -import java.util.function.Function - -class LocalDateTimeDeserializer : Function { - override fun apply(proxy: LocalDateTimeProxy): LocalDateTime { - return LocalDateTime.of(proxy.date, proxy.time) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/LocalTimeDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/LocalTimeDeserializer.kt deleted file mode 100644 index e0d7c76d2a..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/LocalTimeDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.LocalTimeSerializer.LocalTimeProxy -import java.time.LocalTime -import java.util.function.Function - -class LocalTimeDeserializer : Function { - override fun apply(proxy: LocalTimeProxy): LocalTime { - return LocalTime.of(proxy.hour.toInt(), proxy.minute.toInt(), proxy.second.toInt(), proxy.nano) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/MergeWhitelists.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/MergeWhitelists.kt deleted file mode 100644 index b603aca408..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/MergeWhitelists.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.core.serialization.SerializationWhitelist -import java.util.function.Function - -class MergeWhitelists : Function, Array>> { - override fun apply(whitelists: Array): Array> { - return whitelists.flatMapTo(LinkedHashSet(), SerializationWhitelist::whitelist).toTypedArray() - } -} \ No newline at end of file diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/MonthDayDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/MonthDayDeserializer.kt deleted file mode 100644 index 59195d0729..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/MonthDayDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.MonthDaySerializer.MonthDayProxy -import java.time.MonthDay -import java.util.function.Function - -class MonthDayDeserializer : Function { - override fun apply(proxy: MonthDayProxy): MonthDay { - return MonthDay.of(proxy.month.toInt(), proxy.day.toInt()) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OffsetDateTimeDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OffsetDateTimeDeserializer.kt deleted file mode 100644 index 275ddd8fa9..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OffsetDateTimeDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.OffsetDateTimeSerializer.OffsetDateTimeProxy -import java.time.OffsetDateTime -import java.util.function.Function - -class OffsetDateTimeDeserializer : Function { - override fun apply(proxy: OffsetDateTimeProxy): OffsetDateTime { - return OffsetDateTime.of(proxy.dateTime, proxy.offset) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OffsetTimeDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OffsetTimeDeserializer.kt deleted file mode 100644 index 550c565355..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OffsetTimeDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.OffsetTimeSerializer.OffsetTimeProxy -import java.time.OffsetTime -import java.util.function.Function - -class OffsetTimeDeserializer : Function { - override fun apply(proxy: OffsetTimeProxy): OffsetTime { - return OffsetTime.of(proxy.time, proxy.offset) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OpaqueBytesSubSequenceDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OpaqueBytesSubSequenceDeserializer.kt deleted file mode 100644 index 9755d1dae0..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OpaqueBytesSubSequenceDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.OpaqueBytesSubSequence -import java.util.function.Function - -class OpaqueBytesSubSequenceDeserializer : Function { - override fun apply(proxy: OpaqueBytes): OpaqueBytesSubSequence { - return OpaqueBytesSubSequence(proxy.bytes, proxy.offset, proxy.size) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OptionalDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OptionalDeserializer.kt deleted file mode 100644 index 0d8d511952..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OptionalDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.OptionalSerializer.OptionalProxy -import java.util.Optional -import java.util.function.Function - -class OptionalDeserializer : Function> { - override fun apply(proxy: OptionalProxy): Optional { - return Optional.ofNullable(proxy.item) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/PeriodDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/PeriodDeserializer.kt deleted file mode 100644 index 25e6381455..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/PeriodDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.PeriodSerializer.PeriodProxy -import java.time.Period -import java.util.function.Function - -class PeriodDeserializer : Function { - override fun apply(proxy: PeriodProxy): Period { - return Period.of(proxy.years, proxy.months, proxy.days) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/PublicKeyDecoder.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/PublicKeyDecoder.kt deleted file mode 100644 index 1be04bd88a..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/PublicKeyDecoder.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.core.crypto.Crypto -import java.security.PublicKey -import java.util.function.Function - -class PublicKeyDecoder : Function { - override fun apply(encoded: ByteArray): PublicKey { - return Crypto.decodePublicKey(encoded) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/SymbolDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/SymbolDeserializer.kt deleted file mode 100644 index 83f058b231..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/SymbolDeserializer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import org.apache.qpid.proton.amqp.Symbol -import java.util.function.Function - -class SymbolDeserializer : Function { - override fun apply(value: String): Symbol { - return Symbol.valueOf(value) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedByteDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedByteDeserializer.kt deleted file mode 100644 index facf725c4a..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedByteDeserializer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import org.apache.qpid.proton.amqp.UnsignedByte -import java.util.function.Function - -class UnsignedByteDeserializer : Function { - override fun apply(underlying: ByteArray): UnsignedByte { - return UnsignedByte(underlying[0]) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedIntegerDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedIntegerDeserializer.kt deleted file mode 100644 index 69732c58ff..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedIntegerDeserializer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import org.apache.qpid.proton.amqp.UnsignedInteger -import java.util.function.Function - -class UnsignedIntegerDeserializer : Function { - override fun apply(underlying: IntArray): UnsignedInteger { - return UnsignedInteger(underlying[0]) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedLongDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedLongDeserializer.kt deleted file mode 100644 index b628b033e8..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedLongDeserializer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import org.apache.qpid.proton.amqp.UnsignedLong -import java.util.function.Function - -class UnsignedLongDeserializer : Function { - override fun apply(underlying: LongArray): UnsignedLong { - return UnsignedLong(underlying[0]) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedShortDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedShortDeserializer.kt deleted file mode 100644 index 5251225f21..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/UnsignedShortDeserializer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import org.apache.qpid.proton.amqp.UnsignedShort -import java.util.function.Function - -class UnsignedShortDeserializer : Function { - override fun apply(underlying: ShortArray): UnsignedShort { - return UnsignedShort(underlying[0]) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/X509CRLDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/X509CRLDeserializer.kt deleted file mode 100644 index 81309fcfbf..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/X509CRLDeserializer.kt +++ /dev/null @@ -1,12 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import java.security.cert.CertificateFactory -import java.security.cert.X509CRL -import java.util.function.Function - -class X509CRLDeserializer : Function { - override fun apply(bytes: ByteArray): X509CRL { - val factory = CertificateFactory.getInstance("X.509") - return factory.generateCRL(bytes.inputStream()) as X509CRL - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/X509CertificateDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/X509CertificateDeserializer.kt deleted file mode 100644 index 9f3423eaed..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/X509CertificateDeserializer.kt +++ /dev/null @@ -1,12 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import java.security.cert.CertificateFactory -import java.security.cert.X509Certificate -import java.util.function.Function - -class X509CertificateDeserializer : Function { - override fun apply(bits: ByteArray): X509Certificate { - val factory = CertificateFactory.getInstance("X.509") - return factory.generateCertificate(bits.inputStream()) as X509Certificate - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/YearDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/YearDeserializer.kt deleted file mode 100644 index 2787e587a2..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/YearDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.YearSerializer.YearProxy -import java.time.Year -import java.util.function.Function - -class YearDeserializer : Function { - override fun apply(proxy: YearProxy): Year { - return Year.of(proxy.year) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/YearMonthDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/YearMonthDeserializer.kt deleted file mode 100644 index 6876aa4082..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/YearMonthDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.YearMonthSerializer.YearMonthProxy -import java.time.YearMonth -import java.util.function.Function - -class YearMonthDeserializer : Function { - override fun apply(proxy: YearMonthProxy): YearMonth { - return YearMonth.of(proxy.year, proxy.month.toInt()) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/ZoneIdDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/ZoneIdDeserializer.kt deleted file mode 100644 index c28dbcab24..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/ZoneIdDeserializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.ZoneIdSerializer.ZoneIdProxy -import java.time.ZoneId -import java.util.function.Function - -class ZoneIdDeserializer : Function { - override fun apply(proxy: ZoneIdProxy): ZoneId { - return ZoneId.of(proxy.id) - } -} diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/ZonedDateTimeDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/ZonedDateTimeDeserializer.kt deleted file mode 100644 index e1cf9485ee..0000000000 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/ZonedDateTimeDeserializer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm.deserializers - -import net.corda.serialization.internal.amqp.custom.ZonedDateTimeSerializer.ZonedDateTimeProxy -import java.util.function.Function - -class ZonedDateTimeDeserializer : Function?> { - override fun apply(proxy: ZonedDateTimeProxy): Array? { - return arrayOf(proxy.dateTime, proxy.offset, proxy.zone) - } -} diff --git a/serialization-djvm/deserializers/src/main/resources/META-INF/DJVM-preload b/serialization-djvm/deserializers/src/main/resources/META-INF/DJVM-preload deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/serialization-djvm/src/main/java/net/corda/serialization/djvm/serializers/CacheKey.java b/serialization-djvm/src/main/java/net/corda/serialization/djvm/serializers/CacheKey.java deleted file mode 100644 index 5ef3728e91..0000000000 --- a/serialization-djvm/src/main/java/net/corda/serialization/djvm/serializers/CacheKey.java +++ /dev/null @@ -1,35 +0,0 @@ -package net.corda.serialization.djvm.serializers; - -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; - -/** - * This class is deliberately written in Java so - * that it can be package private. - */ -final class CacheKey { - private final byte[] bytes; - private final int hashValue; - - CacheKey(@NotNull byte[] bytes) { - this.bytes = bytes; - this.hashValue = Arrays.hashCode(bytes); - } - - @NotNull - byte[] getBytes() { - return bytes; - } - - @Override - public boolean equals(Object other) { - return (this == other) - || (other instanceof CacheKey && Arrays.equals(bytes, ((CacheKey) other).bytes)); - } - - @Override - public int hashCode() { - return hashValue; - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/AMQPSerializationScheme.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/AMQPSerializationScheme.kt deleted file mode 100644 index 91250fee68..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/AMQPSerializationScheme.kt +++ /dev/null @@ -1,34 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.SerializationContext.UseCase -import net.corda.core.serialization.SerializedBytes -import net.corda.core.utilities.ByteSequence -import net.corda.serialization.internal.CordaSerializationMagic -import net.corda.serialization.internal.SerializationScheme -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.amqpMagic - -/** - * This is an ephemeral [SerializationScheme] that will only ever - * support a single [SerializerFactory]. The [ClassLoader] that - * underpins everything this scheme is deserializing is not expected - * to be long-lived either. - */ -class AMQPSerializationScheme( - val serializerFactory: SerializerFactory -) : SerializationScheme { - override fun deserialize(byteSequence: ByteSequence, clazz: Class, context: SerializationContext): T { - return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz, context) - } - - override fun serialize(obj: T, context: SerializationContext): SerializedBytes { - return SerializationOutput(serializerFactory).serialize(obj, context) - } - - override fun canDeserializeVersion(magic: CordaSerializationMagic, target: UseCase): Boolean { - return magic == amqpMagic && target == UseCase.P2P - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/DelegatingClassLoader.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/DelegatingClassLoader.kt deleted file mode 100644 index ce916bd2ff..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/DelegatingClassLoader.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.djvm.rewiring.SandboxClassLoader - -class DelegatingClassLoader(private val delegate: SandboxClassLoader) : ClassLoader(null) { - @Throws(ClassNotFoundException::class) - override fun loadClass(name: String, resolve: Boolean): Class<*> { - return delegate.toSandboxClass(name) - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/SandboxSerializationSchemeBuilder.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/SandboxSerializationSchemeBuilder.kt deleted file mode 100644 index d0406e2e4f..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/SandboxSerializationSchemeBuilder.kt +++ /dev/null @@ -1,150 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.internal.objectOrNewInstance -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.SerializationWhitelist -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.MergeWhitelists -import net.corda.serialization.djvm.serializers.SandboxBitSetSerializer -import net.corda.serialization.djvm.serializers.SandboxCertPathSerializer -import net.corda.serialization.djvm.serializers.SandboxCharacterSerializer -import net.corda.serialization.djvm.serializers.SandboxCollectionSerializer -import net.corda.serialization.djvm.serializers.SandboxCorDappCustomSerializer -import net.corda.serialization.djvm.serializers.SandboxCurrencySerializer -import net.corda.serialization.djvm.serializers.SandboxDecimal128Serializer -import net.corda.serialization.djvm.serializers.SandboxDecimal32Serializer -import net.corda.serialization.djvm.serializers.SandboxDecimal64Serializer -import net.corda.serialization.djvm.serializers.SandboxDurationSerializer -import net.corda.serialization.djvm.serializers.SandboxEnumSerializer -import net.corda.serialization.djvm.serializers.SandboxEnumSetSerializer -import net.corda.serialization.djvm.serializers.SandboxInputStreamSerializer -import net.corda.serialization.djvm.serializers.SandboxInstantSerializer -import net.corda.serialization.djvm.serializers.SandboxLocalDateSerializer -import net.corda.serialization.djvm.serializers.SandboxLocalDateTimeSerializer -import net.corda.serialization.djvm.serializers.SandboxLocalTimeSerializer -import net.corda.serialization.djvm.serializers.SandboxMapSerializer -import net.corda.serialization.djvm.serializers.SandboxMonthDaySerializer -import net.corda.serialization.djvm.serializers.SandboxOffsetDateTimeSerializer -import net.corda.serialization.djvm.serializers.SandboxOffsetTimeSerializer -import net.corda.serialization.djvm.serializers.SandboxOpaqueBytesSubSequenceSerializer -import net.corda.serialization.djvm.serializers.SandboxOptionalSerializer -import net.corda.serialization.djvm.serializers.SandboxPeriodSerializer -import net.corda.serialization.djvm.serializers.SandboxPrimitiveSerializer -import net.corda.serialization.djvm.serializers.SandboxPublicKeySerializer -import net.corda.serialization.djvm.serializers.SandboxSymbolSerializer -import net.corda.serialization.djvm.serializers.SandboxToStringSerializer -import net.corda.serialization.djvm.serializers.SandboxUnsignedByteSerializer -import net.corda.serialization.djvm.serializers.SandboxUnsignedIntegerSerializer -import net.corda.serialization.djvm.serializers.SandboxUnsignedLongSerializer -import net.corda.serialization.djvm.serializers.SandboxUnsignedShortSerializer -import net.corda.serialization.djvm.serializers.SandboxX509CRLSerializer -import net.corda.serialization.djvm.serializers.SandboxX509CertificateSerializer -import net.corda.serialization.djvm.serializers.SandboxYearMonthSerializer -import net.corda.serialization.djvm.serializers.SandboxYearSerializer -import net.corda.serialization.djvm.serializers.SandboxZoneIdSerializer -import net.corda.serialization.djvm.serializers.SandboxZonedDateTimeSerializer -import net.corda.serialization.internal.SerializationScheme -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.SerializerFactoryFactory -import net.corda.serialization.internal.amqp.addToWhitelist -import java.math.BigDecimal -import java.math.BigInteger -import java.util.Date -import java.util.UUID -import java.util.function.Function -import java.util.function.Predicate - -class SandboxSerializationSchemeBuilder( - private val classLoader: SandboxClassLoader, - private val sandboxBasicInput: Function, - private val rawTaskFactory: Function>, - private val taskFactory: Function>, out Function>, - private val predicateFactory: Function>, out Predicate>, - private val customSerializerClassNames: Set, - private val serializationWhitelistNames: Set, - private val serializerFactoryFactory: SerializerFactoryFactory -) { - fun buildFor(context: SerializationContext): SerializationScheme { - return AMQPSerializationScheme(getSerializerFactory(context)) - } - - private fun getSerializerFactory(context: SerializationContext): SerializerFactory { - return serializerFactoryFactory.make(context).apply { - register(SandboxBitSetSerializer(classLoader, taskFactory, this)) - register(SandboxCertPathSerializer(classLoader, taskFactory, this)) - register(SandboxDurationSerializer(classLoader, taskFactory, this)) - register(SandboxEnumSetSerializer(classLoader, taskFactory, this)) - register(SandboxInputStreamSerializer(classLoader, taskFactory)) - register(SandboxInstantSerializer(classLoader, taskFactory, this)) - register(SandboxLocalDateSerializer(classLoader, taskFactory, this)) - register(SandboxLocalDateTimeSerializer(classLoader, taskFactory, this)) - register(SandboxLocalTimeSerializer(classLoader, taskFactory, this)) - register(SandboxMonthDaySerializer(classLoader, taskFactory, this)) - register(SandboxOffsetDateTimeSerializer(classLoader, taskFactory, this)) - register(SandboxOffsetTimeSerializer(classLoader, taskFactory, this)) - register(SandboxPeriodSerializer(classLoader, taskFactory, this)) - register(SandboxYearMonthSerializer(classLoader, taskFactory, this)) - register(SandboxYearSerializer(classLoader, taskFactory, this)) - register(SandboxZonedDateTimeSerializer(classLoader, taskFactory, this)) - register(SandboxZoneIdSerializer(classLoader, taskFactory, this)) - register(SandboxOpaqueBytesSubSequenceSerializer(classLoader, taskFactory, this)) - register(SandboxOptionalSerializer(classLoader, taskFactory, this)) - register(SandboxPrimitiveSerializer(UUID::class.java, classLoader, sandboxBasicInput)) - register(SandboxPrimitiveSerializer(String::class.java, classLoader, sandboxBasicInput)) - register(SandboxPrimitiveSerializer(Byte::class.javaObjectType, classLoader, sandboxBasicInput)) - register(SandboxPrimitiveSerializer(Short::class.javaObjectType, classLoader, sandboxBasicInput)) - register(SandboxPrimitiveSerializer(Int::class.javaObjectType, classLoader, sandboxBasicInput)) - register(SandboxPrimitiveSerializer(Long::class.javaObjectType, classLoader, sandboxBasicInput)) - register(SandboxPrimitiveSerializer(Float::class.javaObjectType, classLoader, sandboxBasicInput)) - register(SandboxPrimitiveSerializer(Double::class.javaObjectType, classLoader, sandboxBasicInput)) - register(SandboxPrimitiveSerializer(Boolean::class.javaObjectType, classLoader, sandboxBasicInput)) - register(SandboxPrimitiveSerializer(Date::class.javaObjectType, classLoader, sandboxBasicInput)) - register(SandboxCharacterSerializer(classLoader, sandboxBasicInput)) - register(SandboxCollectionSerializer(classLoader, taskFactory, this)) - register(SandboxMapSerializer(classLoader, taskFactory, this)) - register(SandboxEnumSerializer(classLoader, taskFactory, predicateFactory, this)) - register(SandboxPublicKeySerializer(classLoader, taskFactory)) - register(SandboxToStringSerializer(BigDecimal::class.java, classLoader, sandboxBasicInput)) - register(SandboxToStringSerializer(BigInteger::class.java, classLoader, sandboxBasicInput)) - register(SandboxToStringSerializer(StringBuffer::class.java, classLoader, sandboxBasicInput)) - register(SandboxCurrencySerializer(classLoader, taskFactory, sandboxBasicInput)) - register(SandboxX509CertificateSerializer(classLoader, taskFactory)) - register(SandboxX509CRLSerializer(classLoader, taskFactory)) - register(SandboxUnsignedLongSerializer(classLoader, taskFactory)) - register(SandboxUnsignedIntegerSerializer(classLoader, taskFactory)) - register(SandboxUnsignedShortSerializer(classLoader, taskFactory)) - register(SandboxUnsignedByteSerializer(classLoader, taskFactory)) - register(SandboxDecimal128Serializer(classLoader, taskFactory)) - register(SandboxDecimal64Serializer(classLoader, taskFactory)) - register(SandboxDecimal32Serializer(classLoader, taskFactory)) - register(SandboxSymbolSerializer(classLoader, taskFactory, sandboxBasicInput)) - - for (customSerializerName in customSerializerClassNames) { - register(SandboxCorDappCustomSerializer(customSerializerName, classLoader, rawTaskFactory, this)) - } - registerWhitelists(taskFactory, this) - } - } - - private fun registerWhitelists( - taskFactory: Function>, out Function>, - factory: SerializerFactory - ) { - if (serializationWhitelistNames.isEmpty()) { - return - } - - val serializationWhitelists = serializationWhitelistNames.map { whitelistClass -> - classLoader.toSandboxClass(whitelistClass).kotlin.objectOrNewInstance() - }.toArrayOf(classLoader.toSandboxClass(SerializationWhitelist::class.java)) - @Suppress("unchecked_cast") - val mergeTask = taskFactory.apply(MergeWhitelists::class.java) as Function, out Array>> - factory.addToWhitelist(mergeTask.apply(serializationWhitelists).toSet()) - } - - @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") - private fun Collection<*>.toArrayOf(type: Class<*>): Array<*> { - val typedArray = java.lang.reflect.Array.newInstance(type, 0) as Array<*> - return (this as java.util.Collection<*>).toArray(typedArray) - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/SandboxSerializerFactoryFactory.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/SandboxSerializerFactoryFactory.kt deleted file mode 100644 index 96f8c44a03..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/SandboxSerializerFactoryFactory.kt +++ /dev/null @@ -1,116 +0,0 @@ -@file:Suppress("platform_class_mapped_to_kotlin") -package net.corda.serialization.djvm - -import net.corda.core.serialization.SerializationContext -import net.corda.serialization.internal.amqp.AMQPRemoteTypeModel -import net.corda.serialization.internal.amqp.AMQPSerializer -import net.corda.serialization.internal.amqp.CachingCustomSerializerRegistry -import net.corda.serialization.internal.amqp.ComposedSerializerFactory -import net.corda.serialization.internal.amqp.DefaultDescriptorBasedSerializerRegistry -import net.corda.serialization.internal.amqp.DefaultEvolutionSerializerFactory -import net.corda.serialization.internal.amqp.DefaultLocalSerializerFactory -import net.corda.serialization.internal.amqp.DefaultRemoteSerializerFactory -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.SerializerFactoryFactory -import net.corda.serialization.internal.amqp.WhitelistBasedTypeModelConfiguration -import net.corda.serialization.internal.amqp.createClassCarpenter -import net.corda.serialization.internal.model.BaseLocalTypes -import net.corda.serialization.internal.model.ClassCarpentingTypeLoader -import net.corda.serialization.internal.model.ConfigurableLocalTypeModel -import net.corda.serialization.internal.model.SchemaBuildingRemoteTypeCarpenter -import net.corda.serialization.internal.amqp.SerializerFactoryBuilder -import net.corda.serialization.internal.model.TypeLoader -import net.corda.serialization.internal.model.TypeModellingFingerPrinter -import java.lang.Boolean -import java.lang.Byte -import java.lang.Double -import java.lang.Float -import java.lang.Long -import java.lang.Short -import java.util.Collections.singleton -import java.util.Collections.unmodifiableMap -import java.util.Date -import java.util.UUID -import java.util.function.Function -import java.util.function.Predicate - -/** - * This has all been lovingly copied from [SerializerFactoryBuilder]. - */ -class SandboxSerializerFactoryFactory( - private val primitiveSerializerFactory: Function, AMQPSerializer>, - private val localTypes: BaseLocalTypes -) : SerializerFactoryFactory { - - override fun make(context: SerializationContext): SerializerFactory { - val classLoader = context.deserializationClassLoader - - val primitiveTypes = unmodifiableMap(mapOf, Class<*>>( - classLoader.loadClass("sandbox.java.lang.Boolean") to Boolean.TYPE, - classLoader.loadClass("sandbox.java.lang.Byte") to Byte.TYPE, - classLoader.loadClass("sandbox.java.lang.Character") to Character.TYPE, - classLoader.loadClass("sandbox.java.lang.Double") to Double.TYPE, - classLoader.loadClass("sandbox.java.lang.Float") to Float.TYPE, - classLoader.loadClass("sandbox.java.lang.Integer") to Integer.TYPE, - classLoader.loadClass("sandbox.java.lang.Long") to Long.TYPE, - classLoader.loadClass("sandbox.java.lang.Short") to Short.TYPE, - classLoader.loadClass("sandbox.java.lang.String") to String::class.java, - classLoader.loadClass("sandbox.java.util.Date") to Date::class.java, - classLoader.loadClass("sandbox.java.util.UUID") to UUID::class.java, - Void::class.java to Void.TYPE - )) - - val classCarpenter = createClassCarpenter(context) - val descriptorBasedSerializerRegistry = DefaultDescriptorBasedSerializerRegistry() - val customSerializerRegistry = CachingCustomSerializerRegistry( - descriptorBasedSerializerRegistry = descriptorBasedSerializerRegistry, - allowedFor = singleton(classLoader.loadClass("sandbox.java.lang.Object")) - ) - - val localTypeModel = ConfigurableLocalTypeModel( - WhitelistBasedTypeModelConfiguration( - whitelist = context.whitelist, - customSerializerRegistry = customSerializerRegistry, - baseTypes = localTypes - ) - ) - - val fingerPrinter = TypeModellingFingerPrinter(customSerializerRegistry, classLoader) - - val localSerializerFactory = DefaultLocalSerializerFactory( - whitelist = context.whitelist, - typeModel = localTypeModel, - fingerPrinter = fingerPrinter, - classloader = classLoader, - descriptorBasedSerializerRegistry = descriptorBasedSerializerRegistry, - primitiveSerializerFactory = primitiveSerializerFactory, - isPrimitiveType = Predicate { clazz -> clazz.isPrimitive || clazz in primitiveTypes.keys }, - customSerializerRegistry = customSerializerRegistry, - onlyCustomSerializers = false - ) - - val typeLoader: TypeLoader = ClassCarpentingTypeLoader( - carpenter = SchemaBuildingRemoteTypeCarpenter(classCarpenter), - classLoader = classLoader - ) - - val evolutionSerializerFactory = DefaultEvolutionSerializerFactory( - localSerializerFactory = localSerializerFactory, - classLoader = classLoader, - mustPreserveDataWhenEvolving = context.preventDataLoss, - primitiveTypes = primitiveTypes, - baseTypes = localTypes - ) - - val remoteSerializerFactory = DefaultRemoteSerializerFactory( - evolutionSerializerFactory = evolutionSerializerFactory, - descriptorBasedSerializerRegistry = descriptorBasedSerializerRegistry, - remoteTypeModel = AMQPRemoteTypeModel(), - localTypeModel = localTypeModel, - typeLoader = typeLoader, - localSerializerFactory = localSerializerFactory - ) - - return ComposedSerializerFactory(localSerializerFactory, remoteSerializerFactory, customSerializerRegistry) - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/SandboxWhitelist.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/SandboxWhitelist.kt deleted file mode 100644 index 9df1b34cb0..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/SandboxWhitelist.kt +++ /dev/null @@ -1,13 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.ClassWhitelist - -class SandboxWhitelist : ClassWhitelist { - companion object { - private val packageName = "^sandbox\\.(?:java|kotlin)(?:[.]|$)".toRegex() - } - - override fun hasListed(type: Class<*>): Boolean { - return packageName.containsMatchIn(type.`package`.name) - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/Serialization.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/Serialization.kt deleted file mode 100644 index 6b73fa6e61..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/Serialization.kt +++ /dev/null @@ -1,117 +0,0 @@ -@file:JvmName("Serialization") -package net.corda.serialization.djvm - -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.SerializationContext.UseCase -import net.corda.core.serialization.SerializationFactory -import net.corda.core.serialization.SerializedBytes -import net.corda.core.serialization.internal.SerializationEnvironment -import net.corda.core.utilities.ByteSequence -import net.corda.djvm.rewiring.createRawPredicateFactory -import net.corda.djvm.rewiring.createSandboxPredicate -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.CheckEnum -import net.corda.serialization.djvm.deserializers.DescribeEnum -import net.corda.serialization.djvm.deserializers.GetEnumNames -import net.corda.serialization.djvm.serializers.PrimitiveSerializer -import net.corda.serialization.internal.GlobalTransientClassWhiteList -import net.corda.serialization.internal.SerializationContextImpl -import net.corda.serialization.internal.SerializationFactoryImpl -import net.corda.serialization.internal.amqp.AMQPSerializer -import net.corda.serialization.internal.amqp.amqpMagic -import net.corda.serialization.internal.model.BaseLocalTypes -import java.util.EnumSet -import java.util.function.Function -import java.util.function.Predicate - -@Suppress("NOTHING_TO_INLINE") -inline fun SandboxClassLoader.toSandboxAnyClass(clazz: Class<*>): Class { - @Suppress("unchecked_cast") - return toSandboxClass(clazz) as Class -} - -fun createSandboxSerializationEnv(classLoader: SandboxClassLoader): SerializationEnvironment { - return createSandboxSerializationEnv(classLoader, emptySet(), emptySet()) -} - -fun createSandboxSerializationEnv( - classLoader: SandboxClassLoader, - customSerializerClassNames: Set, - serializationWhitelistNames: Set -): SerializationEnvironment { - val p2pContext: SerializationContext = SerializationContextImpl( - preferredSerializationVersion = amqpMagic, - deserializationClassLoader = DelegatingClassLoader(classLoader), - whitelist = GlobalTransientClassWhiteList(SandboxWhitelist()), - properties = emptyMap(), - objectReferencesEnabled = true, - carpenterDisabled = true, - useCase = UseCase.P2P, - encoding = null - ) - - val sandboxBasicInput = classLoader.createBasicInput() - val rawTaskFactory = classLoader.createRawTaskFactory() - val taskFactory = rawTaskFactory.compose(classLoader.createSandboxFunction()) - val predicateFactory = classLoader.createRawPredicateFactory().compose(classLoader.createSandboxPredicate()) - - val primitiveSerializerFactory: Function, AMQPSerializer> = Function { clazz -> - PrimitiveSerializer(clazz, sandboxBasicInput) - } - @Suppress("unchecked_cast") - val isEnumPredicate = predicateFactory.apply(CheckEnum::class.java) as Predicate> - @Suppress("unchecked_cast") - val enumConstants = taskFactory.apply(DescribeEnum::class.java) as Function, Array> - @Suppress("unchecked_cast") - val enumConstantNames = enumConstants.andThen(taskFactory.apply(GetEnumNames::class.java)) - .andThen { (it as Array).map(Any::toString) } as Function, List> - - val sandboxLocalTypes = BaseLocalTypes( - collectionClass = classLoader.toSandboxClass(Collection::class.java), - enumSetClass = classLoader.toSandboxClass(EnumSet::class.java), - exceptionClass = classLoader.toSandboxClass(Exception::class.java), - mapClass = classLoader.toSandboxClass(Map::class.java), - stringClass = classLoader.toSandboxClass(String::class.java), - isEnum = isEnumPredicate, - enumConstants = enumConstants, - enumConstantNames = enumConstantNames - ) - val schemeBuilder = SandboxSerializationSchemeBuilder( - classLoader = classLoader, - sandboxBasicInput = sandboxBasicInput, - rawTaskFactory = rawTaskFactory, - taskFactory = taskFactory, - predicateFactory = predicateFactory, - customSerializerClassNames = customSerializerClassNames, - serializationWhitelistNames = serializationWhitelistNames, - serializerFactoryFactory = SandboxSerializerFactoryFactory( - primitiveSerializerFactory = primitiveSerializerFactory, - localTypes = sandboxLocalTypes - ) - ) - val factory = SerializationFactoryImpl(mutableMapOf()).apply { - registerScheme(schemeBuilder.buildFor(p2pContext)) - } - return SerializationEnvironment.with(factory, p2pContext = p2pContext) -} - -inline fun SerializedBytes.deserializeFor(classLoader: SandboxClassLoader): Any { - return deserializeTo(T::class.java, classLoader) -} - -inline fun ByteSequence.deserializeTypeFor(classLoader: SandboxClassLoader): Any { - return deserializeTo(T::class.java, classLoader) -} - -fun ByteSequence.deserializeTo(clazz: Class, classLoader: SandboxClassLoader): Any { - val sandboxClazz = classLoader.toSandboxClass(clazz) - return deserializeTo(sandboxClazz) -} - -fun ByteSequence.deserializeTo(clazz: Class<*>): Any { - return deserializeTo(clazz, SerializationFactory.defaultFactory) -} - -fun ByteSequence.deserializeTo(clazz: Class<*>, factory: SerializationFactory): Any { - return factory.deserialize(this, clazz, factory.defaultContext) -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/ExceptionUtils.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/ExceptionUtils.kt deleted file mode 100644 index edb859d3bd..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/ExceptionUtils.kt +++ /dev/null @@ -1,39 +0,0 @@ -@file:JvmName("ExceptionUtils") -package net.corda.serialization.djvm.serializers - -import net.corda.serialization.internal.amqp.AMQPNotSerializableException - -/** - * Utility function which helps tracking the path in the object graph when exceptions are thrown. - * Since there might be a chain of nested calls it is useful to record which part of the graph caused an issue. - * Path information is added to the message of the exception being thrown. - */ -@Suppress("TooGenericExceptionCaught") -internal inline fun ifThrowsAppend(strToAppendFn: () -> String, block: () -> T): T { - try { - return block() - } catch (th: Throwable) { - when (th) { - is AMQPNotSerializableException -> th.classHierarchy.add(strToAppendFn()) - // Do not overwrite the message of these exceptions as it may be used. - is ClassNotFoundException -> {} - is NoClassDefFoundError -> {} - else -> th.resetMessage("${strToAppendFn()} -> ${th.message}") - } - throw th - } -} - -/** - * Not a public property so will have to use reflection - */ -private fun Throwable.resetMessage(newMsg: String) { - val detailMessageField = Throwable::class.java.getDeclaredField("detailMessage") - detailMessageField.isAccessible = true - detailMessageField.set(this, newMsg) -} - -/** - * We currently only support deserialisation, and so we're going to need this. - */ -fun abortReadOnly(): Nothing = throw UnsupportedOperationException("Read Only!") \ No newline at end of file diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/PrimitiveSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/PrimitiveSerializer.kt deleted file mode 100644 index 52e9cd847c..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/PrimitiveSerializer.kt +++ /dev/null @@ -1,36 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.serialization.internal.amqp.AMQPSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import net.corda.serialization.internal.amqp.typeDescriptorFor -import org.apache.qpid.proton.amqp.Binary -import org.apache.qpid.proton.amqp.Symbol -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class PrimitiveSerializer( - override val type: Class<*>, - private val sandboxBasicInput: Function -) : AMQPSerializer { - override val typeDescriptor: Symbol = typeDescriptorFor(type) - - override fun readObject( - obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext - ): Any { - return (obj as? Binary)?.array ?: sandboxBasicInput.apply(obj)!! - } - - override fun writeClassInfo(output: SerializationOutput) { - abortReadOnly() - } - - override fun writeObject( - obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext, debugIndent: Int - ) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxBitSetSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxBitSetSerializer.kt deleted file mode 100644 index af11ec7887..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxBitSetSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.BitSetDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.BitSetSerializer.BitSetProxy -import java.util.BitSet -import java.util.function.Function - -class SandboxBitSetSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(BitSet::class.java), - proxyClass = classLoader.toSandboxAnyClass(BitSetProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(BitSetDeserializer::class.java) - - override val deserializationAliases = aliasFor(BitSet::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCertPathSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCertPathSerializer.kt deleted file mode 100644 index 25710d654e..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCertPathSerializer.kt +++ /dev/null @@ -1,41 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.DESERIALIZATION_CACHE_PROPERTY -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.CertPathDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.CertPathSerializer.CertPathProxy -import java.security.cert.CertPath -import java.util.function.Function - -class SandboxCertPathSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(CertPath::class.java), - proxyClass = classLoader.toSandboxAnyClass(CertPathProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(CertPathDeserializer::class.java) - - override val deserializationAliases = aliasFor(CertPath::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } - - override fun fromProxy(proxy: Any, context: SerializationContext): Any { - // This requires [CertPathProxy] to have correct - // implementations for [equals] and [hashCode]. - @Suppress("unchecked_cast") - return (context.properties[DESERIALIZATION_CACHE_PROPERTY] as? MutableMap) - ?.computeIfAbsent(proxy, ::fromProxy) - ?: fromProxy(proxy) - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCharacterSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCharacterSerializer.kt deleted file mode 100644 index 926982a82b..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCharacterSerializer.kt +++ /dev/null @@ -1,37 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxCharacterSerializer( - classLoader: SandboxClassLoader, - private val basicInput: Function -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(Char::class.javaObjectType)) { - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return basicInput.apply(convertToChar(obj))!! - } - - private fun convertToChar(obj: Any): Any { - return when (obj) { - is Short -> obj.toChar() - is Int -> obj.toChar() - else -> obj - } - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxClassSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxClassSerializer.kt deleted file mode 100644 index 46d3be88b3..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxClassSerializer.kt +++ /dev/null @@ -1,47 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.ClassDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.AMQPNotSerializableException -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.ClassSerializer.ClassProxy -import java.util.function.Function - -@Suppress("unchecked_cast") -class SandboxClassSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = Class::class.java as Class, - proxyClass = classLoader.toSandboxAnyClass(ClassProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(ClassDeserializer::class.java) - private val nameOf: Function - - init { - val fetch = proxyClass.getMethod("getClassName") - nameOf = Function { proxy -> - fetch(proxy).toString() - } - } - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return try { - task.apply(proxy)!! - } catch (e: ClassNotFoundException) { - val className = nameOf.apply(proxy) - throw AMQPNotSerializableException(type, - "Could not instantiate $className - not on the classpath", - "$className was not found by the node, check the Node containing the CorDapp that " + - "implements $className is loaded and on the Classpath", - mutableListOf(className) - ) - } - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCollectionSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCollectionSerializer.kt deleted file mode 100644 index 5c5fa25c19..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCollectionSerializer.kt +++ /dev/null @@ -1,129 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.core.utilities.NonEmptySet -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.CreateCollection -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.AMQPSerializer -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.LocalSerializerFactory -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import net.corda.serialization.internal.amqp.redescribe -import net.corda.serialization.internal.model.LocalTypeInformation -import net.corda.serialization.internal.model.TypeIdentifier -import org.apache.qpid.proton.amqp.Symbol -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type -import java.util.EnumSet -import java.util.NavigableSet -import java.util.SortedSet -import java.util.function.Function - -class SandboxCollectionSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - private val localFactory: LocalSerializerFactory -) : CustomSerializer.Implements(clazz = classLoader.toSandboxAnyClass(Collection::class.java)) { - @Suppress("unchecked_cast") - private val creator: Function, out Any?> - = taskFactory.apply(CreateCollection::class.java) as Function, out Any?> - - private val unsupportedTypes: Set> = listOf( - EnumSet::class.java - ).mapTo(LinkedHashSet()) { - classLoader.toSandboxAnyClass(it) - } - - // The order matters here - the first match should be the most specific one. - // Kotlin preserves the ordering for us by associating into a LinkedHashMap. - private val supportedTypes: Map, Class>> = listOf( - List::class.java, - NonEmptySet::class.java, - NavigableSet::class.java, - SortedSet::class.java, - Set::class.java, - Collection::class.java - ).associateBy { - classLoader.toSandboxAnyClass(it) - } - - private fun getBestMatchFor(type: Class): Map.Entry, Class>> - = supportedTypes.entries.first { it.key.isAssignableFrom(type) } - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun isSerializerFor(clazz: Class<*>): Boolean { - return super.isSerializerFor(clazz) && unsupportedTypes.none { it.isAssignableFrom(clazz) } - } - - override fun specialiseFor(declaredType: Type): AMQPSerializer? { - if (declaredType !is ParameterizedType) { - return null - } - - @Suppress("unchecked_cast") - val rawType = declaredType.rawType as Class - return ConcreteCollectionSerializer(declaredType, getBestMatchFor(rawType), creator, localFactory) - } - - override fun readObject( - obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext - ): Any { - throw UnsupportedOperationException("Factory only") - } - - override fun writeDescribedObject( - obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext - ) { - throw UnsupportedOperationException("Factory Only") - } -} - -private class ConcreteCollectionSerializer( - declaredType: ParameterizedType, - private val matchingType: Map.Entry, Class>>, - private val creator: Function, out Any?>, - factory: LocalSerializerFactory -) : AMQPSerializer { - override val type: ParameterizedType = declaredType - - override val typeDescriptor: Symbol by lazy { - factory.createDescriptor( - LocalTypeInformation.ACollection( - observedType = declaredType, - typeIdentifier = TypeIdentifier.forGenericType(declaredType), - elementType = factory.getTypeInformation(declaredType.actualTypeArguments[0]) - ) - ) - } - - override fun readObject( - obj: Any, - schemas: SerializationSchemas, - input: DeserializationInput, - context: SerializationContext - ): Any { - val inboundType = type.actualTypeArguments[0] - return ifThrowsAppend(type::getTypeName) { - val args = (obj as List<*>).map { - input.readObjectOrNull(redescribe(it, inboundType), schemas, inboundType, context) - }.toTypedArray() - creator.apply(arrayOf(matchingType.key, args))!! - } - } - - override fun writeClassInfo(output: SerializationOutput) { - abortReadOnly() - } - - override fun writeObject( - obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext, debugIndent: Int - ) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCorDappCustomSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCorDappCustomSerializer.kt deleted file mode 100644 index 9420dddf9b..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCorDappCustomSerializer.kt +++ /dev/null @@ -1,93 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import com.google.common.reflect.TypeToken -import net.corda.core.internal.objectOrNewInstance -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.SerializationCustomSerializer -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.CorDappCustomDeserializer -import net.corda.serialization.internal.amqp.AMQPNotSerializableException -import net.corda.serialization.internal.amqp.AMQPTypeIdentifiers -import net.corda.serialization.internal.amqp.CORDAPP_TYPE -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.Descriptor -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.ObjectSerializer -import net.corda.serialization.internal.amqp.PROXY_TYPE -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.typeDescriptorFor -import net.corda.serialization.internal.model.TypeIdentifier -import org.apache.qpid.proton.amqp.Symbol -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type -import java.util.Collections.singleton -import java.util.function.Function - -class SandboxCorDappCustomSerializer( - private val serializerName: String, - classLoader: SandboxClassLoader, - rawTaskFactory: Function>, - factory: SerializerFactory -) : CustomSerializer() { - private val unproxy: Function - private val types: List - - init { - val serializationCustomSerializer = classLoader.toSandboxClass(SerializationCustomSerializer::class.java) - val customSerializerClass = classLoader.toSandboxClass(serializerName) - types = customSerializerClass.genericInterfaces - .mapNotNull { it as? ParameterizedType } - .filter { it.rawType == serializationCustomSerializer } - .flatMap { it.actualTypeArguments.toList() } - if (types.size != 2) { - throw AMQPNotSerializableException( - type = SandboxCorDappCustomSerializer::class.java, - msg = "Unable to determine serializer parent types" - ) - } - - val unproxyTask = classLoader.toSandboxClass(CorDappCustomDeserializer::class.java) - .getConstructor(serializationCustomSerializer) - .newInstance(customSerializerClass.kotlin.objectOrNewInstance()) - unproxy = rawTaskFactory.apply(unproxyTask) - } - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override val type: Type = types[CORDAPP_TYPE] - private val proxySerializer: ObjectSerializer by lazy { - ObjectSerializer.make(factory.getTypeInformation(types[PROXY_TYPE]), factory) - } - private val deserializationAlias: TypeIdentifier get() = - TypeIdentifier.Erased(AMQPTypeIdentifiers.nameForType(type).replace("sandbox.", ""), 0) - - override val typeDescriptor: Symbol = typeDescriptorFor(type) - override val descriptor: Descriptor = Descriptor(typeDescriptor) - override val deserializationAliases: Set = singleton(deserializationAlias) - - /** - * For 3rd party plugin serializers we are going to exist on exact type matching. i.e. we will - * not support base class serializers for derived types. - */ - override fun isSerializerFor(clazz: Class<*>): Boolean { - return TypeToken.of(type) == TypeToken.of(clazz) - } - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return unproxy.apply(proxySerializer.readObject(obj, schemas, input, context))!! - } - - override fun writeClassInfo(output: SerializationOutput) { - abortReadOnly() - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } - - override fun toString(): String = "${this::class.java}($serializerName)" -} \ No newline at end of file diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCurrencySerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCurrencySerializer.kt deleted file mode 100644 index 758943ee96..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCurrencySerializer.kt +++ /dev/null @@ -1,40 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.CreateCurrency -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.Currency -import java.util.function.Function - -class SandboxCurrencySerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - basicInput: Function -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(Currency::class.java)) { - private val creator: Function - - init { - val createTask = taskFactory.apply(CreateCurrency::class.java) - creator = basicInput.andThen(createTask) - } - - override val deserializationAliases = aliasFor(Currency::class.java) - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return creator.apply(obj)!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDecimal128Serializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDecimal128Serializer.kt deleted file mode 100644 index 234a966988..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDecimal128Serializer.kt +++ /dev/null @@ -1,35 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.Decimal128Deserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.amqp.Decimal128 -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxDecimal128Serializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function> -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(Decimal128::class.java)) { - @Suppress("unchecked_cast") - private val transformer: Function - = taskFactory.apply(Decimal128Deserializer::class.java) as Function - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - val decimal128 = obj as Decimal128 - return transformer.apply(longArrayOf(decimal128.mostSignificantBits, decimal128.leastSignificantBits))!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDecimal32Serializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDecimal32Serializer.kt deleted file mode 100644 index 9375431625..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDecimal32Serializer.kt +++ /dev/null @@ -1,34 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.Decimal32Deserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.amqp.Decimal32 -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxDecimal32Serializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function> -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(Decimal32::class.java)) { - @Suppress("unchecked_cast") - private val transformer: Function - = taskFactory.apply(Decimal32Deserializer::class.java) as Function - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return transformer.apply(intArrayOf((obj as Decimal32).bits))!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDecimal64Serializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDecimal64Serializer.kt deleted file mode 100644 index 3461cf7d60..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDecimal64Serializer.kt +++ /dev/null @@ -1,34 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.Decimal64Deserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.amqp.Decimal64 -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxDecimal64Serializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function> -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(Decimal64::class.java)) { - @Suppress("unchecked_cast") - private val transformer: Function - = taskFactory.apply(Decimal64Deserializer::class.java) as Function - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return transformer.apply(longArrayOf((obj as Decimal64).bits))!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDurationSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDurationSerializer.kt deleted file mode 100644 index 8b2f8613e2..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxDurationSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.DurationDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.DurationSerializer.DurationProxy -import java.time.Duration -import java.util.function.Function - -class SandboxDurationSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(Duration::class.java), - proxyClass = classLoader.toSandboxAnyClass(DurationProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(DurationDeserializer::class.java) - - override val deserializationAliases = aliasFor(Duration::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxEnumSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxEnumSerializer.kt deleted file mode 100644 index 361b467c08..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxEnumSerializer.kt +++ /dev/null @@ -1,121 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.CheckEnum -import net.corda.serialization.djvm.deserializers.DescribeEnum -import net.corda.serialization.djvm.deserializers.GetEnumNames -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.AMQPNotSerializableException -import net.corda.serialization.internal.amqp.AMQPSerializer -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.LocalSerializerFactory -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import net.corda.serialization.internal.model.EnumTransforms -import net.corda.serialization.internal.model.LocalTypeInformation -import net.corda.serialization.internal.model.TypeIdentifier -import org.apache.qpid.proton.amqp.Symbol -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function -import java.util.function.Predicate - -class SandboxEnumSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - predicateFactory: Function>, out Predicate>, - private val localFactory: LocalSerializerFactory -) : CustomSerializer.Implements(clazz = classLoader.toSandboxAnyClass(Enum::class.java)) { - @Suppress("unchecked_cast") - private val describeEnum: Function, Array> - = taskFactory.apply(DescribeEnum::class.java) as Function, Array> - @Suppress("unchecked_cast") - private val getEnumNames: Function, List> - = (taskFactory.apply(GetEnumNames::class.java) as Function, Array>) - .andThen { it.map(Any::toString) } - @Suppress("unchecked_cast") - private val isEnum: Predicate> - = predicateFactory.apply(CheckEnum::class.java) as Predicate> - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun isSerializerFor(clazz: Class<*>): Boolean { - return super.isSerializerFor(clazz) && isEnum.test(clazz) - } - - override fun specialiseFor(declaredType: Type): AMQPSerializer? { - if (declaredType !is Class<*>) { - return null - } - val members = describeEnum.apply(declaredType) - val memberNames = getEnumNames.apply(members) - return ConcreteEnumSerializer(declaredType, members, memberNames, localFactory) - } - - override fun readObject( - obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext - ): Any { - throw UnsupportedOperationException("Factory only") - } - - override fun writeDescribedObject( - obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext - ) { - throw UnsupportedOperationException("Factory Only") - } -} - -private class ConcreteEnumSerializer( - declaredType: Class<*>, - private val members: Array, - private val memberNames: List, - factory: LocalSerializerFactory -) : AMQPSerializer { - override val type: Class<*> = declaredType - - override val typeDescriptor: Symbol by lazy { - factory.createDescriptor( - /* - * Partially populated, providing just the information - * required by the fingerprinter. - */ - LocalTypeInformation.AnEnum( - declaredType, - TypeIdentifier.forGenericType(declaredType), - memberNames, - emptyMap(), - emptyList(), - EnumTransforms.empty - ) - ) - } - - override fun readObject( - obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext - ): Any { - val enumName = (obj as List<*>)[0] as String - val enumOrd = obj[1] as Int - val fromOrd = members[enumOrd] - - if (enumName != memberNames[enumOrd]) { - throw AMQPNotSerializableException( - type, - "Deserializing obj as enum $type with value $enumName.$enumOrd but ordinality has changed" - ) - } - return fromOrd - } - - override fun writeClassInfo(output: SerializationOutput) { - abortReadOnly() - } - - override fun writeObject( - obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext, debugIndent: Int - ) { - abortReadOnly() - } -} \ No newline at end of file diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxEnumSetSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxEnumSetSerializer.kt deleted file mode 100644 index 4339d472f7..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxEnumSetSerializer.kt +++ /dev/null @@ -1,35 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.EnumSetDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.EnumSetSerializer.EnumSetProxy -import java.util.Collections.singleton -import java.util.EnumSet -import java.util.function.Function - -class SandboxEnumSetSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(EnumSet::class.java), - proxyClass = classLoader.toSandboxAnyClass(EnumSetProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(EnumSetDeserializer::class.java) - - override val additionalSerializers: Set> = singleton( - SandboxClassSerializer(classLoader, taskFactory, factory) - ) - - override val deserializationAliases = aliasFor(EnumSet::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxInputStreamSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxInputStreamSerializer.kt deleted file mode 100644 index 03c30cd789..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxInputStreamSerializer.kt +++ /dev/null @@ -1,37 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.InputStreamDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.codec.Data -import java.io.InputStream -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxInputStreamSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function> -) : CustomSerializer.Implements(classLoader.toSandboxAnyClass(InputStream::class.java)) { - @Suppress("unchecked_cast") - private val decoder: Function - = taskFactory.apply(InputStreamDeserializer::class.java) as Function - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override val deserializationAliases = aliasFor(InputStream::class.java) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray - return decoder.apply(bits)!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxInstantSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxInstantSerializer.kt deleted file mode 100644 index ae6ed2c48f..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxInstantSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.InstantDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.InstantSerializer.InstantProxy -import java.time.Instant -import java.util.function.Function - -class SandboxInstantSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(Instant::class.java), - proxyClass = classLoader.toSandboxAnyClass(InstantProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(InstantDeserializer::class.java) - - override val deserializationAliases = aliasFor(Instant::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxLocalDateSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxLocalDateSerializer.kt deleted file mode 100644 index 9d09131390..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxLocalDateSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.LocalDateDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.LocalDateSerializer.LocalDateProxy -import java.time.LocalDate -import java.util.function.Function - -class SandboxLocalDateSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(LocalDate::class.java), - proxyClass = classLoader.toSandboxAnyClass(LocalDateProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(LocalDateDeserializer::class.java) - - override val deserializationAliases = aliasFor(LocalDate::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxLocalDateTimeSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxLocalDateTimeSerializer.kt deleted file mode 100644 index 8dca5f0617..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxLocalDateTimeSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.LocalDateTimeDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.LocalDateTimeSerializer.LocalDateTimeProxy -import java.time.LocalDateTime -import java.util.function.Function - -class SandboxLocalDateTimeSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(LocalDateTime::class.java), - proxyClass = classLoader.toSandboxAnyClass(LocalDateTimeProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(LocalDateTimeDeserializer::class.java) - - override val deserializationAliases = aliasFor(LocalDateTime::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxLocalTimeSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxLocalTimeSerializer.kt deleted file mode 100644 index 1b545397fa..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxLocalTimeSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.LocalTimeDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.LocalTimeSerializer.LocalTimeProxy -import java.time.LocalTime -import java.util.function.Function - -class SandboxLocalTimeSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(LocalTime::class.java), - proxyClass = classLoader.toSandboxAnyClass(LocalTimeProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(LocalTimeDeserializer::class.java) - - override val deserializationAliases = aliasFor(LocalTime::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMapSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMapSerializer.kt deleted file mode 100644 index 6803ff4612..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMapSerializer.kt +++ /dev/null @@ -1,124 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.CreateMap -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.AMQPSerializer -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.LocalSerializerFactory -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import net.corda.serialization.internal.amqp.redescribe -import net.corda.serialization.internal.model.LocalTypeInformation -import net.corda.serialization.internal.model.TypeIdentifier -import org.apache.qpid.proton.amqp.Symbol -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type -import java.util.EnumMap -import java.util.NavigableMap -import java.util.SortedMap -import java.util.TreeMap -import java.util.function.Function - -class SandboxMapSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - private val localFactory: LocalSerializerFactory -) : CustomSerializer.Implements(clazz = classLoader.toSandboxAnyClass(Map::class.java)) { - @Suppress("unchecked_cast") - private val creator: Function, out Any?> - = taskFactory.apply(CreateMap::class.java) as Function, out Any?> - - // The order matters here - the first match should be the most specific one. - // Kotlin preserves the ordering for us by associating into a LinkedHashMap. - private val supportedTypes: Map, Class>> = listOf( - TreeMap::class.java, - LinkedHashMap::class.java, - NavigableMap::class.java, - SortedMap::class.java, - EnumMap::class.java, - Map::class.java - ).associateBy { - classLoader.toSandboxAnyClass(it) - } - - private fun getBestMatchFor(type: Class): Map.Entry, Class>> - = supportedTypes.entries.first { it.key.isAssignableFrom(type) } - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun specialiseFor(declaredType: Type): AMQPSerializer? { - if (declaredType !is ParameterizedType) { - return null - } - - @Suppress("unchecked_cast") - val rawType = declaredType.rawType as Class - return ConcreteMapSerializer(declaredType, getBestMatchFor(rawType), creator, localFactory) - } - - override fun readObject( - obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext - ): Any { - throw UnsupportedOperationException("Factory only") - } - - override fun writeDescribedObject( - obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext - ) { - throw UnsupportedOperationException("Factory Only") - } -} - -private class ConcreteMapSerializer( - declaredType: ParameterizedType, - private val matchingType: Map.Entry, Class>>, - private val creator: Function, out Any?>, - factory: LocalSerializerFactory -) : AMQPSerializer { - override val type: ParameterizedType = declaredType - - override val typeDescriptor: Symbol by lazy { - factory.createDescriptor( - LocalTypeInformation.AMap( - observedType = declaredType, - typeIdentifier = TypeIdentifier.forGenericType(declaredType), - keyType = factory.getTypeInformation(declaredType.actualTypeArguments[0]), - valueType = factory.getTypeInformation(declaredType.actualTypeArguments[1]) - ) - ) - } - - override fun readObject( - obj: Any, - schemas: SerializationSchemas, - input: DeserializationInput, - context: SerializationContext - ): Any { - val inboundKeyType = type.actualTypeArguments[0] - val inboundValueType = type.actualTypeArguments[1] - return ifThrowsAppend(type::getTypeName) { - val entries = (obj as Map<*, *>).map { - arrayOf( - input.readObjectOrNull(redescribe(it.key, inboundKeyType), schemas, inboundKeyType, context), - input.readObjectOrNull(redescribe(it.value, inboundValueType), schemas, inboundValueType, context) - ) - }.toTypedArray() - creator.apply(arrayOf(matchingType.key, entries))!! - } - } - - override fun writeClassInfo(output: SerializationOutput) { - abortReadOnly() - } - - override fun writeObject( - obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext, debugIndent: Int - ) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMonthDaySerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMonthDaySerializer.kt deleted file mode 100644 index 899dc20a7d..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMonthDaySerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.MonthDayDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.MonthDaySerializer.MonthDayProxy -import java.time.MonthDay -import java.util.function.Function - -class SandboxMonthDaySerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(MonthDay::class.java), - proxyClass = classLoader.toSandboxAnyClass(MonthDayProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(MonthDayDeserializer::class.java) - - override val deserializationAliases = aliasFor(MonthDay::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOffsetDateTimeSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOffsetDateTimeSerializer.kt deleted file mode 100644 index ebff5cdcd3..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOffsetDateTimeSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.OffsetDateTimeDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.OffsetDateTimeSerializer.OffsetDateTimeProxy -import java.time.OffsetDateTime -import java.util.function.Function - -class SandboxOffsetDateTimeSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(OffsetDateTime::class.java), - proxyClass = classLoader.toSandboxAnyClass(OffsetDateTimeProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(OffsetDateTimeDeserializer::class.java) - - override val deserializationAliases = aliasFor(OffsetDateTime::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOffsetTimeSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOffsetTimeSerializer.kt deleted file mode 100644 index 52f5721d13..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOffsetTimeSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.OffsetTimeDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.OffsetTimeSerializer.OffsetTimeProxy -import java.time.OffsetTime -import java.util.function.Function - -class SandboxOffsetTimeSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(OffsetTime::class.java), - proxyClass = classLoader.toSandboxAnyClass(OffsetTimeProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(OffsetTimeDeserializer::class.java) - - override val deserializationAliases = aliasFor(OffsetTime::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOpaqueBytesSubSequenceSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOpaqueBytesSubSequenceSerializer.kt deleted file mode 100644 index 35ac384f5e..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOpaqueBytesSubSequenceSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.OpaqueBytesSubSequence -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.OpaqueBytesSubSequenceDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import java.util.function.Function - -class SandboxOpaqueBytesSubSequenceSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(OpaqueBytesSubSequence::class.java), - proxyClass = classLoader.toSandboxAnyClass(OpaqueBytes::class.java), - factory = factory -) { - private val task = taskFactory.apply(OpaqueBytesSubSequenceDeserializer::class.java) - - override val deserializationAliases = aliasFor(OpaqueBytesSubSequence::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOptionalSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOptionalSerializer.kt deleted file mode 100644 index 82a633d603..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxOptionalSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.OptionalDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.OptionalSerializer.OptionalProxy -import java.util.Optional -import java.util.function.Function - -class SandboxOptionalSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(Optional::class.java), - proxyClass = classLoader.toSandboxAnyClass(OptionalProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(OptionalDeserializer::class.java) - - override val deserializationAliases = aliasFor(Optional::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxPeriodSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxPeriodSerializer.kt deleted file mode 100644 index 2f82e02e80..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxPeriodSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.PeriodDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.PeriodSerializer.PeriodProxy -import java.time.Period -import java.util.function.Function - -class SandboxPeriodSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(Period::class.java), - proxyClass = classLoader.toSandboxAnyClass(PeriodProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(PeriodDeserializer::class.java) - - override val deserializationAliases = aliasFor(Period::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxPrimitiveSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxPrimitiveSerializer.kt deleted file mode 100644 index 9af70c9d4a..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxPrimitiveSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxPrimitiveSerializer( - clazz: Class<*>, - classLoader: SandboxClassLoader, - private val basicInput: Function -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(clazz)) { - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return basicInput.apply(obj)!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxPublicKeySerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxPublicKeySerializer.kt deleted file mode 100644 index f826672647..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxPublicKeySerializer.kt +++ /dev/null @@ -1,41 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.DESERIALIZATION_CACHE_PROPERTY -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.PublicKeyDecoder -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.security.PublicKey -import java.util.function.Function - -class SandboxPublicKeySerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function> -) : CustomSerializer.Implements(classLoader.toSandboxAnyClass(PublicKey::class.java)) { - @Suppress("unchecked_cast") - private val decoder = taskFactory.apply(PublicKeyDecoder::class.java) as Function - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override val deserializationAliases = aliasFor(PublicKey::class.java) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray - @Suppress("unchecked_cast") - return (context.properties[DESERIALIZATION_CACHE_PROPERTY] as? MutableMap) - ?.computeIfAbsent(CacheKey(bits)) { key -> - decoder.apply(key.bytes) - } ?: decoder.apply(bits)!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxSymbolSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxSymbolSerializer.kt deleted file mode 100644 index 2dd0ba116c..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxSymbolSerializer.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.SymbolDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.amqp.Symbol -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxSymbolSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - basicInput: Function -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(Symbol::class.java)) { - private val transformer: Function - - init { - val transformTask = taskFactory.apply(SymbolDeserializer::class.java) - @Suppress("unchecked_cast") - transformer = basicInput.andThen(transformTask) as Function - } - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return transformer.apply((obj as Symbol).toString())!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxToStringSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxToStringSerializer.kt deleted file mode 100644 index f3f17b67ab..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxToStringSerializer.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxToStringSerializer( - unsafeClass: Class<*>, - classLoader: SandboxClassLoader, - basicInput: Function -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(unsafeClass)) { - private val creator: Function - - init { - val stringClass = classLoader.loadClass("sandbox.java.lang.String") - val clazzConstructor = clazz.getConstructor(stringClass) - creator = basicInput.andThen { s -> clazzConstructor.newInstance(s) } - } - - override val deserializationAliases = aliasFor(unsafeClass) - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return creator.apply(obj)!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedByteSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedByteSerializer.kt deleted file mode 100644 index f871186997..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedByteSerializer.kt +++ /dev/null @@ -1,34 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.UnsignedByteDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.amqp.UnsignedByte -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxUnsignedByteSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function> -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(UnsignedByte::class.java)) { - @Suppress("unchecked_cast") - private val transformer: Function - = taskFactory.apply(UnsignedByteDeserializer::class.java) as Function - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return transformer.apply(byteArrayOf((obj as UnsignedByte).toByte()))!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedIntegerSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedIntegerSerializer.kt deleted file mode 100644 index 7e612d7a10..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedIntegerSerializer.kt +++ /dev/null @@ -1,34 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.UnsignedIntegerDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.amqp.UnsignedInteger -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxUnsignedIntegerSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function> -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(UnsignedInteger::class.java)) { - @Suppress("unchecked_cast") - private val transformer: Function - = taskFactory.apply(UnsignedIntegerDeserializer::class.java) as Function - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return transformer.apply(intArrayOf((obj as UnsignedInteger).toInt()))!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedLongSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedLongSerializer.kt deleted file mode 100644 index b25404377f..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedLongSerializer.kt +++ /dev/null @@ -1,34 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.UnsignedLongDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.amqp.UnsignedLong -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxUnsignedLongSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function> -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(UnsignedLong::class.java)) { - @Suppress("unchecked_cast") - private val transformer: Function - = taskFactory.apply(UnsignedLongDeserializer::class.java) as Function - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return transformer.apply(longArrayOf((obj as UnsignedLong).toLong()))!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} \ No newline at end of file diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedShortSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedShortSerializer.kt deleted file mode 100644 index a999825eb7..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxUnsignedShortSerializer.kt +++ /dev/null @@ -1,34 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.UnsignedShortDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.amqp.UnsignedShort -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.util.function.Function - -class SandboxUnsignedShortSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function> -) : CustomSerializer.Is(classLoader.toSandboxAnyClass(UnsignedShort::class.java)) { - @Suppress("unchecked_cast") - private val transformer: Function - = taskFactory.apply(UnsignedShortDeserializer::class.java) as Function - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - return transformer.apply(shortArrayOf((obj as UnsignedShort).toShort()))!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxX509CRLSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxX509CRLSerializer.kt deleted file mode 100644 index 0c19470e25..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxX509CRLSerializer.kt +++ /dev/null @@ -1,42 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.DESERIALIZATION_CACHE_PROPERTY -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.X509CRLDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.security.cert.X509CRL -import java.util.function.Function - -class SandboxX509CRLSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function> -) : CustomSerializer.Implements(classLoader.toSandboxAnyClass(X509CRL::class.java)) { - @Suppress("unchecked_cast") - private val generator: Function - = taskFactory.apply(X509CRLDeserializer::class.java) as Function - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override val deserializationAliases = aliasFor(X509CRL::class.java) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray - @Suppress("unchecked_cast") - return (context.properties[DESERIALIZATION_CACHE_PROPERTY] as? MutableMap) - ?.computeIfAbsent(CacheKey(bits)) { key -> - generator.apply(key.bytes) - } ?: generator.apply(bits)!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxX509CertificateSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxX509CertificateSerializer.kt deleted file mode 100644 index cf6a78da7e..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxX509CertificateSerializer.kt +++ /dev/null @@ -1,42 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.core.serialization.DESERIALIZATION_CACHE_PROPERTY -import net.corda.core.serialization.SerializationContext -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.X509CertificateDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.Schema -import net.corda.serialization.internal.amqp.SerializationOutput -import net.corda.serialization.internal.amqp.SerializationSchemas -import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type -import java.security.cert.X509Certificate -import java.util.function.Function - -class SandboxX509CertificateSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function> -) : CustomSerializer.Implements(classLoader.toSandboxAnyClass(X509Certificate::class.java)) { - @Suppress("unchecked_cast") - private val generator: Function - = taskFactory.apply(X509CertificateDeserializer::class.java) as Function - - override val schemaForDocumentation: Schema = Schema(emptyList()) - - override val deserializationAliases = aliasFor(X509Certificate::class.java) - - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any { - val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray - @Suppress("unchecked_cast") - return (context.properties[DESERIALIZATION_CACHE_PROPERTY] as? MutableMap) - ?.computeIfAbsent(CacheKey(bits)) { key -> - generator.apply(key.bytes) - } ?: generator.apply(bits)!! - } - - override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) { - abortReadOnly() - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxYearMonthSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxYearMonthSerializer.kt deleted file mode 100644 index 051b812368..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxYearMonthSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.YearMonthDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.YearMonthSerializer.YearMonthProxy -import java.time.YearMonth -import java.util.function.Function - -class SandboxYearMonthSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(YearMonth::class.java), - proxyClass = classLoader.toSandboxAnyClass(YearMonthProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(YearMonthDeserializer::class.java) - - override val deserializationAliases = aliasFor(YearMonth::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxYearSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxYearSerializer.kt deleted file mode 100644 index 776a1ada15..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxYearSerializer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.YearDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.YearSerializer.YearProxy -import java.time.Year -import java.util.function.Function - -class SandboxYearSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(Year::class.java), - proxyClass = classLoader.toSandboxAnyClass(YearProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(YearDeserializer::class.java) - - override val deserializationAliases = aliasFor(Year::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxZoneIdSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxZoneIdSerializer.kt deleted file mode 100644 index 722c0ad255..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxZoneIdSerializer.kt +++ /dev/null @@ -1,32 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.ZoneIdDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.ZoneIdSerializer.ZoneIdProxy -import java.time.ZoneId -import java.util.function.Function - -class SandboxZoneIdSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(ZoneId::class.java), - proxyClass = classLoader.toSandboxAnyClass(ZoneIdProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(ZoneIdDeserializer::class.java) - - override val revealSubclassesInSchema: Boolean = true - - override val deserializationAliases = aliasFor(ZoneId::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return task.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxZonedDateTimeSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxZonedDateTimeSerializer.kt deleted file mode 100644 index ea9126c772..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxZonedDateTimeSerializer.kt +++ /dev/null @@ -1,47 +0,0 @@ -package net.corda.serialization.djvm.serializers - -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.deserializers.ZonedDateTimeDeserializer -import net.corda.serialization.djvm.toSandboxAnyClass -import net.corda.serialization.internal.amqp.CustomSerializer -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.custom.ZonedDateTimeSerializer.ZonedDateTimeProxy -import java.time.LocalDateTime -import java.time.ZoneId -import java.time.ZoneOffset -import java.time.ZonedDateTime -import java.util.function.Function - -class SandboxZonedDateTimeSerializer( - classLoader: SandboxClassLoader, - taskFactory: Function>, out Function>, - factory: SerializerFactory -) : CustomSerializer.Proxy( - clazz = classLoader.toSandboxAnyClass(ZonedDateTime::class.java), - proxyClass = classLoader.toSandboxAnyClass(ZonedDateTimeProxy::class.java), - factory = factory -) { - private val task = taskFactory.apply(ZonedDateTimeDeserializer::class.java) - private val creator: Function - - init { - val createTask = clazz.getMethod( - "createDJVM", - classLoader.toSandboxClass(LocalDateTime::class.java), - classLoader.toSandboxClass(ZoneOffset::class.java), - classLoader.toSandboxClass(ZoneId::class.java) - ) - creator = task.andThen { input -> - @Suppress("unchecked_cast", "SpreadOperator") - createTask(null, *(input as Array))!! - } - } - - override val deserializationAliases = aliasFor(ZonedDateTime::class.java) - - override fun toProxy(obj: Any): Any = abortReadOnly() - - override fun fromProxy(proxy: Any): Any { - return creator.apply(proxy)!! - } -} diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/Serializers.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/Serializers.kt deleted file mode 100644 index cd67433f37..0000000000 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/Serializers.kt +++ /dev/null @@ -1,8 +0,0 @@ -@file:JvmName("Serializers") -package net.corda.serialization.djvm.serializers - -import net.corda.serialization.internal.model.TypeIdentifier -import java.lang.reflect.Type -import java.util.Collections.singleton - -fun aliasFor(type: Type): Set = singleton(TypeIdentifier.forGenericType(type)) diff --git a/serialization-djvm/src/test/java/greymalkin/ExternalData.java b/serialization-djvm/src/test/java/greymalkin/ExternalData.java deleted file mode 100644 index 8774478bdb..0000000000 --- a/serialization-djvm/src/test/java/greymalkin/ExternalData.java +++ /dev/null @@ -1,13 +0,0 @@ -package greymalkin; - -public class ExternalData { - private final String data; - - public ExternalData(String data) { - this.data = data; - } - - public String getData() { - return data; - } -} diff --git a/serialization-djvm/src/test/java/greymalkin/ExternalEnum.java b/serialization-djvm/src/test/java/greymalkin/ExternalEnum.java deleted file mode 100644 index 2180693367..0000000000 --- a/serialization-djvm/src/test/java/greymalkin/ExternalEnum.java +++ /dev/null @@ -1,11 +0,0 @@ -package greymalkin; - -import net.corda.core.serialization.CordaSerializable; - -@SuppressWarnings("unused") -@CordaSerializable -public enum ExternalEnum { - DOH, - RAY, - ME -} diff --git a/serialization-djvm/src/test/java/net/corda/serialization/djvm/InnocentData.java b/serialization-djvm/src/test/java/net/corda/serialization/djvm/InnocentData.java deleted file mode 100644 index 1f350f940d..0000000000 --- a/serialization-djvm/src/test/java/net/corda/serialization/djvm/InnocentData.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.corda.serialization.djvm; - -import net.corda.core.serialization.CordaSerializable; - -@CordaSerializable -public class InnocentData { - private final String message; - private final Short number; - - public InnocentData(String message, Short number) { - this.message = message; - this.number = number; - } - - public String getMessage() { - return message; - } - - public Short getNumber() { - return number; - } -} diff --git a/serialization-djvm/src/test/java/net/corda/serialization/djvm/MultiConstructorData.java b/serialization-djvm/src/test/java/net/corda/serialization/djvm/MultiConstructorData.java deleted file mode 100644 index 9a6e66d86e..0000000000 --- a/serialization-djvm/src/test/java/net/corda/serialization/djvm/MultiConstructorData.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.corda.serialization.djvm; - -import net.corda.core.serialization.ConstructorForDeserialization; -import net.corda.core.serialization.CordaSerializable; - -@SuppressWarnings({"unused", "WeakerAccess"}) -@CordaSerializable -public class MultiConstructorData { - private final String message; - private final long bigNumber; - private final Character tag; - - @ConstructorForDeserialization - public MultiConstructorData(String message, long bigNumber, Character tag) { - this.message = message; - this.bigNumber = bigNumber; - this.tag = tag; - } - - public MultiConstructorData(String message, long bigNumber) { - this(message, bigNumber, null); - } - - public MultiConstructorData(String message, char tag) { - this(message, 0, tag); - } - - public MultiConstructorData(String message) { - this(message, 0); - } - - public String getMessage() { - return message; - } - - public long getBigNumber() { - return bigNumber; - } - - public Character getTag() { - return tag; - } - - @SuppressWarnings("StringBufferReplaceableByString") - @Override - public String toString() { - return new StringBuilder("MultiConstructor[message='").append(message) - .append("', bigNumber=").append(bigNumber) - .append(", tag=").append(tag) - .append(']') - .toString(); - } -} diff --git a/serialization-djvm/src/test/java/net/corda/serialization/djvm/VeryEvilData.java b/serialization-djvm/src/test/java/net/corda/serialization/djvm/VeryEvilData.java deleted file mode 100644 index d1ea8f0ad0..0000000000 --- a/serialization-djvm/src/test/java/net/corda/serialization/djvm/VeryEvilData.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.corda.serialization.djvm; - -@SuppressWarnings("unused") -public class VeryEvilData extends InnocentData { - static { - if (!VeryEvilData.class.getName().startsWith("sandbox.")) { - // Execute our evil payload OUTSIDE the sandbox! - throw new IllegalStateException("Victory is mine!"); - } - } - - public VeryEvilData(String message, Short number) { - super(message, number); - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/Assertions.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/Assertions.kt deleted file mode 100644 index 6b7e58e43c..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/Assertions.kt +++ /dev/null @@ -1,14 +0,0 @@ -@file:JvmName("Assertions") -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import org.junit.jupiter.api.Assertions.assertNull - -inline fun assertNotCordaSerializable() { - assertNotCordaSerializable(T::class.java) -} - -fun assertNotCordaSerializable(clazz: Class<*>) { - assertNull(clazz.getAnnotation(CordaSerializable::class.java), - "$clazz must NOT be annotated as @CordaSerializable!") -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeBigDecimalTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeBigDecimalTest.kt deleted file mode 100644 index 4d91d996b9..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeBigDecimalTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.math.BigDecimal -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeBigDecimalTest : TestBase(KOTLIN) { - companion object { - const val VERY_BIG_DECIMAL = 994349993939.32737232 - } - - @Test - fun `test deserializing big decimal`() { - val bigDecimal = BigDecimalData(BigDecimal.valueOf(VERY_BIG_DECIMAL)) - val data = bigDecimal.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxBigInteger = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showBigDecimal = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowBigDecimal::class.java) - val result = showBigDecimal.apply(sandboxBigInteger) ?: fail("Result cannot be null") - - assertEquals(ShowBigDecimal().apply(bigDecimal), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowBigDecimal : Function { - override fun apply(data: BigDecimalData): String { - return with(data) { - "BigDecimal: $number" - } - } - } -} - -@CordaSerializable -data class BigDecimalData(val number: BigDecimal) \ No newline at end of file diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeBigIntegerTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeBigIntegerTest.kt deleted file mode 100644 index cec80daf48..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeBigIntegerTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.math.BigInteger -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeBigIntegerTest : TestBase(KOTLIN) { - companion object { - const val VERY_BIG_NUMBER = 1234567890123456789 - } - - @Test - fun `test deserializing big integer`() { - val bigInteger = BigIntegerData(BigInteger.valueOf(VERY_BIG_NUMBER)) - val data = bigInteger.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxBigInteger = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showBigInteger = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowBigInteger::class.java) - val result = showBigInteger.apply(sandboxBigInteger) ?: fail("Result cannot be null") - - assertEquals(ShowBigInteger().apply(bigInteger), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowBigInteger : Function { - override fun apply(data: BigIntegerData): String { - return with(data) { - "BigInteger: $number" - } - } - } -} - -@CordaSerializable -data class BigIntegerData(val number: BigInteger) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeBitSetTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeBitSetTest.kt deleted file mode 100644 index 3a1cb6e15c..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeBitSetTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.BitSet -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeBitSetTest : TestBase(KOTLIN) { - @Test - fun `test deserializing bitset`() { - val bitSet = BitSet.valueOf(byteArrayOf(0x00, 0x70, 0x55, 0x3A, 0x48, 0x12)) - val data = bitSet.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxBitSet = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showBitSet = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowBitSet::class.java) - val result = showBitSet.apply(sandboxBitSet) ?: fail("Result cannot be null") - - assertEquals(bitSet.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowBitSet : Function { - override fun apply(bitSet: BitSet): String { - return bitSet.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCertificatesTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCertificatesTest.kt deleted file mode 100644 index 96de396080..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCertificatesTest.kt +++ /dev/null @@ -1,134 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x509.CRLReason -import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.cert.X509v2CRLBuilder -import org.bouncycastle.cert.jcajce.JcaX509CRLConverter -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.security.KeyPairGenerator -import java.security.cert.CertPath -import java.security.cert.CertificateFactory -import java.security.cert.X509CRL -import java.security.cert.X509Certificate -import java.util.Date -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeCertificatesTest : TestBase(KOTLIN) { - companion object { - // The sandbox's localisation may not match that of the host. - // E.g. line separator characters. - fun String.toUNIX(): String { - return replace(System.lineSeparator(), "\n") - } - - // Remove the lines which have been added since Java 8. - fun String.toJava8Format(): String { - return replace(" params: null\n", "") - } - - val factory: CertificateFactory = CertificateFactory.getInstance("X.509") - lateinit var certificate: X509Certificate - - @Suppress("unused") - @BeforeAll - @JvmStatic - fun loadCertificate() { - certificate = this::class.java.classLoader.getResourceAsStream("testing.cert")?.use { input -> - factory.generateCertificate(input) as X509Certificate - } ?: fail("Certificate not found") - } - } - - @Test - fun `test deserialize certificate path`() { - val certPath = factory.generateCertPath(listOf(certificate)) - val data = certPath.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxCertPath = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showCertPath = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowCertPath::class.java) - val result = showCertPath.apply(sandboxCertPath) ?: fail("Result cannot be null") - - assertEquals(ShowCertPath().apply(certPath).toUNIX().toJava8Format(), result.toString()) - assertThat(result::class.java.name).startsWith("sandbox.") - } - } - - class ShowCertPath : Function { - override fun apply(certPath: CertPath): String { - return "CertPath -> $certPath" - } - } - - @Test - fun `test deserialize X509 certificate`() { - val data = certificate.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxCertificate = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showCertificate = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowCertificate::class.java) - val result = showCertificate.apply(sandboxCertificate) ?: fail("Result cannot be null") - - assertEquals(ShowCertificate().apply(certificate).toUNIX().toJava8Format(), result.toString()) - assertThat(result::class.java.name).startsWith("sandbox.") - } - } - - class ShowCertificate : Function { - override fun apply(certificate: X509Certificate): String { - return "X.509 Certificate -> $certificate" - } - } - - @Test - fun `test X509 CRL`() { - val caKeyPair = KeyPairGenerator.getInstance("RSA") - .generateKeyPair() - val signer = JcaContentSignerBuilder("SHA256WithRSAEncryption") - .build(caKeyPair.private) - - val now = Date() - val crl = with(X509v2CRLBuilder(X500Name("CN=Test CA"), now)) { - addCRLEntry(certificate.serialNumber, now, CRLReason.privilegeWithdrawn) - JcaX509CRLConverter().getCRL(build(signer)) - } - val data = crl.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxCRL = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showCRL = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowCRL::class.java) - val result = showCRL.apply(sandboxCRL) ?: fail("Result cannot be null") - - assertEquals(ShowCRL().apply(crl).toUNIX(), result.toString()) - assertThat(result::class.java.name).startsWith("sandbox.") - } - } - - class ShowCRL : Function { - override fun apply(crl: X509CRL): String { - return "X.509 CRL -> $crl" - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeClassTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeClassTest.kt deleted file mode 100644 index 657a71f1f6..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeClassTest.kt +++ /dev/null @@ -1,59 +0,0 @@ -package net.corda.serialization.djvm - -import greymalkin.ExternalData -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.djvm.messages.Severity -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.io.NotSerializableException -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeClassTest : TestBase(KOTLIN) { - @Test - fun `test deserializing existing class`() { - val myClass = ExternalData::class.java - val data = myClass.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxInstant = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showClass = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowClass::class.java) - val result = showClass.apply(sandboxInstant) ?: fail("Result cannot be null") - - assertEquals("sandbox.${myClass.name}", result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - @Test - fun `test deserializing missing class`() { - // The DJVM will refuse to find this class because it belongs to net.corda.djvm.**. - val myClass = Severity::class.java - val data = myClass.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val ex = assertThrows{ data.deserializeFor(classLoader) } - assertThat(ex) - .isExactlyInstanceOf(NotSerializableException::class.java) - .hasMessageContaining("Severity was not found by the node,") - } - } -} - -class ShowClass : Function, String> { - override fun apply(type: Class<*>): String { - return type.name - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCollectionsTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCollectionsTest.kt deleted file mode 100644 index efca075039..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCollectionsTest.kt +++ /dev/null @@ -1,215 +0,0 @@ -package net.corda.serialization.djvm - -import greymalkin.ExternalEnum -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.core.utilities.NonEmptySet -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.EnumSet -import java.util.NavigableSet -import java.util.SortedSet -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeCollectionsTest : TestBase(KOTLIN) { - @Test - fun `test deserializing string list`() { - val stringList = StringList(listOf("Hello", "World", "!")) - val data = stringList.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxList = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showStringList = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowStringList::class.java) - val result = showStringList.apply(sandboxList) ?: fail("Result cannot be null") - - assertEquals(stringList.lines.joinToString(), result.toString()) - assertEquals("Hello, World, !", result.toString()) - } - } - - class ShowStringList : Function { - override fun apply(data: StringList): String { - return data.lines.joinToString() - } - } - - @Test - fun `test deserializing integer set`() { - val integerSet = IntegerSet(linkedSetOf(10, 3, 15, 2, 10)) - val data = integerSet.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxSet = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showIntegerSet = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowIntegerSet::class.java) - val result = showIntegerSet.apply(sandboxSet) ?: fail("Result cannot be null") - - assertEquals(integerSet.numbers.joinToString(), result.toString()) - assertEquals("10, 3, 15, 2", result.toString()) - } - } - - class ShowIntegerSet : Function { - override fun apply(data: IntegerSet): String { - return data.numbers.joinToString() - } - } - - @Test - fun `test deserializing integer sorted set`() { - val integerSortedSet = IntegerSortedSet(sortedSetOf(10, 15, 1000, 3, 2, 10)) - val data = integerSortedSet.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxSet = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showIntegerSortedSet = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowIntegerSortedSet::class.java) - val result = showIntegerSortedSet.apply(sandboxSet) ?: fail("Result cannot be null") - - assertEquals(integerSortedSet.numbers.joinToString(), result.toString()) - assertEquals("2, 3, 10, 15, 1000", result.toString()) - } - } - - class ShowIntegerSortedSet : Function { - override fun apply(data: IntegerSortedSet): String { - return data.numbers.joinToString() - } - } - - @Test - fun `test deserializing long navigable set`() { - val longNavigableSet = LongNavigableSet(sortedSetOf(99955L, 10, 15, 1000, 3, 2, 10)) - val data = longNavigableSet.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxSet = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showLongNavigableSet = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowLongNavigableSet::class.java) - val result = showLongNavigableSet.apply(sandboxSet) ?: fail("Result cannot be null") - - assertEquals(longNavigableSet.numbers.joinToString(), result.toString()) - assertEquals("2, 3, 10, 15, 1000, 99955", result.toString()) - } - } - - class ShowLongNavigableSet : Function { - override fun apply(data: LongNavigableSet): String { - return data.numbers.joinToString() - } - } - - @Test - fun `test deserializing short collection`() { - val shortCollection = ShortCollection(listOf(10, 200, 3000)) - val data = shortCollection.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxCollection = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showShortCollection = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowShortCollection::class.java) - val result = showShortCollection.apply(sandboxCollection) ?: fail("Result cannot be null") - - assertEquals(shortCollection.numbers.joinToString(), result.toString()) - assertEquals("10, 200, 3000", result.toString()) - } - } - - class ShowShortCollection : Function { - override fun apply(data: ShortCollection): String { - return data.numbers.joinToString() - } - } - - @Test - fun `test deserializing non-empty string set`() { - val nonEmptyStrings = NonEmptyStringSet(NonEmptySet.of("Hello", "World", "!")) - val data = nonEmptyStrings.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxSet = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showNonEmptyStringSet = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowNonEmptyStringSet::class.java) - val result = showNonEmptyStringSet.apply(sandboxSet) ?: fail("Result cannot be null") - - assertEquals(nonEmptyStrings.lines.joinToString(), result.toString()) - assertEquals("Hello, World, !", result.toString()) - } - } - - class ShowNonEmptyStringSet : Function { - override fun apply(data: NonEmptyStringSet): String { - return data.lines.joinToString() - } - } - - @Test - fun `test deserializing enum set`() { - val enumSet = HasEnumSet(EnumSet.of(ExternalEnum.DOH)) - val data = enumSet.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxSet = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showHasEnumSet = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowHasEnumSet::class.java) - val result = showHasEnumSet.apply(sandboxSet) ?: fail("Result cannot be null") - - assertEquals(enumSet.values.toString(), result.toString()) - assertEquals("[DOH]", result.toString()) - } - } - - class ShowHasEnumSet : Function { - override fun apply(data: HasEnumSet): String { - return data.values.toString() - } - } -} - -@CordaSerializable -class StringList(val lines: List) - -@CordaSerializable -class IntegerSet(val numbers: Set) - -@CordaSerializable -class IntegerSortedSet(val numbers: SortedSet) - -@CordaSerializable -class LongNavigableSet(val numbers: NavigableSet) - -@CordaSerializable -class ShortCollection(val numbers: Collection) - -@CordaSerializable -class NonEmptyStringSet(val lines: NonEmptySet) - -@CordaSerializable -class HasEnumSet(val values: EnumSet) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeComposedCustomDataTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeComposedCustomDataTest.kt deleted file mode 100644 index 076a5d00d2..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeComposedCustomDataTest.kt +++ /dev/null @@ -1,119 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.SerializationCustomSerializer -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.RegisterExtension -import org.junit.jupiter.api.fail -import java.io.NotSerializableException -import java.util.function.Function - -class DeserializeComposedCustomDataTest: TestBase(KOTLIN) { - companion object { - const val MESSAGE = "Hello Sandbox!" - const val BIG_NUMBER = 23823L - - @Suppress("unused") - @BeforeAll - @JvmStatic - fun checkData() { - assertNotCordaSerializable() - assertNotCordaSerializable() - } - } - - @RegisterExtension - @JvmField - val serialization = LocalSerialization(setOf(StringAtomSerializer(), LongAtomSerializer()), emptySet()) - - @Test - fun `test deserializing composed object`() { - val composed = ComposedData(StringAtom(MESSAGE), LongAtom(BIG_NUMBER)) - val data = composed.serialize() - - sandbox { - val customSerializers = setOf( - StringAtomSerializer::class.java.name, - LongAtomSerializer::class.java.name - ) - _contextSerializationEnv.set(createSandboxSerializationEnv( - classLoader = classLoader, - customSerializerClassNames = customSerializers, - serializationWhitelistNames = emptySet() - )) - - val sandboxComplex = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showComplex = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowComposedData::class.java) - val result = showComplex.apply(sandboxComplex) ?: fail("Result cannot be null") - assertEquals(SANDBOX_STRING, result::class.java.name) - assertEquals(ShowComposedData().apply(composed), result.toString()) - } - } - - @Test - fun `test deserialization needs custom serializer`() { - val composed = ComposedData(StringAtom(MESSAGE), LongAtom(BIG_NUMBER)) - val data = composed.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertThrows { data.deserializeFor(classLoader) } - } - } - - class ShowComposedData : Function { - private fun show(atom: Atom<*>): String = atom.toString() - - override fun apply(composed: ComposedData): String { - return "Composed: message=${show(composed.message)} and value=${show(composed.value)}" - } - } - - class StringAtomSerializer : SerializationCustomSerializer { - data class Proxy(val value: String) - - override fun fromProxy(proxy: Proxy): StringAtom = StringAtom(proxy.value) - override fun toProxy(obj: StringAtom): Proxy = Proxy(obj.atom) - } - - class LongAtomSerializer : SerializationCustomSerializer { - data class Proxy(val value: Long?) - - override fun fromProxy(proxy: Proxy): LongAtom = LongAtom(proxy.value) - override fun toProxy(obj: LongAtom): Proxy = Proxy(obj.atom) - } -} - -@CordaSerializable -class ComposedData( - val message: StringAtom, - val value: LongAtom -) - -interface Atom { - val atom: T -} - -abstract class AbstractAtom(initialValue: T) : Atom { - override val atom: T = initialValue - - override fun toString(): String { - return "[$atom]" - } -} - -/** - * These classes REQUIRE custom serializers because their - * constructor parameters cannot be mapped to properties - * automatically. THIS IS DELIBERATE! - */ -class StringAtom(initialValue: String) : AbstractAtom(initialValue) -class LongAtom(initialValue: Long?) : AbstractAtom(initialValue) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCurrencyTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCurrencyTest.kt deleted file mode 100644 index fc747aebce..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCurrencyTest.kt +++ /dev/null @@ -1,45 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.Currency -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeCurrencyTest : TestBase(KOTLIN) { - @Test - fun `test deserializing currency`() { - val currency = CurrencyData(Currency.getInstance("GBP")) - val data = currency.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxCurrency = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showCurrency = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowCurrency::class.java) - val result = showCurrency.apply(sandboxCurrency) ?: fail("Result cannot be null") - - assertEquals(ShowCurrency().apply(currency), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowCurrency : Function { - override fun apply(data: CurrencyData): String { - return with(data) { - "Currency: $currency" - } - } - } -} - -@CordaSerializable -data class CurrencyData(val currency: Currency) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCustomGenericDataTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCustomGenericDataTest.kt deleted file mode 100644 index 0d624edb84..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCustomGenericDataTest.kt +++ /dev/null @@ -1,97 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.SerializationCustomSerializer -import net.corda.core.serialization.internal.MissingSerializerException -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.RegisterExtension -import org.junit.jupiter.api.fail -import java.util.function.Function - -class DeserializeCustomGenericDataTest: TestBase(KOTLIN) { - companion object { - const val MESSAGE = "Hello Sandbox!" - const val BIG_NUMBER = 23823L - - @Suppress("unused") - @BeforeAll - @JvmStatic - fun checkData() { - assertNotCordaSerializable>() - assertNotCordaSerializable() - } - } - - @RegisterExtension - @JvmField - val serialization = LocalSerialization(setOf(CustomSerializer()), emptySet()) - - @Test - fun `test deserializing custom generic object`() { - val complex = ComplexGenericData(MESSAGE, BIG_NUMBER) - val data = complex.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv( - classLoader = classLoader, - customSerializerClassNames = setOf(CustomSerializer::class.java.name), - serializationWhitelistNames = emptySet() - )) - - val sandboxComplex = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showComplex = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowComplexData::class.java) - val result = showComplex.apply(sandboxComplex) ?: fail("Result cannot be null") - assertEquals(SANDBOX_STRING, result::class.java.name) - assertEquals(ShowComplexData().apply(complex), result.toString()) - } - } - - @Test - fun `test deserialization needs custom serializer`() { - val complex = ComplexGenericData(MESSAGE, BIG_NUMBER) - val data = complex.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertThrows { data.deserializeFor(classLoader) } - } - } - - class ShowComplexData : Function { - private fun show(generic: GenericData<*>): String = generic.toString() - - override fun apply(complex: ComplexGenericData): String { - return "Complex: message=${show(complex.message)} and value=${show(complex.value)}" - } - } - - /** - * This class REQUIRES a custom serializer because its - * constructor parameters cannot be mapped to properties - * automatically. THIS IS DELIBERATE! - */ - class ComplexGenericData(msg: String, initialValue: Long?) { - val message = GenericData(msg) - val value = GenericData(initialValue) - } - - class GenericData(val data: T) { - override fun toString(): String { - return "[$data]" - } - } - - class CustomSerializer : SerializationCustomSerializer { - data class Proxy(val message: String, val value: Long?) - - override fun fromProxy(proxy: Proxy): ComplexGenericData = ComplexGenericData(proxy.message, proxy.value) - override fun toProxy(obj: ComplexGenericData): Proxy = Proxy(obj.message.data, obj.value.data) - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCustomisedEnumTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCustomisedEnumTest.kt deleted file mode 100644 index 657ab25f34..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeCustomisedEnumTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.EnumSource -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeCustomisedEnumTest : TestBase(KOTLIN) { - @ParameterizedTest - @EnumSource(UserRole::class) - fun `test deserialize enum with custom toString`(role: UserRole) { - val userEnumData = UserEnumData(role) - val data = userEnumData.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxData = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showUserEnumData = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowUserEnumData::class.java) - val result = showUserEnumData.apply(sandboxData) ?: fail("Result cannot be null") - - assertEquals(ShowUserEnumData().apply(userEnumData), result.toString()) - assertEquals("UserRole: name='${role.roleName}', ordinal='${role.ordinal}'", result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowUserEnumData : Function { - override fun apply(input: UserEnumData): String { - return with(input) { - "UserRole: name='${role.roleName}', ordinal='${role.ordinal}'" - } - } - } -} - -interface Role { - val roleName: String -} - -@Suppress("unused") -@CordaSerializable -enum class UserRole(override val roleName: String) : Role { - CONTROLLER(roleName = "Controller"), - WORKER(roleName = "Worker"); - - override fun toString() = roleName -} - -@CordaSerializable -data class UserEnumData(val role: UserRole) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeDurationTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeDurationTest.kt deleted file mode 100644 index 7a2c6a4db8..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeDurationTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.Duration -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeDurationTest : TestBase(KOTLIN) { - @Test - fun `test deserializing duration`() { - val duration = Duration.ofSeconds(12345, 6789) - val data = duration.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxDuration = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showDuration = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowDuration::class.java) - val result = showDuration.apply(sandboxDuration) ?: fail("Result cannot be null") - - assertEquals(duration.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowDuration : Function { - override fun apply(duration: Duration): String { - return duration.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumSetTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumSetTest.kt deleted file mode 100644 index 22681c44f5..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumSetTest.kt +++ /dev/null @@ -1,43 +0,0 @@ -package net.corda.serialization.djvm - -import greymalkin.ExternalEnum -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.EnumSource -import java.util.EnumSet -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeEnumSetTest : TestBase(KOTLIN) { - @ParameterizedTest - @EnumSource(ExternalEnum::class) - fun `test deserialize enum set`(value: ExternalEnum) { - val enumSet = EnumSet.of(value) - val data = enumSet.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxEnumSet = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showEnumSet = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowEnumSet::class.java) - val result = showEnumSet.apply(sandboxEnumSet) ?: fail("Result cannot be null") - - assertEquals(ShowEnumSet().apply(enumSet), result.toString()) - assertEquals("EnumSet: [$value]'", result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowEnumSet : Function, String> { - override fun apply(input: EnumSet<*>): String { - return "EnumSet: $input'" - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumTest.kt deleted file mode 100644 index 2dbbb2ea16..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumTest.kt +++ /dev/null @@ -1,55 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.EnumSource -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeEnumTest : TestBase(KOTLIN) { - @ParameterizedTest - @EnumSource(ExampleEnum::class) - fun `test deserialize basic enum`(value: ExampleEnum) { - val example = ExampleData(value) - val data = example.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxExample = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showExampleData = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowExampleData::class.java) - val result = showExampleData.apply(sandboxExample) ?: fail("Result cannot be null") - - assertEquals(ShowExampleData().apply(example), result.toString()) - assertEquals("Example: name='${value.name}', ordinal='${value.ordinal}'", result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowExampleData : Function { - override fun apply(input: ExampleData): String { - return with(input) { - "Example: name='${value.name}', ordinal='${value.ordinal}'" - } - } - } -} - -@Suppress("unused") -@CordaSerializable -enum class ExampleEnum { - ONE, - TWO, - THREE -} - -@CordaSerializable -data class ExampleData(val value: ExampleEnum) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumWithEvolutionTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumWithEvolutionTest.kt deleted file mode 100644 index 32fea60fd0..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumWithEvolutionTest.kt +++ /dev/null @@ -1,155 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.CordaSerializationTransformEnumDefault -import net.corda.core.serialization.CordaSerializationTransformEnumDefaults -import net.corda.core.serialization.CordaSerializationTransformRename -import net.corda.core.serialization.CordaSerializationTransformRenames -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.SerializedBytes -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.EvolvedEnum.ONE -import net.corda.serialization.djvm.EvolvedEnum.TWO -import net.corda.serialization.djvm.EvolvedEnum.THREE -import net.corda.serialization.djvm.EvolvedEnum.FOUR -import net.corda.serialization.djvm.OriginalEnum.One -import net.corda.serialization.djvm.OriginalEnum.Two -import net.corda.serialization.djvm.SandboxType.KOTLIN -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.Transform -import net.corda.serialization.internal.amqp.TransformTypes -import net.corda.serialization.internal.amqp.TypeNotation -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.ExtensionContext -import org.junit.jupiter.api.fail -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.ArgumentsProvider -import org.junit.jupiter.params.provider.ArgumentsSource -import java.util.EnumMap -import java.util.function.Function -import java.util.stream.Stream - -@ExtendWith(LocalSerialization::class) -class DeserializeEnumWithEvolutionTest : TestBase(KOTLIN) { - class EvolutionArgumentProvider : ArgumentsProvider { - override fun provideArguments(context: ExtensionContext?): Stream { - return Stream.of( - Arguments.of(ONE, One), - Arguments.of(TWO, Two), - Arguments.of(THREE, One), - Arguments.of(FOUR, Two) - ) - } - } - - private fun String.devolve() = replace("Evolved", "Original") - - private fun devolveType(type: TypeNotation): TypeNotation { - return when (type) { - is CompositeType -> type.copy( - name = type.name.devolve(), - fields = type.fields.map { it.copy(type = it.type.devolve()) } - ) - is RestrictedType -> type.copy(name = type.name.devolve()) - else -> type - } - } - - private fun SerializedBytes<*>.devolve(context: SerializationContext): SerializedBytes { - val envelope = DeserializationInput.getEnvelope(this, context.encodingWhitelist).apply { - val schemaTypes = schema.types.map(::devolveType) - with(schema.types as MutableList) { - clear() - addAll(schemaTypes) - } - - val transforms = transformsSchema.types.asSequence().associateTo(LinkedHashMap()) { - it.key.devolve() to it.value - } - with(transformsSchema.types as MutableMap>>) { - clear() - putAll(transforms) - } - } - return SerializedBytes(envelope.write()) - } - - @ParameterizedTest - @ArgumentsSource(EvolutionArgumentProvider::class) - fun `test deserialising evolved enum`(value: EvolvedEnum, expected: OriginalEnum) { - val context = (_contextSerializationEnv.get() ?: fail("No serialization environment!")).p2pContext - - val evolvedData = value.serialize() - val originalData = evolvedData.devolve(context) - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - val sandboxOriginal = originalData.deserializeFor(classLoader) - assertEquals("sandbox." + OriginalEnum::class.java.name, sandboxOriginal::class.java.name) - assertEquals(expected.toString(), sandboxOriginal.toString()) - } - } - - @ParameterizedTest - @ArgumentsSource(EvolutionArgumentProvider::class) - fun `test deserialising data with evolved enum`(value: EvolvedEnum, expected: OriginalEnum) { - val context = (_contextSerializationEnv.get() ?: fail("No serialization environment!")).p2pContext - - val evolvedData = EvolvedData(value).serialize() - val originalData = evolvedData.devolve(context) - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - val sandboxOriginal = originalData.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val result = taskFactory.compose(classLoader.createSandboxFunction()) - .apply(ShowOriginalData::class.java) - .apply(sandboxOriginal) ?: fail("Result cannot be null") - assertThat(result.toString()) - .isEqualTo(ShowOriginalData().apply(OriginalData(expected))) - } - } - - class ShowOriginalData : Function { - override fun apply(input: OriginalData): String { - return with(input) { - "Name='${value.name}', Ordinal='${value.ordinal}'" - } - } - } -} - -@CordaSerializable -enum class OriginalEnum { - One, - Two -} - -@CordaSerializable -data class OriginalData(val value: OriginalEnum) - -@CordaSerializable -@CordaSerializationTransformRenames( - CordaSerializationTransformRename(from = "One", to = "ONE"), - CordaSerializationTransformRename(from = "Two", to = "TWO") -) -@CordaSerializationTransformEnumDefaults( - CordaSerializationTransformEnumDefault(new = "THREE", old = "One"), - CordaSerializationTransformEnumDefault(new = "FOUR", old = "Two") -) -enum class EvolvedEnum { - ONE, - TWO, - THREE, - FOUR -} - -@CordaSerializable -data class EvolvedData(val value: EvolvedEnum) \ No newline at end of file diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeGenericsTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeGenericsTest.kt deleted file mode 100644 index cf78125b82..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeGenericsTest.kt +++ /dev/null @@ -1,171 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeGenericsTest : TestBase(KOTLIN) { - @Test - fun `test deserializing generic wrapper with String`() { - val wrappedString = GenericWrapper(data = "Hello World!") - val data = wrappedString.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxWrapper = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val getGenericData = taskFactory.compose(classLoader.createSandboxFunction()).apply(GetGenericData::class.java) - val result = getGenericData.apply(sandboxWrapper) ?: fail("Result cannot be null") - - assertEquals("Hello World!", result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - @Test - fun `test deserializing generic wrapper with Integer`() { - val wrappedInteger = GenericWrapper(data = 1000) - val data = wrappedInteger.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxWrapper = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val getGenericData = taskFactory.compose(classLoader.createSandboxFunction()).apply(GetGenericData::class.java) - val result = getGenericData.apply(sandboxWrapper) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Integer", result::class.java.name) - assertEquals(1000, classLoader.createBasicOutput().apply(result)) - } - } - - @Test - fun `test deserializing generic wrapper with array of Integer`() { - val wrappedArrayOfInteger = GenericWrapper(arrayOf(1000, 2000, 3000)) - val data = wrappedArrayOfInteger.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxWrapper = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val getGenericData = taskFactory.compose(classLoader.createSandboxFunction()).apply(GetGenericData::class.java) - val result = getGenericData.apply(sandboxWrapper) ?: fail("Result cannot be null") - - assertEquals("[Lsandbox.java.lang.Integer;", result::class.java.name) - assertThat(classLoader.createBasicOutput().apply(result)) - .isEqualTo(arrayOf(1000, 2000, 3000)) - } - } - - @Test - fun `test deserializing generic wrapper with primitive int array`() { - val wrappedArrayOfInteger = GenericWrapper(intArrayOf(1000, 2000, 3000)) - val data = wrappedArrayOfInteger.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxWrapper = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val getGenericData = taskFactory.compose(classLoader.createSandboxFunction()).apply(GetGenericData::class.java) - val result = getGenericData.apply(sandboxWrapper) ?: fail("Result cannot be null") - - assertEquals("[I", result::class.java.name) - assertThat(classLoader.createBasicOutput().apply(result)) - .isEqualTo(intArrayOf(1000, 2000, 3000)) - } - } - - @Test - fun `test deserializing generic list`() { - val wrappedList = GenericWrapper(data = listOf("Hello World!")) - val data = wrappedList.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxWrapper = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val sandboxFunction = classLoader.createSandboxFunction() - val getGenericData = taskFactory.compose(sandboxFunction).apply(GetGenericData::class.java) - val dataResult = getGenericData.apply(sandboxWrapper) ?: fail("Result cannot be null") - - assertEquals("[Hello World!]", dataResult.toString()) - assertEquals("sandbox.java.util.Collections\$UnmodifiableRandomAccessList", dataResult::class.java.name) - - val getGenericIterableData = taskFactory.compose(sandboxFunction).apply(GetGenericIterableData::class.java) - val dataItemResult = getGenericIterableData.apply(sandboxWrapper) ?: fail("Result cannot be null") - assertEquals(SANDBOX_STRING, dataItemResult::class.java.name) - } - } - - class GetGenericData : Function, Any?> { - override fun apply(input: GenericWrapper): Any? { - return input.data - } - } - - class GetGenericIterableData : Function>, Any?> { - override fun apply(input: GenericWrapper>): Any? { - return input.data.iterator().let { - if (it.hasNext()) { - it.next() - } else { - null - } - } - } - } - - @Test - fun `test deserializing concrete wrapper`() { - val wrapped = ConcreteWrapper( - first = GenericWrapper("Hello World"), - second = GenericWrapper('!') - ) - val data = wrapped.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxWrapped = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showConcreteWrapper = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowConcreteWrapper::class.java) - val result = showConcreteWrapper.apply(sandboxWrapped) ?: fail("Result cannot be null") - - assertEquals("Concrete: first='Hello World', second='!'", result.toString()) - } - } - - class ShowConcreteWrapper : Function { - override fun apply(input: ConcreteWrapper): String { - return "Concrete: first='${input.first.data}', second='${input.second.data}'" - } - } -} - -@CordaSerializable -data class GenericWrapper(val data: T) - -@CordaSerializable -data class ConcreteWrapper( - val first: GenericWrapper, - val second: GenericWrapper -) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeInputStreamTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeInputStreamTest.kt deleted file mode 100644 index 37af4815c7..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeInputStreamTest.kt +++ /dev/null @@ -1,43 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.internal.readFully -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.io.InputStream -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeInputStreamTest : TestBase(KOTLIN) { - companion object { - const val MESSAGE = "Round and round the rugged rocks..." - } - - @Test - fun `test deserializing input stream`() { - val data = MESSAGE.byteInputStream().serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxStream = data.deserializeTypeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showInputStream = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowInputStream::class.java) - val result = showInputStream.apply(sandboxStream) ?: fail("Result cannot be null") - - assertEquals(String(MESSAGE.byteInputStream().readFully()), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowInputStream : Function { - override fun apply(input: InputStream): String { - return String(input.readFully()) - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeInstantTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeInstantTest.kt deleted file mode 100644 index 33e7685513..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeInstantTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.Instant -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeInstantTest : TestBase(KOTLIN) { - @Test - fun `test deserializing instant`() { - val instant = Instant.now() - val data = instant.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxInstant = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showInstant = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowInstant::class.java) - val result = showInstant.apply(sandboxInstant) ?: fail("Result cannot be null") - - assertEquals(instant.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowInstant : Function { - override fun apply(instant: Instant): String { - return instant.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeJavaWithMultipleConstructorsTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeJavaWithMultipleConstructorsTest.kt deleted file mode 100644 index 26381f6a34..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeJavaWithMultipleConstructorsTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail - -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeJavaWithMultipleConstructorsTest : TestBase(KOTLIN) { - @Test - fun `test deserializing existing class`() { - val multiData = MultiConstructorData("Hello World", Long.MAX_VALUE, '!') - val data = multiData.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxData = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showMultiData = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowMultiData::class.java) - val result = showMultiData.apply(sandboxData) ?: fail("Result cannot be null") - - assertThat(result.toString()) - .isEqualTo("MultiConstructor[message='Hello World', bigNumber=9223372036854775807, tag=!]") - } - } - - class ShowMultiData : Function { - override fun apply(data: MultiConstructorData): String { - return data.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeKotlinAliasTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeKotlinAliasTest.kt deleted file mode 100644 index bc2c023db2..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeKotlinAliasTest.kt +++ /dev/null @@ -1,69 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.crypto.SecureHash -import net.corda.core.node.services.AttachmentId -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeKotlinAliasTest : TestBase(KOTLIN) { - @Test - fun `test deserializing kotlin alias`() { - val attachmentId = SecureHash.allOnesHash - val data = attachmentId.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxAttachmentId = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showAlias = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowAlias::class.java) - val result = showAlias.apply(sandboxAttachmentId) ?: fail("Result cannot be null") - - assertEquals(ShowAlias().apply(attachmentId), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowAlias : Function { - override fun apply(id: AttachmentId): String { - return id.toString() - } - } - - @Test - fun `test deserializing data with kotlin alias`() { - val attachment = AttachmentData(SecureHash.allOnesHash) - val data = attachment.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxAttachment = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showAliasData = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowAliasData::class.java) - val result = showAliasData.apply(sandboxAttachment) ?: fail("Result cannot be null") - - assertEquals(ShowAliasData().apply(attachment), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowAliasData: Function { - override fun apply(data: AttachmentData): String { - return data.toString() - } - } -} - -@CordaSerializable -data class AttachmentData(val id: AttachmentId) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeLocalDateTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeLocalDateTest.kt deleted file mode 100644 index efc6c28c6e..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeLocalDateTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.LocalDate -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeLocalDateTest : TestBase(KOTLIN) { - @Test - fun `test deserializing local date`() { - val date = LocalDate.now() - val data = date.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxDate = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showLocalDate = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowLocalDate::class.java) - val result = showLocalDate.apply(sandboxDate) ?: fail("Result cannot be null") - - assertEquals(date.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowLocalDate : Function { - override fun apply(date: LocalDate): String { - return date.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeLocalDateTimeTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeLocalDateTimeTest.kt deleted file mode 100644 index 04c87136e7..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeLocalDateTimeTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.LocalDateTime -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeLocalDateTimeTest : TestBase(KOTLIN) { - @Test - fun `test deserializing local date-time`() { - val dateTime = LocalDateTime.now() - val data = dateTime.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxDateTime = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showLocalDateTime = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowLocalDateTime::class.java) - val result = showLocalDateTime.apply( sandboxDateTime) ?: fail("Result cannot be null") - - assertEquals(dateTime.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowLocalDateTime : Function { - override fun apply(dateTime: LocalDateTime): String { - return dateTime.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeLocalTimeTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeLocalTimeTest.kt deleted file mode 100644 index de5cc7b738..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeLocalTimeTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.LocalTime -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeLocalTimeTest : TestBase(KOTLIN) { - @Test - fun `test deserializing local time`() { - val time = LocalTime.now() - val data = time.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxTime = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showLocalTime = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowLocalTime::class.java) - val result = showLocalTime.apply(sandboxTime) ?: fail("Result cannot be null") - - assertEquals(time.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowLocalTime : Function { - override fun apply(time: LocalTime): String { - return time.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeMapsTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeMapsTest.kt deleted file mode 100644 index 9214a79a5f..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeMapsTest.kt +++ /dev/null @@ -1,203 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.EnumMap -import java.util.NavigableMap -import java.util.SortedMap -import java.util.TreeMap -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeMapsTest : TestBase(KOTLIN) { - @Test - fun `test deserializing map`() { - val stringMap = StringMap(mapOf("Open" to "Hello World", "Close" to "Goodbye, Cruel World")) - val data = stringMap.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxMap = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showStringMap = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowStringMap::class.java) - val result = showStringMap.apply(sandboxMap) ?: fail("Result cannot be null") - - assertEquals(stringMap.values.entries.joinToString(), result.toString()) - assertEquals("Open=Hello World, Close=Goodbye, Cruel World", result.toString()) - } - } - - class ShowStringMap : Function { - override fun apply(data: StringMap): String { - return data.values.entries.joinToString() - } - } - - @Test - fun `test deserializing sorted map`() { - val sortedMap = StringSortedMap(sortedMapOf( - 100 to "Goodbye, Cruel World", - 10 to "Hello World", - 50 to "Having Fun!" - )) - val data = sortedMap.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxMap = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showStringSortedMap = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowStringSortedMap::class.java) - val result = showStringSortedMap.apply(sandboxMap) ?: fail("Result cannot be null") - - assertEquals(sortedMap.values.entries.joinToString(), result.toString()) - assertEquals("10=Hello World, 50=Having Fun!, 100=Goodbye, Cruel World", result.toString()) - } - } - - class ShowStringSortedMap : Function { - override fun apply(data: StringSortedMap): String { - return data.values.entries.joinToString() - } - } - - @Test - fun `test deserializing navigable map`() { - val navigableMap = StringNavigableMap(mapOf( - 10000L to "Goodbye, Cruel World", - 1000L to "Hello World", - 5000L to "Having Fun!" - ).toMap(TreeMap())) - val data = navigableMap.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxMap = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showStringNavigableMap = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowStringNavigableMap::class.java) - val result = showStringNavigableMap.apply(sandboxMap) ?: fail("Result cannot be null") - - assertEquals(navigableMap.values.entries.joinToString(), result.toString()) - assertEquals("1000=Hello World, 5000=Having Fun!, 10000=Goodbye, Cruel World", result.toString()) - } - } - - class ShowStringNavigableMap : Function { - override fun apply(data: StringNavigableMap): String { - return data.values.entries.joinToString() - } - } - - @Test - fun `test deserializing linked hash map`() { - val linkedHashMap = StringLinkedHashMap(linkedMapOf( - "Close" to "Goodbye, Cruel World", - "Open" to "Hello World", - "During" to "Having Fun!" - )) - val data = linkedHashMap.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxMap = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val sandboxFunction = classLoader.createSandboxFunction() - val showStringLinkedHashMap = taskFactory.compose(sandboxFunction).apply(ShowStringLinkedHashMap::class.java) - val result = showStringLinkedHashMap.apply(sandboxMap) ?: fail("Result cannot be null") - - assertEquals(linkedHashMap.values.entries.joinToString(), result.toString()) - assertEquals("Close=Goodbye, Cruel World, Open=Hello World, During=Having Fun!", result.toString()) - } - } - - class ShowStringLinkedHashMap : Function { - override fun apply(data: StringLinkedHashMap): String { - return data.values.entries.joinToString() - } - } - - @Test - fun `test deserializing tree map`() { - val treeMap = StringTreeMap(mapOf( - 10000 to "Goodbye, Cruel World", - 1000 to "Hello World", - 5000 to "Having Fun!" - ).toMap(TreeMap())) - val data = treeMap.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxMap = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showStringTreeMap = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowStringTreeMap::class.java) - val result = showStringTreeMap.apply(sandboxMap) ?: fail("Result cannot be null") - - assertEquals(treeMap.values.entries.joinToString(), result.toString()) - assertEquals("1000=Hello World, 5000=Having Fun!, 10000=Goodbye, Cruel World", result.toString()) - } - } - - class ShowStringTreeMap : Function { - override fun apply(data: StringTreeMap): String { - return data.values.entries.joinToString() - } - } - - @Test - fun `test deserializing enum map`() { - val enumMap = EnumMap(mapOf( - ExampleEnum.ONE to "One!", - ExampleEnum.TWO to "Two!" - )) - val data = enumMap.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxMap = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showEnumMap = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowEnumMap::class.java) - val result = showEnumMap.apply(sandboxMap) ?: fail("Result cannot be null") - - assertEquals(enumMap.toString(), result.toString()) - assertEquals("{ONE=One!, TWO=Two!}", result.toString()) - } - } - - class ShowEnumMap : Function, String> { - override fun apply(data: EnumMap<*, String>): String { - return data.toString() - } - } -} - -@CordaSerializable -class StringMap(val values: Map) - -@CordaSerializable -class StringSortedMap(val values: SortedMap) - -@CordaSerializable -class StringNavigableMap(val values: NavigableMap) - -@CordaSerializable -class StringLinkedHashMap(val values: LinkedHashMap) - -@CordaSerializable -class StringTreeMap(val values: TreeMap) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeMonthDayTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeMonthDayTest.kt deleted file mode 100644 index edc4ca56ab..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeMonthDayTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.MonthDay -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeMonthDayTest : TestBase(KOTLIN) { - @Test - fun `test deserializing month-day`() { - val monthDay = MonthDay.now() - val data = monthDay.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxMonthDay = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showMonthDay = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowMonthDay::class.java) - val result = showMonthDay.apply(sandboxMonthDay) ?: fail("Result cannot be null") - - assertEquals(monthDay.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowMonthDay : Function { - override fun apply(monthDay: MonthDay): String { - return monthDay.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeObjectArraysTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeObjectArraysTest.kt deleted file mode 100644 index 0090ae2535..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeObjectArraysTest.kt +++ /dev/null @@ -1,298 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.UUID -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeObjectArraysTest : TestBase(KOTLIN) { - @Test - fun `test deserializing string array`() { - val stringArray = HasStringArray(arrayOf("Hello", "World", "!")) - val data = stringArray.serialize() - assertEquals("Hello, World, !", ShowStringArray().apply(stringArray)) - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showStringArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowStringArray::class.java) - val result = showStringArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals(SANDBOX_STRING, result::class.java.name) - assertEquals("Hello, World, !", result.toString()) - } - } - - class ShowStringArray : Function { - override fun apply(data: HasStringArray): String { - return data.lines.joinToString() - } - } - - @Test - fun `test deserializing character array`() { - val charArray = HasCharacterArray(arrayOf('H', 'e', 'l', 'l', 'o', '!')) - val data = charArray.serialize() - assertEquals("Hello!", ShowCharacterArray().apply(charArray)) - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showCharacterArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowCharacterArray::class.java) - val result = showCharacterArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals(SANDBOX_STRING, result::class.java.name) - assertEquals("Hello!", result.toString()) - } - } - - class ShowCharacterArray : Function { - override fun apply(data: HasCharacterArray): String { - return data.letters.joinTo(StringBuilder(), separator = "").toString() - } - } - - @Test - fun `test deserializing long array`() { - val longArray = HasLongArray(arrayOf(1000, 2000, 3000, 4000, 5000)) - val data = longArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showLongArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowLongArray::class.java) - val result = showLongArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Long", result::class.java.name) - assertEquals("15000", result.toString()) - } - } - - class ShowLongArray : Function { - override fun apply(data: HasLongArray): Long { - return data.longs.sum() - } - } - - @Test - fun `test deserializing integer array`() { - val integerArray = HasIntegerArray(arrayOf(100, 200, 300, 400, 500)) - val data = integerArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showIntegerArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowIntegerArray::class.java) - val result = showIntegerArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Integer", result::class.java.name) - assertEquals("1500", result.toString()) - } - } - - class ShowIntegerArray : Function { - override fun apply(data: HasIntegerArray): Int { - return data.integers.sum() - } - } - - @Test - fun `test deserializing short array`() { - val shortArray = HasShortArray(arrayOf(100, 200, 300, 400, 500)) - val data = shortArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showShortArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowShortArray::class.java) - val result = showShortArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Integer", result::class.java.name) - assertEquals("1500", result.toString()) - } - } - - class ShowShortArray : Function { - override fun apply(data: HasShortArray): Int { - return data.shorts.sum() - } - } - - @Test - fun `test deserializing byte array`() { - val byteArray = HasByteArray(arrayOf(10, 20, 30, 40, 50)) - val data = byteArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showByteArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowByteArray::class.java) - val result = showByteArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Integer", result::class.java.name) - assertEquals("150", result.toString()) - } - } - - class ShowByteArray : Function { - override fun apply(data: HasByteArray): Int { - return data.bytes.sum() - } - } - - @Test - fun `test deserializing double array`() { - val doubleArray = HasDoubleArray(arrayOf(1000.0, 2000.0, 3000.0, 4000.0, 5000.0)) - val data = doubleArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showDoubleArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowDoubleArray::class.java) - val result = showDoubleArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Double", result::class.java.name) - assertEquals("15000.0", result.toString()) - } - } - - class ShowDoubleArray : Function { - override fun apply(data: HasDoubleArray): Double { - return data.doubles.sum() - } - } - - @Test - fun `test deserializing float array`() { - val floatArray = HasFloatArray(arrayOf(10.0f, 20.0f, 30.0f, 40.0f, 50.0f)) - val data = floatArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showFloatArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowFloatArray::class.java) - val result = showFloatArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Float", result::class.java.name) - assertEquals("150.0", result.toString()) - } - } - - class ShowFloatArray : Function { - override fun apply(data: HasFloatArray): Float { - return data.floats.sum() - } - } - - @Test - fun `test deserializing boolean array`() { - val booleanArray = HasBooleanArray(arrayOf(true, true, true)) - val data = booleanArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showBooleanArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowBooleanArray::class.java) - val result = showBooleanArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Boolean", result::class.java.name) - assertEquals("true", result.toString()) - } - } - - class ShowBooleanArray : Function { - override fun apply(data: HasBooleanArray): Boolean { - return data.bools.all { it } - } - } - - @Test - fun `test deserializing uuid array`() { - val uuid = UUID.randomUUID() - val uuidArray = HasUUIDArray(arrayOf(uuid)) - val data = uuidArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showUUIDArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowUUIDArray::class.java) - val result = showUUIDArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals(SANDBOX_STRING, result::class.java.name) - assertEquals(uuid.toString(), result.toString()) - } - } - - class ShowUUIDArray : Function { - override fun apply(data: HasUUIDArray): String { - return data.uuids.joinTo(StringBuilder()).toString() - } - } -} - -@CordaSerializable -class HasStringArray(val lines: Array) - -@CordaSerializable -class HasCharacterArray(val letters: Array) - -@CordaSerializable -class HasLongArray(val longs: Array) - -@CordaSerializable -class HasIntegerArray(val integers: Array) - -@CordaSerializable -class HasShortArray(val shorts: Array) - -@CordaSerializable -class HasByteArray(val bytes: Array) - -@CordaSerializable -class HasDoubleArray(val doubles: Array) - -@CordaSerializable -class HasFloatArray(val floats: Array) - -@CordaSerializable -class HasBooleanArray(val bools: Array) - -@CordaSerializable -class HasUUIDArray(val uuids: Array) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetDateTimeTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetDateTimeTest.kt deleted file mode 100644 index d21d135f1c..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetDateTimeTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.OffsetDateTime -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeOffsetDateTimeTest : TestBase(KOTLIN) { - @Test - fun `test deserializing offset date-time`() { - val dateTime = OffsetDateTime.now() - val data = dateTime.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxDateTime = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showOffsetDateTime = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowOffsetDateTime::class.java) - val result = showOffsetDateTime.apply(sandboxDateTime) ?: fail("Result cannot be null") - - assertEquals(dateTime.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowOffsetDateTime : Function { - override fun apply(dateTime: OffsetDateTime): String { - return dateTime.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt deleted file mode 100644 index c23fe08ebe..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.OffsetTime -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeOffsetTimeTest : TestBase(KOTLIN) { - @Test - fun `test deserializing offset time`() { - val time = OffsetTime.now() - val data = time.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxTime = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showOffsetTime = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowOffsetTime::class.java) - val result = showOffsetTime.apply(sandboxTime) ?: fail("Result cannot be null") - - assertEquals(time.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowOffsetTime : Function { - override fun apply(time: OffsetTime): String { - return time.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOpaqueBytesSubSequenceTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOpaqueBytesSubSequenceTest.kt deleted file mode 100644 index ca1041af21..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOpaqueBytesSubSequenceTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.core.utilities.OpaqueBytesSubSequence -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeOpaqueBytesSubSequenceTest : TestBase(KOTLIN) { - companion object { - const val MESSAGE = "The rain in spain falls mainly on the plain." - const val OFFSET = MESSAGE.length / 2 - } - - @Test - fun `test deserializing opaquebytes subsequence`() { - val subSequence = OpaqueBytesSubSequence( - bytes = MESSAGE.toByteArray(), - offset = OFFSET, - size = MESSAGE.length - OFFSET - ) - val data = subSequence.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxBytes = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val sandboxFunction = classLoader.createSandboxFunction() - val showOpaqueBytesSubSequence = taskFactory.compose(sandboxFunction).apply(ShowOpaqueBytesSubSequence::class.java) - val result = showOpaqueBytesSubSequence.apply(sandboxBytes) ?: fail("Result cannot be null") - - assertEquals(MESSAGE.substring(OFFSET), String(result as ByteArray)) - assertEquals(String(subSequence.copyBytes()), String(result)) - } - } - - class ShowOpaqueBytesSubSequence : Function { - override fun apply(sequence: OpaqueBytesSubSequence): ByteArray { - return sequence.copyBytes() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOptionalTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOptionalTest.kt deleted file mode 100644 index f908ff439e..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOptionalTest.kt +++ /dev/null @@ -1,58 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.Optional -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeOptionalTest : TestBase(KOTLIN) { - @Test - fun `test deserializing optional with object`() { - val optional = Optional.of("Hello World!") - val data = optional.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxOptional = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showOptional = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowOptional::class.java) - val result = showOptional.apply(sandboxOptional) ?: fail("Result cannot be null") - - assertEquals("Optional -> Optional[Hello World!]", result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - @Test - fun `test deserializing optional without object`() { - val optional = Optional.empty() - val data = optional.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxOptional = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showOptional = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowOptional::class.java) - val result = showOptional.apply(sandboxOptional) ?: fail("Result cannot be null") - - assertEquals("Optional -> Optional.empty", result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } -} - -class ShowOptional : Function, String> { - override fun apply(optional: Optional<*>): String { - return "Optional -> $optional" - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePeriodTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePeriodTest.kt deleted file mode 100644 index 781d74a634..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePeriodTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.Period -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializePeriodTest : TestBase(KOTLIN) { - @Test - fun `test deserializing period`() { - val period = Period.of(1, 2, 3) - val data = period.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxPeriod = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showPeriod = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowPeriod::class.java) - val result = showPeriod.apply(sandboxPeriod) ?: fail("Result cannot be null") - - assertEquals(period.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowPeriod : Function { - override fun apply(period: Period): String { - return period.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePrimitiveArraysTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePrimitiveArraysTest.kt deleted file mode 100644 index 74c7af3e16..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePrimitiveArraysTest.kt +++ /dev/null @@ -1,239 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializePrimitiveArraysTest : TestBase(KOTLIN) { - @Test - fun `test deserializing character array`() { - val charArray = PrimitiveCharArray(charArrayOf('H', 'e', 'l', 'l', 'o', '!')) - val data = charArray.serialize() - assertEquals("Hello!", ShowCharArray().apply(charArray)) - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showCharArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowCharArray::class.java) - val result = showCharArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals(SANDBOX_STRING, result::class.java.name) - assertEquals("Hello!", result.toString()) - } - } - - class ShowCharArray : Function { - override fun apply(data: PrimitiveCharArray): String { - return data.letters.joinTo(StringBuilder(), separator = "").toString() - } - } - - @Test - fun `test deserializing integer array`() { - val intArray = PrimitiveIntegerArray(intArrayOf(100, 200, 300, 400, 500)) - val data = intArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showIntegerArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowIntegerArray::class.java) - val result = showIntegerArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Integer", result::class.java.name) - assertEquals("1500", result.toString()) - } - } - - class ShowIntegerArray : Function { - override fun apply(data: PrimitiveIntegerArray): Int { - return data.integers.sum() - } - } - - @Test - fun `test deserializing long array`() { - val longArray = PrimitiveLongArray(longArrayOf(1000, 2000, 3000, 4000, 5000)) - val data = longArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showLongArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowLongArray::class.java) - val result = showLongArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Long", result::class.java.name) - assertEquals("15000", result.toString()) - } - } - - class ShowLongArray : Function { - override fun apply(data: PrimitiveLongArray): Long { - return data.longs.sum() - } - } - - @Test - fun `test deserializing short array`() { - val shortArray = PrimitiveShortArray(shortArrayOf(100, 200, 300, 400, 500)) - val data = shortArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showShortArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowShortArray::class.java) - val result = showShortArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Integer", result::class.java.name) - assertEquals("1500", result.toString()) - } - } - - class ShowShortArray : Function { - override fun apply(data: PrimitiveShortArray): Int { - return data.shorts.sum() - } - } - - @Test - fun `test deserializing byte array`() { - val byteArray = PrimitiveByteArray(byteArrayOf(10, 20, 30, 40, 50)) - val data = byteArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showByteArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowByteArray::class.java) - val result = showByteArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Integer", result::class.java.name) - assertEquals("150", result.toString()) - } - } - - class ShowByteArray : Function { - override fun apply(data: PrimitiveByteArray): Int { - return data.bytes.sum() - } - } - - @Test - fun `test deserializing boolean array`() { - val booleanArray = PrimitiveBooleanArray(booleanArrayOf(true, true)) - val data = booleanArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showBooleanArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowBooleanArray::class.java) - val result = showBooleanArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Boolean", result::class.java.name) - assertEquals("true", result.toString()) - } - } - - class ShowBooleanArray : Function { - override fun apply(data: PrimitiveBooleanArray): Boolean { - return data.bools.all { it } - } - } - - @Test - fun `test deserializing double array`() { - val doubleArray = PrimitiveDoubleArray(doubleArrayOf(1000.0, 2000.0, 3000.0, 4000.0, 5000.0)) - val data = doubleArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showDoubleArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowDoubleArray::class.java) - val result = showDoubleArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Double", result::class.java.name) - assertEquals("15000.0", result.toString()) - } - } - - class ShowDoubleArray : Function { - override fun apply(data: PrimitiveDoubleArray): Double { - return data.doubles.sum() - } - } - - @Test - fun `test deserializing float array`() { - val floatArray = PrimitiveFloatArray(floatArrayOf(100.0f, 200.0f, 300.0f, 400.0f, 500.0f)) - val data = floatArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showFloatArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowFloatArray::class.java) - val result = showFloatArray.apply(sandboxArray) ?: fail("Result cannot be null") - - assertEquals("sandbox.java.lang.Float", result::class.java.name) - assertEquals("1500.0", result.toString()) - } - } - - class ShowFloatArray : Function { - override fun apply(data: PrimitiveFloatArray): Float { - return data.floats.sum() - } - } -} - -@CordaSerializable -class PrimitiveCharArray(val letters: CharArray) - -@CordaSerializable -class PrimitiveShortArray(val shorts: ShortArray) - -@CordaSerializable -class PrimitiveIntegerArray(val integers: IntArray) - -@CordaSerializable -class PrimitiveLongArray(val longs: LongArray) - -@CordaSerializable -class PrimitiveByteArray(val bytes: ByteArray) - -@CordaSerializable -class PrimitiveBooleanArray(val bools: BooleanArray) - -@CordaSerializable -class PrimitiveDoubleArray(val doubles: DoubleArray) - -@CordaSerializable -class PrimitiveFloatArray(val floats: FloatArray) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePrimitivesTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePrimitivesTest.kt deleted file mode 100644 index 00c5fce429..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePrimitivesTest.kt +++ /dev/null @@ -1,76 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import java.util.Date -import java.util.UUID - -@ExtendWith(LocalSerialization::class) -class DeserializePrimitivesTest : TestBase(KOTLIN) { - @Test - fun `test naked uuid`() { - val uuid = UUID.randomUUID() - val data = uuid.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxUUID = data.deserializeFor(classLoader) - assertEquals(uuid.toString(), sandboxUUID.toString()) - assertEquals("sandbox.${uuid::class.java.name}", sandboxUUID::class.java.name) - } - } - - @Test - fun `test wrapped uuid`() { - val uuid = WrappedUUID(UUID.randomUUID()) - val data = uuid.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxUUID = data.deserializeFor(classLoader) - assertEquals(uuid.toString(), sandboxUUID.toString()) - assertEquals("sandbox.${uuid::class.java.name}", sandboxUUID::class.java.name) - } - } - - @Test - fun `test naked date`() { - val now = Date() - val data = now.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxNow = data.deserializeFor(classLoader) - assertEquals(now.toString(), sandboxNow.toString()) - assertEquals("sandbox.${now::class.java.name}", sandboxNow::class.java.name) - } - } - - @Test - fun `test wrapped date`() { - val now = WrappedDate(Date()) - val data = now.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxNow = data.deserializeFor(classLoader) - assertEquals(now.toString(), sandboxNow.toString()) - assertEquals("sandbox.${now::class.java.name}", sandboxNow::class.java.name) - } - } -} - -@CordaSerializable -data class WrappedUUID(val uuid: UUID) - -@CordaSerializable -data class WrappedDate(val date: Date) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeProtonJTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeProtonJTest.kt deleted file mode 100644 index 579d8b7a16..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeProtonJTest.kt +++ /dev/null @@ -1,245 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.apache.qpid.proton.amqp.Decimal128 -import org.apache.qpid.proton.amqp.Decimal32 -import org.apache.qpid.proton.amqp.Decimal64 -import org.apache.qpid.proton.amqp.Symbol -import org.apache.qpid.proton.amqp.UnsignedByte -import org.apache.qpid.proton.amqp.UnsignedInteger -import org.apache.qpid.proton.amqp.UnsignedLong -import org.apache.qpid.proton.amqp.UnsignedShort -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeProtonJTest : TestBase(KOTLIN) { - @Test - fun `test deserializing unsigned long`() { - val protonJ = HasUnsignedLong(UnsignedLong.valueOf(12345678)) - val data = protonJ.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxProtonJ = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showUnsignedLong = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowUnsignedLong::class.java) - val result = showUnsignedLong.apply(sandboxProtonJ) ?: fail("Result cannot be null") - - assertEquals(protonJ.number.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowUnsignedLong : Function { - override fun apply(data: HasUnsignedLong): String { - return data.number.toString() - } - } - - @Test - fun `test deserializing unsigned integer`() { - val protonJ = HasUnsignedInteger(UnsignedInteger.valueOf(123456)) - val data = protonJ.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxProtonJ = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showUnsignedInteger = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowUnsignedInteger::class.java) - val result = showUnsignedInteger.apply(sandboxProtonJ) ?: fail("Result cannot be null") - - assertEquals(protonJ.number.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowUnsignedInteger : Function { - override fun apply(data: HasUnsignedInteger): String { - return data.number.toString() - } - } - - @Test - fun `test deserializing unsigned short`() { - val protonJ = HasUnsignedShort(UnsignedShort.valueOf(12345)) - val data = protonJ.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxProtonJ = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showUnsignedShort = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowUnsignedShort::class.java) - val result = showUnsignedShort.apply(sandboxProtonJ) ?: fail("Result cannot be null") - - assertEquals(protonJ.number.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowUnsignedShort : Function { - override fun apply(data: HasUnsignedShort): String { - return data.number.toString() - } - } - - @Test - fun `test deserializing unsigned byte`() { - val protonJ = HasUnsignedByte(UnsignedByte.valueOf(0x8f.toByte())) - val data = protonJ.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxProtonJ = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showUnsignedByte = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowUnsignedByte::class.java) - val result = showUnsignedByte.apply(sandboxProtonJ) ?: fail("Result cannot be null") - - assertEquals(protonJ.number.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowUnsignedByte : Function { - override fun apply(data: HasUnsignedByte): String { - return data.number.toString() - } - } - - @Test - fun `test deserializing 128 bit decimal`() { - val protonJ = HasDecimal128(Decimal128(12345678, 98765432)) - val data = protonJ.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxProtonJ = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showDecimal128 = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowDecimal128::class.java) - val result = showDecimal128.apply(sandboxProtonJ) ?: fail("Result cannot be null") - - assertThat(result) - .isEqualTo(protonJ.number.let { longArrayOf(it.mostSignificantBits, it.leastSignificantBits) }) - } - } - - class ShowDecimal128 : Function { - override fun apply(data: HasDecimal128): LongArray { - return data.number.let { longArrayOf(it.mostSignificantBits, it.leastSignificantBits) } - } - } - - @Test - fun `test deserializing 64 bit decimal`() { - val protonJ = HasDecimal64(Decimal64(98765432)) - val data = protonJ.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxProtonJ = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showDecimal64 = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowDecimal64::class.java) - val result = showDecimal64.apply(sandboxProtonJ) ?: fail("Result cannot be null") - - assertEquals(protonJ.number.bits.toString(), result.toString()) - } - } - - class ShowDecimal64 : Function { - override fun apply(data: HasDecimal64): Long { - return data.number.bits - } - } - - @Test - fun `test deserializing 32 bit decimal`() { - val protonJ = HasDecimal32(Decimal32(123456)) - val data = protonJ.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxProtonJ = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showDecimal32 = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowDecimal32::class.java) - val result = showDecimal32.apply(sandboxProtonJ) ?: fail("Result cannot be null") - - assertEquals(protonJ.number.bits.toString(), result.toString()) - } - } - - class ShowDecimal32 : Function { - override fun apply(data: HasDecimal32): Int { - return data.number.bits - } - } - - @Test - fun `test deserializing symbol`() { - val protonJ = HasSymbol(Symbol.valueOf("-my-symbol-value-")) - val data = protonJ.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxProtonJ = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showSymbol = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowSymbol::class.java) - val result = showSymbol.apply(sandboxProtonJ) ?: fail("Result cannot be null") - - assertEquals(protonJ.symbol.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowSymbol : Function { - override fun apply(data: HasSymbol): String { - return data.symbol.toString() - } - } -} - -@CordaSerializable -class HasUnsignedLong(val number: UnsignedLong) - -@CordaSerializable -class HasUnsignedInteger(val number: UnsignedInteger) - -@CordaSerializable -class HasUnsignedShort(val number: UnsignedShort) - -@CordaSerializable -class HasUnsignedByte(val number: UnsignedByte) - -@CordaSerializable -class HasDecimal32(val number: Decimal32) - -@CordaSerializable -class HasDecimal64(val number: Decimal64) - -@CordaSerializable -class HasDecimal128(val number: Decimal128) - -@CordaSerializable -class HasSymbol(val symbol: Symbol) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePublicKeyTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePublicKeyTest.kt deleted file mode 100644 index 778a59fde5..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializePublicKeyTest.kt +++ /dev/null @@ -1,106 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.crypto.CompositeKey -import net.corda.core.crypto.Crypto -import net.corda.core.crypto.SignatureScheme -import net.corda.core.internal.hash -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.ExtensionContext -import org.junit.jupiter.api.fail -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.ArgumentsProvider -import org.junit.jupiter.params.provider.ArgumentsSource -import java.security.PublicKey -import java.util.function.Function -import java.util.stream.Stream - -@ExtendWith(LocalSerialization::class) -@Disabled -class DeserializePublicKeyTest : TestBase(KOTLIN) { - class SignatureSchemeProvider : ArgumentsProvider { - override fun provideArguments(context: ExtensionContext?): Stream { - return Crypto.supportedSignatureSchemes().stream() - .filter { it != Crypto.COMPOSITE_KEY } - .map { Arguments.of(it) } - } - } - - @ArgumentsSource(SignatureSchemeProvider::class) - @ParameterizedTest(name = "{index} => {0}") - fun `test deserializing public key`(signatureScheme: SignatureScheme) { - val keyPair = Crypto.generateKeyPair(signatureScheme) - val publicKey = PublicKeyData(keyPair.public) - val data = publicKey.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxKey = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showPublicKey = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowPublicKey::class.java) - val result = showPublicKey.apply(sandboxKey) ?: fail("Result cannot be null") - - assertEquals(ShowPublicKey().apply(publicKey), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - @Test - fun `test composite public key`() { - val key1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256).public - val key2 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256).public - val key3 = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512).public - - val compositeKey = CompositeKey.Builder() - .addKey(key1, weight = 1) - .addKey(key2, weight = 1) - .addKey(key3, weight = 1) - .build(2) - val compositeData = PublicKeyData(compositeKey) - val data = compositeData.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxData = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory().compose(classLoader.createSandboxFunction()) - val showPublicKey = taskFactory.apply(ShowPublicKey::class.java) - val result = showPublicKey.apply(sandboxData) ?: fail("Result cannot be null") - - assertEquals(ShowPublicKey().apply(compositeData), result.toString()) - - val sandboxKey = taskFactory.apply(GetPublicKey::class.java) - .apply(sandboxData) ?: fail("PublicKey cannot be null") - assertThat(sandboxKey::class.java.name) - .isEqualTo("sandbox." + CompositeKey::class.java.name) - } - } - - class ShowPublicKey : Function { - override fun apply(data: PublicKeyData): String { - return with(data) { - "PublicKey: algorithm='${key.algorithm}', format='${key.format}', hash=${key.hash}" - } - } - } - - class GetPublicKey : Function { - override fun apply(data: PublicKeyData): PublicKey { - return data.key - } - } -} - -@CordaSerializable -data class PublicKeyData(val key: PublicKey) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeRemoteCustomisedEnumTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeRemoteCustomisedEnumTest.kt deleted file mode 100644 index 7c2f5ffee7..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeRemoteCustomisedEnumTest.kt +++ /dev/null @@ -1,144 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.SerializedBytes -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -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 org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.EnumSource -import java.util.function.Function - -/** - * Corda 4.4 briefly serialised [Enum] values using [Enum.toString] rather - * than [Enum.name]. We need to be able to deserialise these values now - * that the bug has been fixed. - */ -@ExtendWith(LocalSerialization::class) -class DeserializeRemoteCustomisedEnumTest : TestBase(KOTLIN) { - @ParameterizedTest - @EnumSource(Broken::class) - fun `test deserialize broken enum with custom toString`(broken: Broken) { - val workingData = broken.serialize().rewriteEnumAsWorking() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxWorkingClass = classLoader.toSandboxClass(Working::class.java) - val sandboxWorkingValue = workingData.deserializeFor(classLoader) - assertThat(sandboxWorkingValue::class.java).isSameAs(sandboxWorkingClass) - assertThat(sandboxWorkingValue.toString()).isEqualTo(broken.label) - } - } - - /** - * This function rewrites the [SerializedBytes] for a naked [Broken] object - * into the [SerializedBytes] that Corda 4.4 would generate for an equivalent - * [Working] object. - */ - @Suppress("unchecked_cast") - private fun SerializedBytes.rewriteEnumAsWorking(): SerializedBytes { - val envelope = DeserializationInput.getEnvelope(this).apply { - val restrictedType = schema.types[0] as RestrictedType - (schema.types as MutableList)[0] = restrictedType.copy( - name = toWorking(restrictedType.name) - ) - } - return SerializedBytes(envelope.write()) - } - - @ParameterizedTest - @EnumSource(Broken::class) - fun `test deserialize composed broken enum with custom toString`(broken: Broken) { - val brokenContainer = BrokenContainer(broken) - val workingData = brokenContainer.serialize().rewriteContainerAsWorking() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxContainer = workingData.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showWorkingData = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowWorkingData::class.java) - val result = showWorkingData.apply(sandboxContainer) ?: fail("Result cannot be null") - - assertEquals("Working: label='${broken.label}', ordinal='${broken.ordinal}'", result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowWorkingData : Function { - override fun apply(input: WorkingContainer): String { - return with(input) { - "Working: label='${value.label}', ordinal='${value.ordinal}'" - } - } - } - - /** - * This function rewrites the [SerializedBytes] for a [Broken] - * property that has been composed inside a [BrokenContainer]. - * It will generate the [SerializedBytes] that Corda 4.4 would - * generate for an equivalent [WorkingContainer]. - */ - @Suppress("unchecked_cast") - private fun SerializedBytes.rewriteContainerAsWorking(): SerializedBytes { - val envelope = DeserializationInput.getEnvelope(this).apply { - val compositeType = schema.types[0] as CompositeType - (schema.types as MutableList)[0] = compositeType.copy( - name = toWorking(compositeType.name), - fields = compositeType.fields.map { it.copy(type = toWorking(it.type)) } - ) - val restrictedType = schema.types[1] as RestrictedType - (schema.types as MutableList)[1] = restrictedType.copy( - name = toWorking(restrictedType.name) - ) - } - return SerializedBytes(envelope.write()) - } - - private fun toWorking(oldName: String): String = oldName.replace("Broken", "Working") - - /** - * This is the enumerated type, as it actually exist. - */ - @Suppress("unused") - enum class Working(val label: String) { - ZERO("None"), - ONE("Once"), - TWO("Twice"); - - @Override - override fun toString(): String = label - } - - @CordaSerializable - data class WorkingContainer(val value: Working) - - /** - * This represents a broken serializer's view of the [Working] - * enumerated type, which would serialize using [Enum.toString] - * rather than [Enum.name]. - */ - @Suppress("unused") - @CordaSerializable - enum class Broken(val label: String) { - None("None"), - Once("Once"), - Twice("Twice"); - - @Override - override fun toString(): String = label - } - - @CordaSerializable - data class BrokenContainer(val value: Broken) -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeStringBufferTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeStringBufferTest.kt deleted file mode 100644 index 35a5f9cfb0..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeStringBufferTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeStringBufferTest : TestBase(KOTLIN) { - @Test - fun `test deserializing string buffer`() { - val buffer = StringBuffer("Hello World!") - val data = buffer.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxBuffer = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showStringBuffer = taskFactory.compose(classLoader.createSandboxFunction()).apply( ShowStringBuffer::class.java) - val result = showStringBuffer.apply(sandboxBuffer) ?: fail("Result cannot be null") - - assertEquals(ShowStringBuffer().apply(buffer), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowStringBuffer : Function { - override fun apply(buffer: StringBuffer): String { - return buffer.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeStringTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeStringTest.kt deleted file mode 100644 index 914ca2f387..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeStringTest.kt +++ /dev/null @@ -1,71 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeStringTest : TestBase(KOTLIN) { - @Test - fun `test deserializing string`() { - val stringMessage = StringMessage("Hello World!") - val data = stringMessage.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxString = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showStringMessage = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowStringMessage::class.java) - val result = showStringMessage.apply(sandboxString) ?: fail("Result cannot be null") - - assertEquals(stringMessage.message, result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowStringMessage : Function { - override fun apply(data: StringMessage): String { - return data.message - } - } - - @Test - fun `test deserializing string list of arrays`() { - val stringListArray = StringListOfArray(listOf( - arrayOf("Hello"), arrayOf("World"), arrayOf("!")) - ) - val data = stringListArray.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxListArray = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showStringListOfArray = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowStringListOfArray::class.java) - val result = showStringListOfArray.apply(sandboxListArray) ?: fail("Result cannot be null") - - assertEquals(stringListArray.data.flatMap(Array::toList).joinToString(), result.toString()) - } - } - - class ShowStringListOfArray : Function { - override fun apply(obj: StringListOfArray): String { - return obj.data.flatMap(Array::toList).joinToString() - } - } -} - -@CordaSerializable -data class StringMessage(val message: String) - -@CordaSerializable -class StringListOfArray(val data: List>) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeWithCustomSerializerTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeWithCustomSerializerTest.kt deleted file mode 100644 index 36a0a65178..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeWithCustomSerializerTest.kt +++ /dev/null @@ -1,88 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.SerializationCustomSerializer -import net.corda.core.serialization.internal.MissingSerializerException -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.RegisterExtension -import org.junit.jupiter.api.fail -import java.util.function.Function - -class DeserializeWithCustomSerializerTest: TestBase(KOTLIN) { - companion object { - const val MESSAGE = "Hello Sandbox!" - - @Suppress("unused") - @BeforeAll - @JvmStatic - fun checkData() { - assertNotCordaSerializable() - } - } - - @RegisterExtension - @JvmField - val serialization = LocalSerialization(setOf(CustomSerializer()), emptySet()) - - @Test - fun `test deserializing custom object`() { - val custom = CustomData(MESSAGE) - val data = custom.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv( - classLoader = classLoader, - customSerializerClassNames = setOf(CustomSerializer::class.java.name), - serializationWhitelistNames = emptySet() - )) - - val sandboxCustom = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showCustom = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowCustomData::class.java) - val result = showCustom.apply(sandboxCustom) ?: fail("Result cannot be null") - - assertEquals(custom.value, result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - @Test - fun `test deserialization needs custom serializer`() { - val custom = CustomData(MESSAGE) - val data = custom.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertThrows { data.deserializeFor(classLoader) } - } - } - - class ShowCustomData : Function { - override fun apply(custom: CustomData): String { - return custom.value - } - } - - /** - * This class REQUIRES a custom serializer because its - * constructor parameter cannot be mapped to a property - * automatically. THIS IS DELIBERATE! - */ - class CustomData(initialValue: String) { - // DO NOT MOVE THIS PROPERTY INTO THE CONSTRUCTOR! - val value: String = initialValue - } - - class CustomSerializer : SerializationCustomSerializer { - data class Proxy(val value: String) - - override fun fromProxy(proxy: Proxy): CustomData = CustomData(proxy.value) - override fun toProxy(obj: CustomData): Proxy = Proxy(obj.value) - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeWithObjectCustomSerializerTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeWithObjectCustomSerializerTest.kt deleted file mode 100644 index 2b86b220db..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeWithObjectCustomSerializerTest.kt +++ /dev/null @@ -1,75 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.SerializationCustomSerializer -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.RegisterExtension -import org.junit.jupiter.api.fail -import java.util.function.Function - -class DeserializeWithObjectCustomSerializerTest: TestBase(KOTLIN) { - companion object { - const val MESSAGE = "Hello Sandbox!" - - @Suppress("unused") - @BeforeAll - @JvmStatic - fun checkData() { - assertNotCordaSerializable() - } - } - - @RegisterExtension - @JvmField - val serialization = LocalSerialization(setOf(ObjectCustomSerializer), emptySet()) - - @Test - fun `test deserializing custom object with object serializer`() { - val custom = CustomData(MESSAGE) - val data = custom.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv( - classLoader = classLoader, - customSerializerClassNames = setOf(ObjectCustomSerializer::class.java.name), - serializationWhitelistNames = emptySet() - )) - - val sandboxCustom = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showCustom = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowCustomData::class.java) - val result = showCustom.apply(sandboxCustom) ?: fail("Result cannot be null") - - assertEquals(custom.value, result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowCustomData : Function { - override fun apply(custom: CustomData): String { - return custom.value - } - } - - /** - * This class REQUIRES a custom serializer because its - * constructor parameter cannot be mapped to a property - * automatically. THIS IS DELIBERATE! - */ - class CustomData(initialValue: String) { - // DO NOT MOVE THIS PROPERTY INTO THE CONSTRUCTOR! - val value: String = initialValue - } - - object ObjectCustomSerializer : SerializationCustomSerializer { - data class Proxy(val value: String) - - override fun fromProxy(proxy: Proxy): CustomData = CustomData(proxy.value) - override fun toProxy(obj: CustomData): Proxy = Proxy(obj.value) - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeWithSerializationWhitelistTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeWithSerializationWhitelistTest.kt deleted file mode 100644 index 8b190fe1b1..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeWithSerializationWhitelistTest.kt +++ /dev/null @@ -1,81 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.SerializationWhitelist -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.RegisterExtension -import org.junit.jupiter.api.fail -import java.io.NotSerializableException -import java.util.function.Function - -class DeserializeWithSerializationWhitelistTest: TestBase(KOTLIN) { - companion object { - const val MESSAGE = "Hello Sandbox!" - - @Suppress("unused") - @BeforeAll - @JvmStatic - fun checkData() { - assertNotCordaSerializable() - } - } - - @RegisterExtension - @JvmField - val serialization = LocalSerialization(emptySet(), setOf(CustomWhitelist)) - - @Test - fun `test deserializing custom object`() { - val custom = CustomData(MESSAGE) - val data = custom.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv( - classLoader = classLoader, - customSerializerClassNames = emptySet(), - serializationWhitelistNames = setOf(CustomWhitelist::class.java.name) - )) - - val sandboxCustom = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showCustom = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowCustomData::class.java) - val result = showCustom.apply(sandboxCustom) ?: fail("Result cannot be null") - - assertEquals(custom.value, result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - @Test - fun `test deserialization needs whitelisting`() { - val custom = CustomData(MESSAGE) - val data = custom.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - val ex = assertThrows { data.deserializeFor(classLoader) } - assertThat(ex).hasMessageContaining( - "Class \"class sandbox.${CustomData::class.java.name}\" is not on the whitelist or annotated with @CordaSerializable." - ) - } - } - - class ShowCustomData : Function { - override fun apply(custom: CustomData): String { - return custom.value - } - } - - data class CustomData(val value: String) - - object CustomWhitelist : SerializationWhitelist { - override val whitelist: List> = listOf(CustomData::class.java) - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeYearMonthTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeYearMonthTest.kt deleted file mode 100644 index ab03c397d5..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeYearMonthTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.YearMonth -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeYearMonthTest : TestBase(KOTLIN) { - @Test - fun `test deserializing year-month`() { - val yearMonth = YearMonth.now() - val data = yearMonth.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxYearMonth = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showYearMonth = taskFactory.compose(classLoader.createSandboxFunction()).apply( ShowYearMonth::class.java) - val result = showYearMonth.apply(sandboxYearMonth) ?: fail("Result cannot be null") - - assertEquals(yearMonth.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowYearMonth : Function { - override fun apply(yearMonth: YearMonth): String { - return yearMonth.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeYearTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeYearTest.kt deleted file mode 100644 index 6d998ce243..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeYearTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.Year -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeYearTest : TestBase(KOTLIN) { - @Test - fun `test deserializing year`() { - val year = Year.now() - val data = year.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxYear = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showYear = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowYear::class.java) - val result = showYear.apply(sandboxYear) ?: fail("Result cannot be null") - - assertEquals(year.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowYear : Function { - override fun apply(year: Year): String { - return year.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeZoneIdTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeZoneIdTest.kt deleted file mode 100644 index fbbd937328..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeZoneIdTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.extension.ExtensionContext -import org.junit.jupiter.api.fail -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.ArgumentsProvider -import org.junit.jupiter.params.provider.ArgumentsSource -import java.time.ZoneId -import java.util.function.Function -import java.util.stream.Stream - -@ExtendWith(LocalSerialization::class) -class DeserializeZoneIdTest : TestBase(KOTLIN) { - class ZoneIdProvider : ArgumentsProvider { - override fun provideArguments(context: ExtensionContext?): Stream { - return ZoneId.getAvailableZoneIds().stream() - .sorted().limit(10).map { Arguments.of(ZoneId.of(it)) } - } - } - - @ArgumentsSource(ZoneIdProvider::class) - @ParameterizedTest(name = "{index} => {0}") - fun `test deserializing zone id`(zoneId: ZoneId) { - val data = zoneId.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxZoneId = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showZoneId = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowZoneId::class.java) - val result = showZoneId.apply(sandboxZoneId) ?: fail("Result cannot be null") - - assertEquals(zoneId.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowZoneId : Function { - override fun apply(zoneId: ZoneId): String { - return zoneId.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeZonedDateTimeTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeZonedDateTimeTest.kt deleted file mode 100644 index 4ee3ffda95..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeZonedDateTimeTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.time.ZonedDateTime -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class DeserializeZonedDateTimeTest : TestBase(KOTLIN) { - @Test - fun `test deserializing zoned date-time`() { - val dateTime = ZonedDateTime.now() - val data = dateTime.serialize() - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxDateTime = data.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showZonedDateTime = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowZonedDateTime::class.java) - val result = showZonedDateTime.apply(sandboxDateTime) ?: fail("Result cannot be null") - - assertEquals(dateTime.toString(), result.toString()) - assertEquals(SANDBOX_STRING, result::class.java.name) - } - } - - class ShowZonedDateTime : Function { - override fun apply(dateTime: ZonedDateTime): String { - return dateTime.toString() - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/LocalSerialization.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/LocalSerialization.kt deleted file mode 100644 index 36042d6920..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/LocalSerialization.kt +++ /dev/null @@ -1,73 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.SerializationContext.UseCase -import net.corda.core.serialization.SerializationCustomSerializer -import net.corda.core.serialization.SerializationWhitelist -import net.corda.core.serialization.internal.SerializationEnvironment -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.serialization.internal.BuiltInExceptionsWhitelist -import net.corda.serialization.internal.CordaSerializationMagic -import net.corda.serialization.internal.GlobalTransientClassWhiteList -import net.corda.serialization.internal.SerializationContextImpl -import net.corda.serialization.internal.SerializationFactoryImpl -import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme -import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap -import net.corda.serialization.internal.amqp.SerializationFactoryCacheKey -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.amqp.amqpMagic -import org.junit.jupiter.api.extension.AfterEachCallback -import org.junit.jupiter.api.extension.BeforeEachCallback -import org.junit.jupiter.api.extension.ExtensionContext - -class LocalSerialization( - private val customSerializers: Set>, - private val serializationWhitelists: Set -) : BeforeEachCallback, AfterEachCallback { - private companion object { - private val AMQP_P2P_CONTEXT = SerializationContextImpl( - amqpMagic, - LocalSerialization::class.java.classLoader, - GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), - emptyMap(), - true, - UseCase.P2P, - null - ) - } - - constructor() : this(emptySet(), emptySet()) - - override fun beforeEach(context: ExtensionContext) { - _contextSerializationEnv.set(createTestSerializationEnv()) - } - - override fun afterEach(context: ExtensionContext) { - _contextSerializationEnv.set(null) - } - - private fun createTestSerializationEnv(): SerializationEnvironment { - val factory = SerializationFactoryImpl(mutableMapOf()).apply { - registerScheme(AMQPSerializationScheme(customSerializers, serializationWhitelists, AccessOrderLinkedHashMap(128))) - } - return SerializationEnvironment.with(factory, AMQP_P2P_CONTEXT) - } - - private class AMQPSerializationScheme( - customSerializers: Set>, - serializationWhitelists: Set, - serializerFactoriesForContexts: AccessOrderLinkedHashMap - ) : AbstractAMQPSerializationScheme(customSerializers, serializationWhitelists, serializerFactoriesForContexts) { - override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory { - throw UnsupportedOperationException() - } - - override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory { - throw UnsupportedOperationException() - } - - override fun canDeserializeVersion(magic: CordaSerializationMagic, target: UseCase): Boolean { - return canDeserializeVersion(magic) && target == UseCase.P2P - } - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/LocalTypeModelTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/LocalTypeModelTest.kt deleted file mode 100644 index 90b5d0813f..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/LocalTypeModelTest.kt +++ /dev/null @@ -1,210 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.SerializationFactory -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.djvm.rewiring.SandboxClassLoader -import net.corda.serialization.djvm.SandboxType.KOTLIN -import net.corda.serialization.internal.SerializationFactoryImpl -import net.corda.serialization.internal.amqp.SerializerFactory -import net.corda.serialization.internal.model.LocalTypeInformation -import net.corda.serialization.internal.model.LocalTypeInformation.ACollection -import net.corda.serialization.internal.model.LocalTypeInformation.AnEnum -import net.corda.serialization.internal.model.LocalTypeInformation.AMap -import net.corda.serialization.internal.model.LocalTypeInformation.Abstract -import net.corda.serialization.internal.model.LocalTypeInformation.Atomic -import net.corda.serialization.internal.model.LocalTypeInformation.Opaque -import org.apache.qpid.proton.amqp.Decimal128 -import org.apache.qpid.proton.amqp.Decimal32 -import org.apache.qpid.proton.amqp.Decimal64 -import org.apache.qpid.proton.amqp.Symbol -import org.apache.qpid.proton.amqp.UnsignedByte -import org.apache.qpid.proton.amqp.UnsignedInteger -import org.apache.qpid.proton.amqp.UnsignedLong -import org.apache.qpid.proton.amqp.UnsignedShort -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import java.util.Date -import java.util.EnumSet -import java.util.UUID - -class LocalTypeModelTest : TestBase(KOTLIN) { - private val serializerFactory: SerializerFactory get() { - val factory = SerializationFactory.defaultFactory as SerializationFactoryImpl - val scheme = factory.getRegisteredSchemes().single() as AMQPSerializationScheme - return scheme.serializerFactory - } - - private inline fun sandbox(classLoader: SandboxClassLoader): Class<*> { - return classLoader.toSandboxClass(T::class.java) - } - - private inline fun assertLocalType(type: Class<*>): LOCAL { - return assertLocalType(LOCAL::class.java, type) as LOCAL - } - - private fun assertLocalType(localType: Class, type: Class<*>): LocalTypeInformation { - val typeData = serializerFactory.getTypeInformation(type) - assertThat(typeData).isInstanceOf(localType) - return typeData - } - - @Test - fun testString() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testLong() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - assertLocalType(Long::class.javaPrimitiveType!!) - } - - @Test - fun testInteger() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - assertLocalType(Int::class.javaPrimitiveType!!) - } - - @Test - fun testShort() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - assertLocalType(Short::class.javaPrimitiveType!!) - } - - @Test - fun testByte() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - assertLocalType(Byte::class.javaPrimitiveType!!) - } - - @Test - fun testDouble() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - assertLocalType(Double::class.javaPrimitiveType!!) - } - - @Test - fun testFloat() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - assertLocalType(Float::class.javaPrimitiveType!!) - } - - @Test - fun testChar() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - assertLocalType(Char::class.javaPrimitiveType!!) - } - - @Test - fun testUnsignedLong() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testUnsignedInteger() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testUnsignedShort() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testUnsignedByte() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testDecimal32() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testDecimal64() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testDecimal128() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testSymbol() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testUUID() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testDate() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testCollection() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox>(classLoader)) - } - - @Test - fun testEnum() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox(classLoader)) - } - - @Test - fun testCustomEnum() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - val anEnum = assertLocalType(sandbox(classLoader)) - assertThat(anEnum.members) - .containsExactlyElementsOf(CustomEnum::class.java.enumConstants.map(CustomEnum::name)) - } - - @Test - fun testEnumSet() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox>(classLoader)) - - val exampleEnumSet = EnumSet.noneOf(ExampleEnum::class.java) - assertLocalType(classLoader.toSandboxClass(exampleEnumSet::class.java)) - } - - @Test - fun testMap() = sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - assertLocalType(sandbox>(classLoader)) - } - - @Suppress("unused") - enum class CustomEnum { - ONE, - TWO; - - override fun toString(): String { - return "[${name.toLowerCase()}]" - } - } -} \ No newline at end of file diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/SafeDeserialisationTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/SafeDeserialisationTest.kt deleted file mode 100644 index 1552279f45..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/SafeDeserialisationTest.kt +++ /dev/null @@ -1,65 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.SerializedBytes -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.serialization.serialize -import net.corda.serialization.djvm.SandboxType.KOTLIN -import net.corda.serialization.internal.amqp.CompositeType -import net.corda.serialization.internal.amqp.DeserializationInput -import net.corda.serialization.internal.amqp.TypeNotation -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.ExtendWith -import org.junit.jupiter.api.fail -import java.util.function.Function - -@ExtendWith(LocalSerialization::class) -class SafeDeserialisationTest : TestBase(KOTLIN) { - companion object { - const val MESSAGE = "Nothing to see here..." - const val NUMBER = 123.toShort() - } - - @Test - fun `test deserialising an evil class`() { - val context = (_contextSerializationEnv.get() ?: fail("No serialization environment!")).p2pContext - - val innocent = InnocentData(MESSAGE, NUMBER) - val innocentData = innocent.serialize() - val envelope = DeserializationInput.getEnvelope(innocentData, context.encodingWhitelist).apply { - val innocentType = schema.types[0] as CompositeType - (schema.types as MutableList)[0] = innocentType.copy( - name = innocentType.name.replace("Innocent", "VeryEvil") - ) - } - val evilData = SerializedBytes(envelope.write()) - - sandbox { - _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) - - val sandboxData = evilData.deserializeFor(classLoader) - - val taskFactory = classLoader.createRawTaskFactory() - val showInnocentData = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowInnocentData::class.java) - val result = showInnocentData.apply(sandboxData) ?: fail("Result cannot be null") - - // Check that we have deserialised the data without instantiating the Evil class. - assertThat(result.toString()) - .isEqualTo("sandbox.net.corda.serialization.djvm.VeryEvilData: $MESSAGE, $NUMBER") - - // Check that instantiating the Evil class does indeed cause an error. - val ex = assertThrows{ VeryEvilData("Naughty!", 0) } - assertThat(ex.cause) - .isExactlyInstanceOf(IllegalStateException::class.java) - .hasMessageContaining("Victory is mine!") - } - } - - class ShowInnocentData : Function { - override fun apply(data: InnocentData): String { - return "${data::class.java.name}: ${data.message}, ${data.number}" - } - } -} - diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/SandboxType.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/SandboxType.kt deleted file mode 100644 index 6b2d5d827f..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/SandboxType.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.corda.serialization.djvm - -enum class SandboxType { - JAVA, - KOTLIN -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/TestBase.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/TestBase.kt deleted file mode 100644 index 75735552f0..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/TestBase.kt +++ /dev/null @@ -1,124 +0,0 @@ -package net.corda.serialization.djvm - -import net.corda.core.serialization.ConstructorForDeserialization -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.CordaSerializationTransformEnumDefault -import net.corda.core.serialization.CordaSerializationTransformEnumDefaults -import net.corda.core.serialization.CordaSerializationTransformRename -import net.corda.core.serialization.CordaSerializationTransformRenames -import net.corda.core.serialization.DeprecatedConstructorForDeserialization -import net.corda.djvm.SandboxConfiguration -import net.corda.djvm.SandboxRuntimeContext -import net.corda.djvm.analysis.AnalysisConfiguration -import net.corda.djvm.messages.Severity -import net.corda.djvm.messages.Severity.WARNING -import net.corda.djvm.source.BootstrapClassLoader -import net.corda.djvm.source.UserPathSource -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Timeout -import org.junit.jupiter.api.fail -import java.io.File -import java.nio.file.Files.exists -import java.nio.file.Files.isDirectory -import java.nio.file.Path -import java.nio.file.Paths -import java.util.concurrent.TimeUnit.MINUTES -import java.util.function.Consumer -import kotlin.concurrent.thread - -@Suppress("unused", "MemberVisibilityCanBePrivate") -@Timeout(5, unit = MINUTES) -abstract class TestBase(type: SandboxType) { - companion object { - const val SANDBOX_STRING = "sandbox.java.lang.String" - - @JvmField - val DETERMINISTIC_RT: Path = Paths.get( - System.getProperty("deterministic-rt.path") ?: fail("deterministic-rt.path property not set")) - - @JvmField - val TESTING_LIBRARIES: List = (System.getProperty("sandbox-libraries.path") - ?: fail("sandbox-libraries.path property not set")) - .split(File.pathSeparator).map { Paths.get(it) }.filter { exists(it) } - - private lateinit var bootstrapClassLoader: BootstrapClassLoader - private lateinit var parentConfiguration: SandboxConfiguration - - @BeforeAll - @JvmStatic - fun setupClassLoader() { - bootstrapClassLoader = BootstrapClassLoader(DETERMINISTIC_RT) - val rootConfiguration = AnalysisConfiguration.createRoot( - userSource = UserPathSource(emptyList()), - visibleAnnotations = setOf( - CordaSerializable::class.java, - CordaSerializationTransformEnumDefault::class.java, - CordaSerializationTransformEnumDefaults::class.java, - CordaSerializationTransformRename::class.java, - CordaSerializationTransformRenames::class.java, - ConstructorForDeserialization::class.java, - DeprecatedConstructorForDeserialization::class.java - ), - bootstrapSource = bootstrapClassLoader - ) - parentConfiguration = SandboxConfiguration.createFor( - analysisConfiguration = rootConfiguration, - profile = null - ) - } - - @AfterAll - @JvmStatic - fun destroyRootContext() { - bootstrapClassLoader.close() - } - } - - val classPaths: List = when(type) { - SandboxType.KOTLIN -> TESTING_LIBRARIES - SandboxType.JAVA -> TESTING_LIBRARIES.filter { isDirectory(it) } - } - - inline fun sandbox(crossinline action: SandboxRuntimeContext.() -> Unit) { - sandbox(Consumer { ctx -> action(ctx) }) - } - - fun sandbox(action: Consumer) { - sandbox(WARNING, emptySet(), action) - } - - inline fun sandbox(visibleAnnotations: Set>, crossinline action: SandboxRuntimeContext.() -> Unit) { - sandbox(visibleAnnotations, Consumer { ctx -> action(ctx) }) - } - - fun sandbox( - visibleAnnotations: Set>, - action: Consumer - ) { - sandbox(WARNING, visibleAnnotations, action) - } - - fun sandbox( - minimumSeverityLevel: Severity, - visibleAnnotations: Set>, - action: Consumer - ) { - var thrownException: Throwable? = null - thread(start = false) { - UserPathSource(classPaths).use { userSource -> - SandboxRuntimeContext(parentConfiguration.createChild(userSource, Consumer { - it.setMinimumSeverityLevel(minimumSeverityLevel) - it.setVisibleAnnotations(visibleAnnotations) - })).use(action) - } - }.apply { - uncaughtExceptionHandler = Thread.UncaughtExceptionHandler { _, ex -> - thrownException = ex - } - start() - join() - } - throw thrownException ?: return - } -} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/TestHelpers.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/TestHelpers.kt deleted file mode 100644 index 0d5a46d179..0000000000 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/TestHelpers.kt +++ /dev/null @@ -1,28 +0,0 @@ -@file:JvmName("TestHelpers") -package net.corda.serialization.djvm - -import net.corda.serialization.internal.SectionId -import net.corda.serialization.internal.amqp.Envelope -import net.corda.serialization.internal.amqp.alsoAsByteBuffer -import net.corda.serialization.internal.amqp.amqpMagic -import net.corda.serialization.internal.amqp.withDescribed -import net.corda.serialization.internal.amqp.withList -import org.apache.qpid.proton.codec.Data -import java.io.ByteArrayOutputStream - -fun Envelope.write(): ByteArray { - val data = Data.Factory.create() - data.withDescribed(Envelope.DESCRIPTOR_OBJECT) { - withList { - putObject(obj) - putObject(schema) - putObject(transformsSchema) - } - } - return ByteArrayOutputStream().use { - amqpMagic.writeTo(it) - SectionId.DATA_AND_STOP.writeTo(it) - it.alsoAsByteBuffer(data.encodedSize().toInt(), data::encode) - it.toByteArray() - } -} diff --git a/serialization-djvm/src/test/resources/log4j2-test.xml b/serialization-djvm/src/test/resources/log4j2-test.xml deleted file mode 100644 index cf527ff319..0000000000 --- a/serialization-djvm/src/test/resources/log4j2-test.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/serialization-djvm/src/test/resources/testing.cert b/serialization-djvm/src/test/resources/testing.cert deleted file mode 100644 index 03e1558cca..0000000000 Binary files a/serialization-djvm/src/test/resources/testing.cert and /dev/null differ diff --git a/serialization-djvm/src/test/scripts/generate-certificate.sh b/serialization-djvm/src/test/scripts/generate-certificate.sh deleted file mode 100755 index 4863542cd5..0000000000 --- a/serialization-djvm/src/test/scripts/generate-certificate.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -KEYPASS=deterministic -STOREPASS=deterministic - -rm -f keystore testing.cert - -keytool -keystore keystore -storetype pkcs12 -genkey -dname 'CN=localhost, O=R3, L=London, C=UK' -keyalg RSA -validity 3650 -keypass ${KEYPASS} -storepass ${STOREPASS} -keytool -keystore keystore -storetype pkcs12 -export -keyalg RSA -file testing.cert -keypass ${KEYPASS} -storepass ${STOREPASS} - -rm -f keystore diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt index 9dd1a398e8..0022030f82 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt @@ -7,7 +7,13 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.client.rpc.RPCException import net.corda.core.CordaException import net.corda.core.CordaRuntimeException -import net.corda.core.contracts.* +import net.corda.core.contracts.Amount +import net.corda.core.contracts.Contract +import net.corda.core.contracts.ContractAttachment +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.PrivacySalt +import net.corda.core.contracts.StateRef +import net.corda.core.contracts.TransactionState import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.crypto.secureRandomBytes @@ -15,24 +21,43 @@ import net.corda.core.flows.FlowException import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.internal.AbstractAttachment -import net.corda.core.serialization.* +import net.corda.core.serialization.ConstructorForDeserialization +import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.EncodingWhitelist +import net.corda.core.serialization.MissingAttachmentsException +import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializationFactory import net.corda.core.transactions.LedgerTransaction import net.corda.core.utilities.OpaqueBytes -import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme +import net.corda.coretesting.internal.rigorousMock import net.corda.nodeapi.internal.crypto.ContentSignerBuilder +import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import net.corda.serialization.internal.* -import net.corda.serialization.internal.amqp.testutils.* +import net.corda.serialization.internal.amqp.testutils.deserialize +import net.corda.serialization.internal.amqp.testutils.serialize +import net.corda.serialization.internal.amqp.testutils.testDefaultFactory +import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution +import net.corda.serialization.internal.amqp.testutils.testSerializationContext import net.corda.serialization.internal.carpenter.ClassCarpenterImpl import net.corda.testing.contracts.DummyContract import net.corda.testing.core.BOB_NAME import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.TestIdentity -import net.corda.coretesting.internal.rigorousMock import org.apache.activemq.artemis.api.core.SimpleString -import org.apache.qpid.proton.amqp.* +import org.apache.qpid.proton.amqp.Decimal128 +import org.apache.qpid.proton.amqp.Decimal32 +import org.apache.qpid.proton.amqp.Decimal64 +import org.apache.qpid.proton.amqp.Symbol +import org.apache.qpid.proton.amqp.UnsignedByte +import org.apache.qpid.proton.amqp.UnsignedInteger +import org.apache.qpid.proton.amqp.UnsignedLong +import org.apache.qpid.proton.amqp.UnsignedShort import org.apache.qpid.proton.codec.DecoderImpl import org.apache.qpid.proton.codec.EncoderImpl -import org.assertj.core.api.Assertions.* +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.assertj.core.api.Assertions.catchThrowable import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.X509v2CRLBuilder import org.bouncycastle.cert.jcajce.JcaX509CRLConverter @@ -49,9 +74,36 @@ import java.io.NotSerializableException import java.math.BigDecimal import java.math.BigInteger import java.security.cert.X509CRL -import java.time.* +import java.time.DayOfWeek +import java.time.Duration +import java.time.Instant +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.Month +import java.time.MonthDay +import java.time.OffsetDateTime +import java.time.OffsetTime +import java.time.Period +import java.time.Year +import java.time.YearMonth +import java.time.ZonedDateTime import java.time.temporal.ChronoUnit -import java.util.* +import java.util.ArrayList +import java.util.Arrays +import java.util.BitSet +import java.util.Currency +import java.util.Date +import java.util.EnumMap +import java.util.EnumSet +import java.util.HashMap +import java.util.NavigableMap +import java.util.Objects +import java.util.Random +import java.util.SortedSet +import java.util.TreeMap +import java.util.TreeSet +import java.util.UUID import kotlin.reflect.full.superclasses import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -222,16 +274,16 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi val bytes = ser.serialize(obj, compression) val decoder = DecoderImpl().apply { - this.register(Envelope.DESCRIPTOR, Envelope) - this.register(Schema.DESCRIPTOR, Schema) - this.register(Descriptor.DESCRIPTOR, Descriptor) - this.register(Field.DESCRIPTOR, Field) - this.register(CompositeType.DESCRIPTOR, CompositeType) - this.register(Choice.DESCRIPTOR, Choice) - this.register(RestrictedType.DESCRIPTOR, RestrictedType) - this.register(ReferencedObject.DESCRIPTOR, ReferencedObject) - this.register(TransformsSchema.DESCRIPTOR, TransformsSchema) - this.register(TransformTypes.DESCRIPTOR, TransformTypes) + register(Envelope.DESCRIPTOR, Envelope.FastPathConstructor(this)) + register(Schema.DESCRIPTOR, Schema) + register(Descriptor.DESCRIPTOR, Descriptor) + register(Field.DESCRIPTOR, Field) + register(CompositeType.DESCRIPTOR, CompositeType) + register(Choice.DESCRIPTOR, Choice) + register(RestrictedType.DESCRIPTOR, RestrictedType) + register(ReferencedObject.DESCRIPTOR, ReferencedObject) + register(TransformsSchema.DESCRIPTOR, TransformsSchema) + register(TransformTypes.DESCRIPTOR, TransformTypes) } EncoderImpl(decoder) DeserializationInput.withDataBytes(bytes, encodingWhitelist) { diff --git a/serialization/build.gradle b/serialization/build.gradle index 224bd642b4..a65ff2da15 100644 --- a/serialization/build.gradle +++ b/serialization/build.gradle @@ -6,7 +6,6 @@ apply plugin: 'com.jfrog.artifactory' description 'Corda serialization' -// required by DJVM and Avian JVM (for running inside the SGX enclave) which only supports Java 8. targetCompatibility = VERSION_1_8 dependencies { diff --git a/serialization/src/main/java/net/corda/serialization/internal/amqp/custom/CacheKey.java b/serialization/src/main/java/net/corda/serialization/internal/amqp/custom/CacheKey.java index 2a341d5130..cf5e7ffa05 100644 --- a/serialization/src/main/java/net/corda/serialization/internal/amqp/custom/CacheKey.java +++ b/serialization/src/main/java/net/corda/serialization/internal/amqp/custom/CacheKey.java @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom; -import net.corda.core.KeepForDJVM; import org.jetbrains.annotations.NotNull; import java.util.Arrays; @@ -9,7 +8,6 @@ import java.util.Arrays; * This class is deliberately written in Java so * that it can be package private. */ -@KeepForDJVM final class CacheKey { private final byte[] bytes; private final int hashValue; diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/AllButBlacklisted.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/AllButBlacklisted.kt index ac13d19e3b..e0579b04ca 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/AllButBlacklisted.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/AllButBlacklisted.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal -import net.corda.core.DeleteForDJVM import net.corda.core.serialization.ClassWhitelist import java.io.* import java.lang.invoke.* @@ -33,7 +32,6 @@ import kotlin.collections.LinkedHashSet * in the blacklist - it will still be serialized as specified by custom serializer. * For more details, see [net.corda.serialization.internal.CordaClassResolver.getRegistration] */ -@DeleteForDJVM object AllButBlacklisted : ClassWhitelist { private val blacklistedClasses = hashSetOf( 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 144a0bb047..8068cdf1a3 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/ByteBufferStreams.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/ByteBufferStreams.kt @@ -1,9 +1,6 @@ @file:JvmName("ByteBufferStreams") -@file:DeleteForDJVM package net.corda.serialization.internal -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.internal.LazyPool import java.io.ByteArrayOutputStream import java.io.IOException @@ -24,7 +21,6 @@ fun byteArrayOutput(task: (ByteBufferOutputStream) -> T): ByteArray { } } -@KeepForDJVM class ByteBufferInputStream(val byteBuffer: ByteBuffer) : InputStream() { @Throws(IOException::class) override fun read(): Int { @@ -46,7 +42,6 @@ class ByteBufferInputStream(val byteBuffer: ByteBuffer) : InputStream() { } } -@KeepForDJVM class ByteBufferOutputStream(size: Int) : ByteArrayOutputStream(size) { companion object { private val ensureCapacity = ByteArrayOutputStream::class.java.getDeclaredMethod("ensureCapacity", Int::class.java).apply { diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/CheckpointSerializationScheme.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/CheckpointSerializationScheme.kt index f037e2dfbb..73890ce8d4 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/CheckpointSerializationScheme.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/CheckpointSerializationScheme.kt @@ -1,13 +1,11 @@ package net.corda.serialization.internal -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CheckpointCustomSerializer import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.EncodingWhitelist import net.corda.core.serialization.SerializationEncoding import net.corda.core.serialization.internal.CheckpointSerializationContext -@KeepForDJVM data class CheckpointSerializationContextImpl @JvmOverloads constructor( override val deserializationClassLoader: ClassLoader, override val whitelist: ClassWhitelist, 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 a99eba253e..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,20 +1,16 @@ package net.corda.serialization.internal -import net.corda.core.KeepForDJVM import net.corda.core.serialization.ClassWhitelist import java.util.* -@KeepForDJVM interface MutableClassWhitelist : ClassWhitelist { fun add(entry: Class<*>) } -@KeepForDJVM object AllWhitelist : ClassWhitelist { override fun hasListed(type: Class<*>): Boolean = true } -@KeepForDJVM class BuiltInExceptionsWhitelist : ClassWhitelist { companion object { private val packageName = "^(?:java|kotlin)(?:[.]|$)".toRegex() @@ -44,11 +40,9 @@ sealed class AbstractMutableClassWhitelist(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 8f8e9d4cb6..17e95d128f 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/ClientContexts.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/ClientContexts.kt @@ -1,8 +1,6 @@ -@file:DeleteForDJVM @file:JvmName("ClientContexts") package net.corda.serialization.internal -import net.corda.core.DeleteForDJVM import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationDefaults import net.corda.serialization.internal.amqp.amqpMagic diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/GeneratedAttachment.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/GeneratedAttachment.kt index 1c0684b49d..a9a89dc5a7 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/GeneratedAttachment.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/GeneratedAttachment.kt @@ -1,10 +1,8 @@ package net.corda.serialization.internal -import net.corda.core.KeepForDJVM import net.corda.core.crypto.sha256 import net.corda.core.internal.AbstractAttachment -@KeepForDJVM class GeneratedAttachment(val bytes: ByteArray, uploader: String?) : AbstractAttachment({ bytes }, uploader) { override val id = bytes.sha256() } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/OrdinalIO.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/OrdinalIO.kt index e094d6b98d..8b297b4f4f 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/OrdinalIO.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/OrdinalIO.kt @@ -1,14 +1,11 @@ package net.corda.serialization.internal -import net.corda.core.KeepForDJVM import java.io.EOFException import java.io.InputStream import java.io.OutputStream import java.nio.ByteBuffer -@KeepForDJVM class OrdinalBits(private val ordinal: Int) { - @KeepForDJVM interface OrdinalWriter { val bits: OrdinalBits @JvmDefault val encodedSize: Int get() = 1 @@ -22,7 +19,6 @@ class OrdinalBits(private val ordinal: Int) { } } -@KeepForDJVM class OrdinalReader(private val values: Array) { private val enumName = values[0].javaClass.simpleName private val range = 0 until values.size 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 7eb236f23d..90f60d099e 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationFormat.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationFormat.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal -import net.corda.core.KeepForDJVM import net.corda.core.serialization.SerializationEncoding import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.OpaqueBytes @@ -14,7 +13,6 @@ import java.nio.ByteBuffer import java.util.zip.DeflaterOutputStream import java.util.zip.InflaterInputStream -@KeepForDJVM class CordaSerializationMagic(bytes: ByteArray) : OpaqueBytes(bytes) { private val bufferView = slice() fun consume(data: ByteSequence): ByteBuffer? { @@ -22,7 +20,6 @@ class CordaSerializationMagic(bytes: ByteArray) : OpaqueBytes(bytes) { } } -@KeepForDJVM enum class SectionId : OrdinalWriter { /** Serialization data follows, and then discard the rest of the stream (if any) as legacy data may have trailing garbage. */ DATA_AND_STOP, @@ -38,7 +35,6 @@ enum class SectionId : OrdinalWriter { override val bits = OrdinalBits(ordinal) } -@KeepForDJVM enum class CordaSerializationEncoding : SerializationEncoding, OrdinalWriter { DEFLATE { override fun wrap(stream: OutputStream) = DeflaterOutputStream(stream) 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 6b63a46655..2ec5e8d9b8 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt @@ -1,7 +1,5 @@ package net.corda.serialization.internal -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.crypto.SecureHash import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.copyBytes @@ -24,7 +22,6 @@ internal object SnappyEncodingWhitelist: EncodingWhitelist { } } -@KeepForDJVM data class SerializationContextImpl @JvmOverloads constructor(override val preferredSerializationVersion: SerializationMagic, override val deserializationClassLoader: ClassLoader, override val whitelist: ClassWhitelist, @@ -82,12 +79,10 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe override fun withEncodingWhitelist(encodingWhitelist: EncodingWhitelist) = copy(encodingWhitelist = encodingWhitelist) } -@KeepForDJVM open class SerializationFactoryImpl( // TODO: This is read-mostly. Probably a faster implementation to be found. private val schemes: MutableMap, SerializationScheme> ) : SerializationFactory() { - @DeleteForDJVM constructor() : this(ConcurrentHashMap()) companion object { @@ -155,7 +150,6 @@ open class SerializationFactoryImpl( override fun hashCode(): Int = registeredSchemes.hashCode() } -@KeepForDJVM interface SerializationScheme { fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean @Throws(NotSerializableException::class) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializeAsTokenContextImpl.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializeAsTokenContextImpl.kt index 025e27a38a..50dc886bd3 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializeAsTokenContextImpl.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializeAsTokenContextImpl.kt @@ -1,7 +1,6 @@ -@file:DeleteForDJVM + package net.corda.serialization.internal -import net.corda.core.DeleteForDJVM import net.corda.core.node.ServiceHub import net.corda.core.serialization.* import net.corda.core.serialization.internal.CheckpointSerializationContext @@ -21,7 +20,6 @@ fun CheckpointSerializationContext.withTokenContext(serializationContext: Serial * Then it is a case of using the companion object methods on [SerializeAsTokenSerializer] to set and clear context as necessary * when serializing to enable/disable tokenization. */ -@DeleteForDJVM class SerializeAsTokenContextImpl(override val serviceHub: ServiceHub, init: SerializeAsTokenContext.() -> Unit) : SerializeAsTokenContext { constructor(toBeTokenized: Any, serializationFactory: SerializationFactory, context: SerializationContext, serviceHub: ServiceHub) : this(serviceHub, { serializationFactory.serialize(toBeTokenized, context.withTokenContext(this)) @@ -68,7 +66,6 @@ class SerializeAsTokenContextImpl(override val serviceHub: ServiceHub, init: Ser * Then it is a case of using the companion object methods on [SerializeAsTokenSerializer] to set and clear context as necessary * when serializing to enable/disable tokenization. */ -@DeleteForDJVM class CheckpointSerializeAsTokenContextImpl(override val serviceHub: ServiceHub, init: SerializeAsTokenContext.() -> Unit) : SerializeAsTokenContext { constructor(toBeTokenized: Any, serializer: CheckpointSerializer, context: CheckpointSerializationContext, serviceHub: ServiceHub) : this(serviceHub, { serializer.serialize(toBeTokenized, context.withTokenContext(this)) 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 160c12298b..619324b24e 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/ServerContexts.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/ServerContexts.kt @@ -1,8 +1,6 @@ @file:JvmName("ServerContexts") -@file:DeleteForDJVM package net.corda.serialization.internal -import net.corda.core.DeleteForDJVM import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationDefaults import net.corda.serialization.internal.amqp.amqpMagic 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 c2d12e8b55..557e10850e 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/SharedContexts.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/SharedContexts.kt @@ -1,9 +1,6 @@ @file:JvmName("SharedContexts") -@file:DeleteForDJVM package net.corda.serialization.internal -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.serialization.* import net.corda.serialization.internal.amqp.amqpMagic @@ -17,7 +14,6 @@ val AMQP_P2P_CONTEXT = SerializationContextImpl( null ) -@KeepForDJVM 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 2ce03e1e3b..c372f9d293 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/UseCaseAwareness.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/UseCaseAwareness.kt @@ -1,7 +1,5 @@ -@file:KeepForDJVM package net.corda.serialization.internal -import net.corda.core.KeepForDJVM import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationFactory import java.util.* diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPRemoteTypeModel.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPRemoteTypeModel.kt index ab4edf859f..50022575cb 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPRemoteTypeModel.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPRemoteTypeModel.kt @@ -3,6 +3,7 @@ package net.corda.serialization.internal.amqp import net.corda.serialization.internal.NotSerializableDetailedException import net.corda.serialization.internal.model.* import java.io.NotSerializableException +import java.util.concurrent.ConcurrentHashMap import kotlin.collections.LinkedHashMap /** @@ -10,7 +11,7 @@ import kotlin.collections.LinkedHashMap */ class AMQPRemoteTypeModel { - private val cache: MutableMap = DefaultCacheProvider.createCache() + private val cache: MutableMap = ConcurrentHashMap() /** * Interpret a [Schema] to obtain a [Map] of all of the [RemoteTypeInformation] contained therein, indexed by 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 1e8cb09080..4b698610c7 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 @@ -1,10 +1,6 @@ @file:JvmName("AMQPSerializationScheme") package net.corda.serialization.internal.amqp - -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM -import net.corda.core.StubOutForDJVM import net.corda.core.cordapp.Cordapp import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.toSynchronised @@ -16,6 +12,7 @@ import net.corda.serialization.internal.DefaultWhitelist import net.corda.serialization.internal.MutableClassWhitelist import net.corda.serialization.internal.SerializationScheme import java.util.* +import java.util.concurrent.ConcurrentMap val AMQP_ENABLED get() = SerializationDefaults.P2P_CONTEXT.preferredSerializationVersion == amqpMagic @@ -40,14 +37,12 @@ interface SerializerFactoryFactory { fun make(context: SerializationContext): SerializerFactory } -@KeepForDJVM abstract class AbstractAMQPSerializationScheme( private val cordappCustomSerializers: Set>, private val cordappSerializationWhitelists: Set, maybeNotConcurrentSerializerFactoriesForContexts: MutableMap, val sff: SerializerFactoryFactory = createSerializerFactoryFactory() ) : SerializationScheme { - @DeleteForDJVM constructor(cordapps: List) : this( cordapps.customSerializers, cordapps.serializationWhitelists, @@ -57,23 +52,19 @@ abstract class AbstractAMQPSerializationScheme( @VisibleForTesting fun getRegisteredCustomSerializers() = cordappCustomSerializers - // This is a bit gross but a broader check for ConcurrentMap is not allowed inside DJVM. private val serializerFactoriesForContexts: MutableMap = - if (maybeNotConcurrentSerializerFactoriesForContexts is - AccessOrderLinkedHashMap) { - Collections.synchronizedMap(maybeNotConcurrentSerializerFactoriesForContexts) - } else { + if (maybeNotConcurrentSerializerFactoriesForContexts is ConcurrentMap<*, *>) { maybeNotConcurrentSerializerFactoriesForContexts + } else { + Collections.synchronizedMap(maybeNotConcurrentSerializerFactoriesForContexts) } companion object { private val serializationWhitelists: List by lazy { listOf(DefaultWhitelist) } - @DeleteForDJVM val List.customSerializers get() = flatMapTo(LinkedHashSet(), Cordapp::serializationCustomSerializers) - @DeleteForDJVM val List.serializationWhitelists get() = flatMapTo(LinkedHashSet(), Cordapp::serializationWhitelists) } @@ -125,6 +116,7 @@ abstract class AbstractAMQPSerializationScheme( fun getSerializerFactory(context: SerializationContext): SerializerFactory { val key = SerializationFactoryCacheKey(context.whitelist, context.deserializationClassLoader, context.preventDataLoss, context.customSerializers) // ConcurrentHashMap.get() is lock free, but computeIfAbsent is not, even if the key is in the map already. + // This was fixed in Java 9, so remove the extra get() when we upgrade (https://bugs.openjdk.org/browse/JDK-8161372). return serializerFactoriesForContexts[key] ?: serializerFactoriesForContexts.computeIfAbsent(key) { when (context.useCase) { SerializationContext.UseCase.RPCClient -> @@ -194,18 +186,7 @@ fun registerCustomSerializers(factory: SerializerFactory) { register(net.corda.serialization.internal.amqp.custom.BitSetSerializer(this)) register(net.corda.serialization.internal.amqp.custom.EnumSetSerializer(this)) register(net.corda.serialization.internal.amqp.custom.ContractAttachmentSerializer(this)) - } - registerNonDeterministicSerializers(factory) -} - -/* - * Register the serializers which will be excluded from the DJVM. - */ -@StubOutForDJVM -private fun registerNonDeterministicSerializers(factory: SerializerFactory) { - with(factory) { register(net.corda.serialization.internal.amqp.custom.PrivateKeySerializer) register(net.corda.serialization.internal.amqp.custom.SimpleStringSerializer) } } - diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializer.kt index b4b0b2e58e..283b997a84 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data @@ -9,7 +8,6 @@ import java.lang.reflect.Type /** * Implemented to serialize and deserialize different types of objects to/from AMQP. */ -@KeepForDJVM interface AMQPSerializer { /** * The JVM type this can serialize and deserialize. diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPStreams.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPStreams.kt index c249a98aed..8ec8ddf598 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPStreams.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPStreams.kt @@ -1,8 +1,6 @@ @file:JvmName("AMQPStreams") -@file:DeleteForDJVM package net.corda.serialization.internal.amqp -import net.corda.core.DeleteForDJVM import net.corda.serialization.internal.ByteBufferInputStream import net.corda.serialization.internal.ByteBufferOutputStream import net.corda.serialization.internal.serializeOutputStreamPool diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AccessOrderLinkedHashMap.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AccessOrderLinkedHashMap.kt index d7e50afa4d..6ebea86dfc 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AccessOrderLinkedHashMap.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AccessOrderLinkedHashMap.kt @@ -1,7 +1,5 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM -@KeepForDJVM class AccessOrderLinkedHashMap(private val maxSize: Int) : LinkedHashMap(16, 0.75f, true) { constructor(loader: () -> Int) : this(loader.invoke()) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt index b6a1b8294e..be13d023fa 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM import net.corda.core.serialization.SerializationContext import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug @@ -14,7 +13,6 @@ import java.lang.reflect.Type /** * Serialization / deserialization of arrays. */ -@KeepForDJVM open class ArraySerializer(override val type: Type, factory: LocalSerializerFactory) : AMQPSerializer { companion object { fun make(type: Type, factory: LocalSerializerFactory) : AMQPSerializer { diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CollectionSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CollectionSerializer.kt index 29953e840a..76bad27da4 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CollectionSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CollectionSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM import net.corda.core.serialization.SerializationContext import net.corda.core.utilities.NonEmptySet import net.corda.serialization.internal.model.LocalTypeInformation @@ -16,7 +15,6 @@ import kotlin.collections.LinkedHashSet /** * Serialization / deserialization of predefined set of supported [Collection] types covering mostly [List]s and [Set]s. */ -@KeepForDJVM class CollectionSerializer(private val declaredType: ParameterizedType, factory: LocalSerializerFactory) : AMQPSerializer { override val type: Type = declaredType diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializer.kt index ee28ca00de..8563e3de56 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializer.kt @@ -1,9 +1,7 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM import net.corda.core.serialization.SerializationContext import net.corda.serialization.internal.model.FingerprintWriter -import net.corda.serialization.internal.model.TypeIdentifier import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type @@ -29,12 +27,6 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { */ open val additionalSerializers: Iterable> = emptyList() - /** - * This custom serializer is also allowed to deserialize these classes. This allows us - * to deserialize objects into completely different types, e.g. `A` -> `sandbox.A`. - */ - open val deserializationAliases: Set = emptySet() - protected abstract val descriptor: Descriptor /** * This exists purely for documentation and cross-platform purposes. It is not used by our serialization / deserialization @@ -73,7 +65,6 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { * subclass in the schema, so that we can distinguish between subclasses. */ // TODO: should this be a custom serializer at all, or should it just be a plain AMQPSerializer? - @KeepForDJVM class SubClass(private val clazz: Class<*>, private val superClassSerializer: CustomSerializer) : CustomSerializer() { // TODO: should this be empty or contain the schema of the super? override val schemaForDocumentation = Schema(emptyList()) @@ -133,13 +124,11 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { /** * Additional base features for a custom serializer for a particular class, that excludes subclasses. */ - @KeepForDJVM abstract class Is(clazz: Class) : CustomSerializerImp(clazz, false) /** * Additional base features for a custom serializer for all implementations of a particular interface or super class. */ - @KeepForDJVM abstract class Implements(clazz: Class) : CustomSerializerImp(clazz, true) /** @@ -149,7 +138,6 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { * The proxy class must use only types which are either native AMQP or other types for which there are pre-registered * custom serializers. */ - @KeepForDJVM abstract class Proxy(clazz: Class, protected val proxyClass: Class

    , protected val factory: LocalSerializerFactory, @@ -212,7 +200,6 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { * @param maker A lambda for constructing an instance, that defaults to calling a constructor that expects a string. * @param unmaker A lambda that extracts the string value for an instance, that defaults to the [toString] method. */ - @KeepForDJVM abstract class ToString(clazz: Class, withInheritance: Boolean = false, private val maker: (String) -> T = clazz.getConstructor(String::class.java).let { `constructor` -> { string -> `constructor`.newInstance(string) } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializerRegistry.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializerRegistry.kt index 76a99a2bf8..b482b6dce7 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializerRegistry.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializerRegistry.kt @@ -6,9 +6,9 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug import net.corda.core.utilities.trace -import net.corda.serialization.internal.model.DefaultCacheProvider import net.corda.serialization.internal.model.TypeIdentifier import java.lang.reflect.Type +import java.util.concurrent.ConcurrentHashMap /** * Thrown when a [CustomSerializer] offers to serialize a type for which custom serialization is not permitted, because @@ -87,7 +87,7 @@ class CachingCustomSerializerRegistry( data class CustomSerializerFound(override val serializerIfFound: AMQPSerializer) : CustomSerializerLookupResult() } - private val customSerializersCache: MutableMap = DefaultCacheProvider.createCache() + private val customSerializersCache: MutableMap = ConcurrentHashMap() private val customSerializers: MutableList = mutableListOf() /** @@ -108,16 +108,8 @@ class CachingCustomSerializerRegistry( register(additional) } - for (alias in customSerializer.deserializationAliases) { - val aliasDescriptor = typeDescriptorFor(alias) - if (aliasDescriptor != customSerializer.typeDescriptor) { - descriptorBasedSerializerRegistry[aliasDescriptor.toString()] = customSerializer - } - } - customSerializer } - } override fun registerExternal(customSerializer: CorDappCustomSerializer) { diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DescriptorBasedSerializerRegistry.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DescriptorBasedSerializerRegistry.kt index 8adc48fbed..7ff51e2f83 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DescriptorBasedSerializerRegistry.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DescriptorBasedSerializerRegistry.kt @@ -1,6 +1,6 @@ package net.corda.serialization.internal.amqp -import net.corda.serialization.internal.model.DefaultCacheProvider +import java.util.concurrent.ConcurrentHashMap /** * The quickest way to find a serializer, if one has already been generated, is to look it up by type descriptor. @@ -14,9 +14,9 @@ interface DescriptorBasedSerializerRegistry { fun getOrBuild(descriptor: String, builder: () -> AMQPSerializer): AMQPSerializer } -class DefaultDescriptorBasedSerializerRegistry: DescriptorBasedSerializerRegistry { +class DefaultDescriptorBasedSerializerRegistry : DescriptorBasedSerializerRegistry { - private val registry: MutableMap> = DefaultCacheProvider.createCache() + private val registry: MutableMap> = ConcurrentHashMap() override fun get(descriptor: String): AMQPSerializer? = registry[descriptor] diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt index 7be1425d32..6ee023d1a1 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt @@ -1,9 +1,9 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM +import net.corda.core.internal.LazyPool import net.corda.core.internal.VisibleForTesting -import net.corda.core.serialization.EncodingWhitelist import net.corda.core.serialization.AMQP_ENVELOPE_CACHE_PROPERTY +import net.corda.core.serialization.EncodingWhitelist import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializedBytes import net.corda.core.utilities.ByteSequence @@ -18,10 +18,10 @@ import net.corda.serialization.internal.model.TypeIdentifier import org.apache.qpid.proton.amqp.Binary import org.apache.qpid.proton.amqp.DescribedType import org.apache.qpid.proton.amqp.UnsignedInteger -import org.apache.qpid.proton.codec.Data +import org.apache.qpid.proton.codec.DecoderImpl +import org.apache.qpid.proton.codec.EncoderImpl import java.io.InputStream import java.io.NotSerializableException -import java.lang.Exception import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.lang.reflect.TypeVariable @@ -36,21 +36,21 @@ data class ObjectAndEnvelope(val obj: T, val envelope: Envelope) * @param serializerFactory This is the factory for [AMQPSerializer] instances and can be shared across multiple * instances and threads. */ -@KeepForDJVM class DeserializationInput constructor( private val serializerFactory: SerializerFactory ) { private val objectHistory: MutableList = mutableListOf() - private val logger = loggerFor() companion object { + private val logger = loggerFor() + @VisibleForTesting @Throws(AMQPNoTypeNotSerializableException::class) fun withDataBytes( byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist, task: (ByteBuffer) -> T - ) : T { + ): T { // Check that the lead bytes match expected header val amqpSequence = amqpMagic.consume(byteSequence) ?: throw AMQPNoTypeNotSerializableException("Serialization header does not match.") @@ -72,17 +72,32 @@ class DeserializationInput constructor( } } + private val decoderPool = LazyPool { + val decoder = DecoderImpl().apply { + register(Envelope.DESCRIPTOR, Envelope.FastPathConstructor(this)) + register(Schema.DESCRIPTOR, Schema) + register(Descriptor.DESCRIPTOR, Descriptor) + register(Field.DESCRIPTOR, Field) + register(CompositeType.DESCRIPTOR, CompositeType) + register(Choice.DESCRIPTOR, Choice) + register(RestrictedType.DESCRIPTOR, RestrictedType) + register(ReferencedObject.DESCRIPTOR, ReferencedObject) + register(TransformsSchema.DESCRIPTOR, TransformsSchema) + register(TransformTypes.DESCRIPTOR, TransformTypes) + } + EncoderImpl(decoder) + decoder + } + @Throws(AMQPNoTypeNotSerializableException::class) - fun getEnvelope(byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist): Envelope { + fun getEnvelope(byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist, lazy: Boolean = false): Envelope { return withDataBytes(byteSequence, encodingWhitelist) { dataBytes -> - val data = Data.Factory.create() - val expectedSize = dataBytes.remaining() - if (data.decode(dataBytes) != expectedSize.toLong()) { - throw AMQPNoTypeNotSerializableException( - "Unexpected size of data", - "Blob is corrupted!.") + decoderPool.reentrantRun { + it.byteBuffer = dataBytes + (it.readObject() as Envelope).apply { + if (!lazy) this.resolvedSchema + } } - Envelope.get(data) } } } @@ -124,22 +139,29 @@ class DeserializationInput constructor( fun deserialize(bytes: ByteSequence, clazz: Class, context: SerializationContext): T = des { /** - * The cache uses object identity rather than [ByteSequence.equals] and - * [ByteSequence.hashCode]. This is for speed: each [ByteSequence] object - * can potentially be large, and we are optimizing for the case when we - * know we will be deserializing the exact same objects multiple times. - * This also means that the cache MUST be short-lived, as otherwise it - * becomes a memory leak. + * So that the [DecoderImpl] is held whilst we get the [Envelope] and [doReadObject], + * since we are using lazy when getting the [Envelope] which means [Schema] and + * [TransformsSchema] are only parsed out of the [bytes] if demanded by [doReadObject]. */ - @Suppress("unchecked_cast") - val envelope = (context.properties[AMQP_ENVELOPE_CACHE_PROPERTY] as? MutableMap) - ?.computeIfAbsent(IdentityKey(bytes)) { key -> - getEnvelope(key.bytes, context.encodingWhitelist) - } ?: getEnvelope(bytes, context.encodingWhitelist) + decoderPool.reentrantRun { + /** + * The cache uses object identity rather than [ByteSequence.equals] and + * [ByteSequence.hashCode]. This is for speed: each [ByteSequence] object + * can potentially be large, and we are optimizing for the case when we + * know we will be deserializing the exact same objects multiple times. + * This also means that the cache MUST be short-lived, as otherwise it + * becomes a memory leak. + */ + @Suppress("unchecked_cast") + val envelope = (context.properties[AMQP_ENVELOPE_CACHE_PROPERTY] as? MutableMap) + ?.computeIfAbsent(IdentityKey(bytes)) { key -> + getEnvelope(key.bytes, context.encodingWhitelist, true) + } ?: getEnvelope(bytes, context.encodingWhitelist, true) - logger.trace { "deserialize blob scheme=\"${envelope.schema}\"" } + logger.trace { "deserialize blob scheme=\"${envelope.schema}\"" } - doReadObject(envelope, clazz, context) + doReadObject(envelope, clazz, context) + } } @Throws(NotSerializableException::class) @@ -155,10 +177,10 @@ class DeserializationInput constructor( private fun doReadObject(envelope: Envelope, clazz: Class, context: SerializationContext): T { return clazz.cast(readObjectOrNull( - obj = redescribe(envelope.obj, clazz), - schema = SerializationSchemas(envelope.schema, envelope.transformsSchema), - type = clazz, - context = context + obj = redescribe(envelope.obj, clazz), + schema = SerializationSchemas(envelope::resolvedSchema), + type = clazz, + context = context )) } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Envelope.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Envelope.kt index 70c4ac55bd..0c7afbec92 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Envelope.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Envelope.kt @@ -1,70 +1,83 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM +import org.apache.qpid.proton.ProtonException import org.apache.qpid.proton.amqp.DescribedType import org.apache.qpid.proton.codec.Data -import org.apache.qpid.proton.codec.DescribedTypeConstructor +import org.apache.qpid.proton.codec.DecoderImpl +import org.apache.qpid.proton.codec.EncodingCodes +import org.apache.qpid.proton.codec.FastPathDescribedTypeConstructor +import java.nio.Buffer +import java.nio.ByteBuffer /** * This class wraps all serialized data, so that the schema can be carried along with it. We will provide various * internal utilities to decompose and recompose with/without schema etc so that e.g. we can store objects with a * (relationally) normalised out schema to avoid excessive duplication. */ -// TODO: make the schema parsing lazy since mostly schemas will have been seen before and we only need it if we -// TODO: don't recognise a type descriptor. -@KeepForDJVM -data class Envelope(val obj: Any?, val schema: Schema, val transformsSchema: TransformsSchema) : DescribedType { - companion object : DescribedTypeConstructor { +class Envelope(val obj: Any?, resolveSchema: () -> Pair) : DescribedType { + + val resolvedSchema: Pair by lazy(resolveSchema) + + val schema: Schema get() = resolvedSchema.first + val transformsSchema: TransformsSchema get() = resolvedSchema.second + + companion object { val DESCRIPTOR = AMQPDescriptorRegistry.ENVELOPE.amqpDescriptor val DESCRIPTOR_OBJECT = Descriptor(null, DESCRIPTOR) // described list should either be two or three elements long private const val ENVELOPE_WITHOUT_TRANSFORMS = 2 private const val ENVELOPE_WITH_TRANSFORMS = 3 + } - private const val BLOB_IDX = 0 - private const val SCHEMA_IDX = 1 - private const val TRANSFORMS_SCHEMA_IDX = 2 + class FastPathConstructor(private val decoder: DecoderImpl) : FastPathDescribedTypeConstructor { - fun get(data: Data): Envelope { - val describedType = data.`object` as DescribedType - if (describedType.descriptor != DESCRIPTOR) { - throw AMQPNoTypeNotSerializableException( - "Unexpected descriptor ${describedType.descriptor}, should be $DESCRIPTOR.") + private val _buffer: ByteBuffer get() = decoder.byteBuffer + + @Suppress("ComplexMethod", "MagicNumber") + private fun readEncodingAndReturnSize(buffer: ByteBuffer, inBytes: Boolean = true): Int { + val encodingCode: Byte = buffer.get() + return when (encodingCode) { + EncodingCodes.LIST8 -> { + (buffer.get().toInt() and 0xff).let { if (inBytes) it else (buffer.get().toInt() and 0xff) } + } + EncodingCodes.LIST32 -> { + buffer.int.let { if (inBytes) it else buffer.int } + } + else -> throw ProtonException("Expected List type but found encoding: $encodingCode") } - val list = describedType.described as List<*> - - // We need to cope with objects serialised without the transforms header element in the - // envelope - val transformSchema: Any? = when (list.size) { - ENVELOPE_WITHOUT_TRANSFORMS -> null - ENVELOPE_WITH_TRANSFORMS -> list[TRANSFORMS_SCHEMA_IDX] - else -> throw AMQPNoTypeNotSerializableException( - "Malformed list, bad length of ${list.size} (should be 2 or 3)") - } - - return newInstance(listOf(list[BLOB_IDX], Schema.get(list[SCHEMA_IDX]!!), - TransformsSchema.newInstance(transformSchema))) } - // This separation of functions is needed as this will be the entry point for the default - // AMQP decoder if one is used (see the unit tests). - override fun newInstance(described: Any?): Envelope { - val list = described as? List<*> ?: throw IllegalStateException("Was expecting a list") - - // We need to cope with objects serialised without the transforms header element in the - // envelope - val transformSchema = when (list.size) { - ENVELOPE_WITHOUT_TRANSFORMS -> TransformsSchema.newInstance(null) - ENVELOPE_WITH_TRANSFORMS -> list[TRANSFORMS_SCHEMA_IDX] as TransformsSchema - else -> throw AMQPNoTypeNotSerializableException( - "Malformed list, bad length of ${list.size} (should be 2 or 3)") + override fun readValue(): Envelope? { + val buffer = _buffer + val size = readEncodingAndReturnSize(buffer, false) + if (size != ENVELOPE_WITHOUT_TRANSFORMS && size != ENVELOPE_WITH_TRANSFORMS) { + throw AMQPNoTypeNotSerializableException("Malformed list, bad length of $size (should be 2 or 3)") } - - return Envelope(list[BLOB_IDX], list[SCHEMA_IDX] as Schema, transformSchema) + val data = Data.Factory.create() + data.decode(buffer) + val obj = data.`object` + val lambda: () -> Pair = { + data.decode(buffer) + val schema = data.`object` + val transformsSchema = if (size > 2) { + data.decode(buffer) + data.`object` + } else null + Schema.get(schema) to TransformsSchema.newInstance(transformsSchema) + } + return Envelope(obj, lambda) } - override fun getTypeClass(): Class<*> = Envelope::class.java + override fun skipValue() { + val buffer = _buffer + val size = readEncodingAndReturnSize(buffer) + (buffer as Buffer).position(buffer.position() + size) + } + + override fun encodesJavaPrimitive(): Boolean = false + + override fun getTypeClass(): Class = Envelope::class.java } override fun getDescriptor(): Any = DESCRIPTOR diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt index b16a6d2213..13c298e1ac 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt @@ -20,8 +20,6 @@ interface EvolutionSerializerFactory { /** * A mapping between Java object types and their equivalent Java primitive types. - * Predominantly for the sake of the DJVM sandbox where e.g. `char` will map to - * sandbox.java.lang.Character instead of java.lang.Character. */ val primitiveTypes: Map, Class<*>> } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/LocalSerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/LocalSerializerFactory.kt index 9b0ce7b9ae..48e82e1a2f 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/LocalSerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/LocalSerializerFactory.kt @@ -11,6 +11,7 @@ import org.apache.qpid.proton.amqp.Symbol import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.util.* +import java.util.concurrent.ConcurrentHashMap import java.util.function.Function import java.util.function.Predicate import javax.annotation.concurrent.ThreadSafe @@ -111,9 +112,9 @@ class DefaultLocalSerializerFactory( private data class ActualAndDeclaredType(val actualType: Class<*>, val declaredType: Type) - private val serializersByActualAndDeclaredType: MutableMap> = DefaultCacheProvider.createCache() - private val serializersByTypeId: MutableMap> = DefaultCacheProvider.createCache() - private val typesByName = DefaultCacheProvider.createCache>() + private val serializersByActualAndDeclaredType: MutableMap> = ConcurrentHashMap() + private val serializersByTypeId: MutableMap> = ConcurrentHashMap() + private val typesByName = ConcurrentHashMap>() override fun createDescriptor(typeInformation: LocalTypeInformation): Symbol = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerPrinter.fingerprint(typeInformation)}") 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 2e00e8d206..6b1d7e7b12 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 @@ -1,7 +1,5 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM -import net.corda.core.StubOutForDJVM import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.SerializationContext import net.corda.serialization.internal.model.LocalTypeInformation @@ -19,7 +17,6 @@ private typealias MapCreationFunction = (Map<*, *>) -> Map<*, *> /** * Serialization / deserialization of certain supported [Map] types. */ -@KeepForDJVM class MapSerializer(private val declaredType: ParameterizedType, factory: LocalSerializerFactory) : AMQPSerializer { override val type: Type = declaredType @@ -150,11 +147,6 @@ private fun Class<*>.checkHashMap() { } } -/** - * The [WeakHashMap] class does not exist within the DJVM, and so we need - * to isolate this reference. - */ -@StubOutForDJVM private fun Class<*>.checkWeakHashMap() { if (WeakHashMap::class.java.isAssignableFrom(this)) { throw IllegalArgumentException("Weak references with map types not supported. Suggested fix: " diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertyDescriptor.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertyDescriptor.kt index 94dcf72281..39c8103fb8 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertyDescriptor.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertyDescriptor.kt @@ -1,7 +1,6 @@ package net.corda.serialization.internal.amqp import com.google.common.reflect.TypeToken -import net.corda.core.KeepForDJVM import net.corda.core.internal.isPublic import net.corda.core.serialization.SerializableCalculatedProperty import net.corda.serialization.internal.amqp.MethodClassifier.* @@ -19,7 +18,6 @@ import java.util.* * @property getter the method of a class that returns a fields value. Determined by * locating a function named getXyz for the property named in field as xyz. */ -@KeepForDJVM data class PropertyDescriptor(val field: Field?, val setter: Method?, val getter: Method?) { override fun toString() = StringBuilder("").apply { appendln("Property - ${field?.name ?: "null field"}\n") diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt index 355827f1d9..3937e302e5 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt @@ -6,7 +6,6 @@ import net.corda.core.utilities.contextLogger import net.corda.core.utilities.trace import net.corda.serialization.internal.model.* import java.io.NotSerializableException -import java.util.Collections.singletonList /** * A factory that knows how to create serializers to deserialize values sent to us by remote parties. @@ -77,7 +76,7 @@ class DefaultRemoteSerializerFactory( // This will save us having to re-interpret the entire schema on re-entry when deserialising individual property values. val serializers = reflected.mapValues { (descriptor, remoteLocalPair) -> descriptorBasedSerializerRegistry.getOrBuild(descriptor) { - getUncached(remoteLocalPair.remoteTypeInformation, remoteLocalPair.localTypeInformation, context) + getUncached(remoteLocalPair.remoteTypeInformation, remoteLocalPair.localTypeInformation) } } @@ -90,8 +89,7 @@ class DefaultRemoteSerializerFactory( private fun getUncached( remoteTypeInformation: RemoteTypeInformation, - localTypeInformation: LocalTypeInformation, - context: SerializationContext + localTypeInformation: LocalTypeInformation ): AMQPSerializer { val remoteDescriptor = remoteTypeInformation.typeDescriptor @@ -112,13 +110,6 @@ class DefaultRemoteSerializerFactory( evolutionSerializerFactory.getEvolutionSerializer(remoteTypeInformation, localTypeInformation) ?: localSerializer - // The type descriptors are never going to match when we deserialise into - // the DJVM's sandbox, but we don't want the node logs to fill up with - // Big 'n Scary warnings either. Assume that the local serializer is fine - // provided the local type is the same one we expect when loading the - // remote class. - remoteTypeInformation.isCompatibleWith(localTypeInformation, context) -> localSerializer - // Descriptors don't match, and something is probably broken, but we let the framework do what it can with the local // serialiser (BlobInspectorTest uniquely breaks if we throw an exception here, and passes if we just warn and continue). else -> { @@ -158,13 +149,4 @@ ${localTypeInformation.prettyPrint(false)} this is RemoteTypeInformation.Parameterised && (localTypeInformation is LocalTypeInformation.ACollection || localTypeInformation is LocalTypeInformation.AMap) - - private fun RemoteTypeInformation.isCompatibleWith( - localTypeInformation: LocalTypeInformation, - context: SerializationContext - ): Boolean { - val localTypes = typeLoader.load(singletonList(this), context) - return localTypes.size == 1 - && localTypeInformation.observedType == localTypes.values.first() - } -} \ No newline at end of file +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Schema.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Schema.kt index 36ac18bfe6..46d85fbd1a 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Schema.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Schema.kt @@ -1,13 +1,16 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM import net.corda.core.internal.uncheckedCast import net.corda.serialization.internal.CordaSerializationMagic import net.corda.serialization.internal.amqp.AMQPTypeIdentifiers.isPrimitive import net.corda.serialization.internal.model.TypeIdentifier -import net.corda.serialization.internal.model.TypeIdentifier.TopType import net.corda.serialization.internal.model.TypeIdentifier.Companion.forGenericType -import org.apache.qpid.proton.amqp.* +import net.corda.serialization.internal.model.TypeIdentifier.TopType +import org.apache.qpid.proton.amqp.Binary +import org.apache.qpid.proton.amqp.DescribedType +import org.apache.qpid.proton.amqp.Symbol +import org.apache.qpid.proton.amqp.UnsignedInteger +import org.apache.qpid.proton.amqp.UnsignedLong import org.apache.qpid.proton.codec.DescribedTypeConstructor import java.io.NotSerializableException import java.lang.reflect.Type @@ -47,7 +50,6 @@ private class RedescribedType( * This and the classes below are OO representations of the AMQP XML schema described in the specification. Their * [toString] representations generate the associated XML form. */ -@KeepForDJVM data class Schema(val types: List) : DescribedType { companion object : DescribedTypeConstructor { val DESCRIPTOR = AMQPDescriptorRegistry.SCHEMA.amqpDescriptor @@ -76,7 +78,6 @@ data class Schema(val types: List) : DescribedType { override fun toString(): String = types.joinToString("\n") } -@KeepForDJVM data class Descriptor(val name: Symbol?, val code: UnsignedLong? = null) : DescribedType { constructor(name: String?) : this(Symbol.valueOf(name)) @@ -117,7 +118,6 @@ data class Descriptor(val name: Symbol?, val code: UnsignedLong? = null) : Descr } } -@KeepForDJVM data class Field( val name: String, val type: String, @@ -185,7 +185,6 @@ sealed class TypeNotation : DescribedType { abstract val descriptor: Descriptor } -@KeepForDJVM data class CompositeType( override val name: String, override val label: String?, @@ -236,7 +235,6 @@ data class CompositeType( } } -@KeepForDJVM data class RestrictedType(override val name: String, override val label: String?, override val provides: List, @@ -287,7 +285,6 @@ data class RestrictedType(override val name: String, } } -@KeepForDJVM data class Choice(val name: String, val value: String) : DescribedType { companion object : DescribedTypeConstructor { val DESCRIPTOR = AMQPDescriptorRegistry.CHOICE.amqpDescriptor @@ -317,7 +314,6 @@ data class Choice(val name: String, val value: String) : DescribedType { } } -@KeepForDJVM data class ReferencedObject(private val refCounter: Int) : DescribedType { companion object : DescribedTypeConstructor { val DESCRIPTOR = AMQPDescriptorRegistry.REFERENCED_OBJECT.amqpDescriptor diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationOutput.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationOutput.kt index 73b7eacae0..b0864af91f 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationOutput.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationOutput.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializedBytes import net.corda.core.utilities.contextLogger @@ -16,7 +15,6 @@ import java.lang.reflect.WildcardType import java.util.* import kotlin.collections.LinkedHashSet -@KeepForDJVM data class BytesAndSchemas( val obj: SerializedBytes, val schema: Schema, @@ -28,7 +26,6 @@ data class BytesAndSchemas( * @param serializerFactory This is the factory for [AMQPSerializer] instances and can be shared across multiple * instances and threads. */ -@KeepForDJVM open class SerializationOutput constructor( internal val serializerFactory: LocalSerializerFactory ) { diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt index 4bfab6b128..70fcbbdbd8 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt @@ -1,11 +1,19 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM import java.io.NotSerializableException import javax.annotation.concurrent.ThreadSafe -@KeepForDJVM -data class SerializationSchemas(val schema: Schema, val transforms: TransformsSchema) +class SerializationSchemas(resolveSchema: () -> Pair) { + constructor(schema: Schema, transforms: TransformsSchema) : this({ schema to transforms }) + + private val resolvedSchema: Pair by lazy(resolveSchema) + + val schema: Schema get() = resolvedSchema.first + val transforms: TransformsSchema get() = resolvedSchema.second + + operator fun component1(): Schema = schema + operator fun component2(): TransformsSchema = transforms +} /** * Factory of serializers designed to be shared across threads and invocations. @@ -16,7 +24,6 @@ data class SerializationSchemas(val schema: Schema, val transforms: TransformsSc * @property onlyCustomSerializers used for testing, when set will cause the factory to throw a * [NotSerializableException] if it cannot find a registered custom serializer for a given type */ -@KeepForDJVM @ThreadSafe interface SerializerFactory : LocalSerializerFactory, RemoteSerializerFactory, CustomSerializerRegistry diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt index e1d0aaee77..8dca0a51a8 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt @@ -1,8 +1,6 @@ package net.corda.serialization.internal.amqp import com.google.common.primitives.Primitives -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.serialization.ClassWhitelist import net.corda.serialization.internal.carpenter.ClassCarpenter import net.corda.serialization.internal.carpenter.ClassCarpenterImpl @@ -12,11 +10,9 @@ import java.util.Collections.unmodifiableMap import java.util.function.Function import java.util.function.Predicate -@KeepForDJVM object SerializerFactoryBuilder { /** * The standard mapping of Java object types to Java primitive types. - * The DJVM will need to override these, but probably not anyone else. */ @Suppress("unchecked_cast") private val javaPrimitiveTypes: Map, Class<*>> = unmodifiableMap(listOf( @@ -46,7 +42,6 @@ object SerializerFactoryBuilder { } @JvmStatic - @DeleteForDJVM fun build( whitelist: ClassWhitelist, classCarpenter: ClassCarpenter, @@ -67,7 +62,6 @@ object SerializerFactoryBuilder { } @JvmStatic - @DeleteForDJVM fun build( whitelist: ClassWhitelist, carpenterClassLoader: ClassLoader, diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SupportedTransforms.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SupportedTransforms.kt index 8c2bd34086..e93a519133 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SupportedTransforms.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SupportedTransforms.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializationTransformEnumDefault import net.corda.core.serialization.CordaSerializationTransformEnumDefaults import net.corda.core.serialization.CordaSerializationTransformRename @@ -16,7 +15,6 @@ import net.corda.core.serialization.CordaSerializationTransformRenames * that reference the transform. Notionally this allows the code that extracts transforms to work on single instances * of a transform or a meta list of them. */ -@KeepForDJVM data class SupportedTransform( val type: Class, val enum: TransformTypes, diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/TransformTypes.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/TransformTypes.kt index 6ea172604e..fc8b159865 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/TransformTypes.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/TransformTypes.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializationTransformEnumDefault import net.corda.core.serialization.CordaSerializationTransformEnumDefaults import net.corda.core.serialization.CordaSerializationTransformRename @@ -20,7 +19,6 @@ import org.apache.qpid.proton.codec.DescribedTypeConstructor */ // TODO: it would be awesome to auto build this list by scanning for transform annotations themselves // TODO: annotated with some annotation -@KeepForDJVM enum class TransformTypes(val build: (Annotation) -> Transform) : DescribedType { /** * Placeholder entry for future transforms where a node receives a transform we've subsequently diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/TransformsSchema.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/TransformsSchema.kt index 09a3a09308..3cfc0cd69f 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/TransformsSchema.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/TransformsSchema.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp -import net.corda.core.KeepForDJVM import net.corda.core.serialization.CordaSerializationTransformEnumDefault import net.corda.core.serialization.CordaSerializationTransformRename import net.corda.serialization.internal.model.LocalTypeInformation @@ -151,7 +150,6 @@ class EnumDefaultSchemaTransform(val old: String, val new: String) : Transform() * @property from the name of the property or constant prior to being changed, i.e. what it was * @property to the new name of the property or constant after the change has been made, i.e. what it is now */ -@KeepForDJVM class RenameSchemaTransform(val from: String, val to: String) : Transform() { companion object : DescribedTypeConstructor { /** @@ -196,7 +194,6 @@ object TransformsAnnotationProcessor { */ fun getTransformsSchema(type: Class<*>): TransformsMap { return when { - // This only detects Enum classes that are outside the DJVM sandbox. type.isEnum -> getEnumTransformsSchema(type) // We only have transforms for enums at present. @@ -316,7 +313,6 @@ data class TransformsSchema(val types: Map, val elements: List) } \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/InstantSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/InstantSerializer.kt index 55807fbda4..a3ff2b01b9 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/InstantSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/InstantSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.time.Instant @@ -19,6 +18,5 @@ class InstantSerializer( override fun fromProxy(proxy: InstantProxy): Instant = Instant.ofEpochSecond(proxy.epochSeconds, proxy.nanos.toLong()) - @KeepForDJVM data class InstantProxy(val epochSeconds: Long, val nanos: Int) } \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalDateSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalDateSerializer.kt index 5801f6eb99..a6e1853f51 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalDateSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalDateSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.time.LocalDate @@ -19,6 +18,5 @@ class LocalDateSerializer( override fun fromProxy(proxy: LocalDateProxy): LocalDate = LocalDate.of(proxy.year, proxy.month.toInt(), proxy.day.toInt()) - @KeepForDJVM data class LocalDateProxy(val year: Int, val month: Byte, val day: Byte) } \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalDateTimeSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalDateTimeSerializer.kt index 79d5b5a8ed..69d3a25ff9 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalDateTimeSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalDateTimeSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.time.LocalDate @@ -26,6 +25,5 @@ class LocalDateTimeSerializer( override fun fromProxy(proxy: LocalDateTimeProxy): LocalDateTime = LocalDateTime.of(proxy.date, proxy.time) - @KeepForDJVM data class LocalDateTimeProxy(val date: LocalDate, val time: LocalTime) } \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalTimeSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalTimeSerializer.kt index 8637baecb3..5e91314bc2 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalTimeSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalTimeSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.time.LocalTime @@ -29,6 +28,5 @@ class LocalTimeSerializer( proxy.nano ) - @KeepForDJVM data class LocalTimeProxy(val hour: Byte, val minute: Byte, val second: Byte, val nano: Int) -} \ No newline at end of file +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/MonthDaySerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/MonthDaySerializer.kt index 1686bb0627..027b8504f8 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/MonthDaySerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/MonthDaySerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.time.MonthDay @@ -17,6 +16,5 @@ class MonthDaySerializer( override fun fromProxy(proxy: MonthDayProxy): MonthDay = MonthDay.of(proxy.month.toInt(), proxy.day.toInt()) - @KeepForDJVM data class MonthDayProxy(val month: Byte, val day: Byte) -} \ No newline at end of file +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OffsetDateTimeSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OffsetDateTimeSerializer.kt index a8b33515a2..3bec0611b4 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OffsetDateTimeSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OffsetDateTimeSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.time.LocalDateTime @@ -26,6 +25,5 @@ class OffsetDateTimeSerializer( override fun fromProxy(proxy: OffsetDateTimeProxy): OffsetDateTime = OffsetDateTime.of(proxy.dateTime, proxy.offset) - @KeepForDJVM data class OffsetDateTimeProxy(val dateTime: LocalDateTime, val offset: ZoneOffset) -} \ No newline at end of file +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OffsetTimeSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OffsetTimeSerializer.kt index 5e2fec1b55..190c81e51e 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OffsetTimeSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OffsetTimeSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.time.LocalTime @@ -26,6 +25,5 @@ class OffsetTimeSerializer( override fun fromProxy(proxy: OffsetTimeProxy): OffsetTime = OffsetTime.of(proxy.time, proxy.offset) - @KeepForDJVM data class OffsetTimeProxy(val time: LocalTime, val offset: ZoneOffset) -} \ No newline at end of file +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OptionalSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OptionalSerializer.kt index be5020d06c..ce27854937 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OptionalSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OptionalSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.util.* @@ -24,6 +23,5 @@ class OptionalSerializer( return Optional.ofNullable(proxy.item) } - @KeepForDJVM data class OptionalProxy(val item: Any?) -} \ No newline at end of file +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PeriodSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PeriodSerializer.kt index 5b01aee63c..43584a0d54 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PeriodSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PeriodSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.time.Period @@ -19,6 +18,5 @@ class PeriodSerializer( override fun fromProxy(proxy: PeriodProxy): Period = Period.of(proxy.years, proxy.months, proxy.days) - @KeepForDJVM data class PeriodProxy(val years: Int, val months: Int, val days: Int) -} \ No newline at end of file +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PrivateKeySerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PrivateKeySerializer.kt index a7c3bf33e6..983b1953b2 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PrivateKeySerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PrivateKeySerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.DeleteForDJVM import net.corda.core.crypto.Crypto import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext.UseCase.Storage @@ -10,7 +9,6 @@ import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type import java.security.PrivateKey -@DeleteForDJVM object PrivateKeySerializer : CustomSerializer.Implements( PrivateKey::class.java diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PublicKeySerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PublicKeySerializer.kt index ee4bceb09b..44ff43ab07 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PublicKeySerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PublicKeySerializer.kt @@ -3,7 +3,13 @@ package net.corda.serialization.internal.amqp.custom import net.corda.core.crypto.Crypto import net.corda.core.serialization.DESERIALIZATION_CACHE_PROPERTY import net.corda.core.serialization.SerializationContext -import net.corda.serialization.internal.amqp.* +import net.corda.serialization.internal.amqp.AMQPTypeIdentifiers +import net.corda.serialization.internal.amqp.CustomSerializer +import net.corda.serialization.internal.amqp.DeserializationInput +import net.corda.serialization.internal.amqp.RestrictedType +import net.corda.serialization.internal.amqp.Schema +import net.corda.serialization.internal.amqp.SerializationOutput +import net.corda.serialization.internal.amqp.SerializationSchemas import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type import java.security.PublicKey @@ -28,7 +34,7 @@ object PublicKeySerializer context: SerializationContext ) { // TODO: Instead of encoding to the default X509 format, we could have a custom per key type (space-efficient) serialiser. - output.writeObject(obj.encoded, data, clazz, context) + output.writeObject(Crypto.encodePublicKey(obj), data, clazz, context) } override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/SimpleStringSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/SimpleStringSerializer.kt index 74abcf125d..083c280835 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/SimpleStringSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/SimpleStringSerializer.kt @@ -1,11 +1,9 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.DeleteForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import org.apache.activemq.artemis.api.core.SimpleString /** * A serializer for [SimpleString]. */ -@DeleteForDJVM object SimpleStringSerializer : CustomSerializer.ToString(SimpleString::class.java) \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ThrowableSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ThrowableSerializer.kt index 3f361e0b17..323f5a20a4 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ThrowableSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ThrowableSerializer.kt @@ -2,7 +2,6 @@ package net.corda.serialization.internal.amqp.custom import net.corda.core.CordaRuntimeException import net.corda.core.CordaThrowable -import net.corda.core.KeepForDJVM import net.corda.core.serialization.SerializationFactory import net.corda.core.utilities.contextLogger import net.corda.serialization.internal.amqp.* @@ -10,7 +9,6 @@ import net.corda.serialization.internal.model.LocalConstructorInformation import net.corda.serialization.internal.model.LocalTypeInformation import java.io.NotSerializableException -@KeepForDJVM class ThrowableSerializer( factory: LocalSerializerFactory ) : CustomSerializer.Proxy( @@ -111,6 +109,5 @@ class StackTraceElementSerializer(factory: LocalSerializerFactory) : CustomSeria override fun fromProxy(proxy: StackTraceElementProxy): StackTraceElement = StackTraceElement(proxy.declaringClass, proxy.methodName, proxy.fileName, proxy.lineNumber) - @KeepForDJVM data class StackTraceElementProxy(val declaringClass: String, val methodName: String, val fileName: String?, val lineNumber: Int) } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/YearMonthSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/YearMonthSerializer.kt index d7aea2eefb..3c34a73004 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/YearMonthSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/YearMonthSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.time.YearMonth @@ -19,6 +18,5 @@ class YearMonthSerializer( override fun fromProxy(proxy: YearMonthProxy): YearMonth = YearMonth.of(proxy.year, proxy.month.toInt()) - @KeepForDJVM data class YearMonthProxy(val year: Int, val month: Byte) } \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/YearSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/YearSerializer.kt index 79b6248f77..705e4a5e61 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/YearSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/YearSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.time.Year @@ -19,6 +18,5 @@ class YearSerializer( override fun fromProxy(proxy: YearProxy): Year = Year.of(proxy.year) - @KeepForDJVM data class YearProxy(val year: Int) } \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ZoneIdSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ZoneIdSerializer.kt index a0340eae57..34856cecef 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ZoneIdSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ZoneIdSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.time.ZoneId @@ -21,6 +20,5 @@ class ZoneIdSerializer( override fun fromProxy(proxy: ZoneIdProxy): ZoneId = ZoneId.of(proxy.id) - @KeepForDJVM data class ZoneIdProxy(val id: String) } \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ZonedDateTimeSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ZonedDateTimeSerializer.kt index 57b2e36bb7..04ce52a65e 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ZonedDateTimeSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ZonedDateTimeSerializer.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp.custom -import net.corda.core.KeepForDJVM import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.SerializerFactory import java.lang.reflect.Method @@ -48,6 +47,5 @@ class ZonedDateTimeSerializer( proxy.zone ) as ZonedDateTime - @KeepForDJVM data class ZonedDateTimeProxy(val dateTime: LocalDateTime, val offset: ZoneOffset, val zone: ZoneId) } \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt index 940b242064..aea420e237 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt @@ -1,9 +1,7 @@ -@file:DeleteForDJVM + package net.corda.serialization.internal.carpenter import com.google.common.base.MoreObjects -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.contextLogger @@ -26,7 +24,6 @@ interface SimpleFieldAccess { operator fun get(name: String): Any? } -@DeleteForDJVM class CarpenterClassLoader(private val parentClassLoader: ClassLoader = Thread.currentThread().contextClassLoader) : ClassLoader(parentClassLoader) { @Throws(ClassNotFoundException::class) @@ -68,7 +65,6 @@ private val moreObjects: String = Type.getInternalName(MoreObjects::class.java) private val toStringHelper: String = Type.getInternalName(MoreObjects.ToStringHelper::class.java) // Allow us to create alternative ClassCarpenters. -@KeepForDJVM interface ClassCarpenter { val whitelist: ClassWhitelist val classloader: ClassLoader @@ -119,7 +115,6 @@ interface ClassCarpenter { * * Equals/hashCode methods are not yet supported. */ -@DeleteForDJVM class ClassCarpenterImpl @JvmOverloads constructor (override val whitelist: ClassWhitelist, cl: ClassLoader = Thread.currentThread().contextClassLoader, private val lenient: Boolean = false diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/Exceptions.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/Exceptions.kt index ffd6096f04..bed6b38a56 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/Exceptions.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/Exceptions.kt @@ -1,7 +1,6 @@ package net.corda.serialization.internal.carpenter import net.corda.core.CordaRuntimeException -import net.corda.core.DeleteForDJVM import org.objectweb.asm.Type /** @@ -17,7 +16,6 @@ abstract class InterfaceMismatchException(msg: String) : ClassCarpenterException class DuplicateNameException(val name: String) : ClassCarpenterException( "An attempt was made to register two classes with the name '$name' within the same ClassCarpenter namespace.") -@DeleteForDJVM class NullablePrimitiveException(val name: String, val field: Class) : ClassCarpenterException( "Field $name is primitive type ${Type.getDescriptor(field)} and thus cannot be nullable") diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/Schema.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/Schema.kt index 90a1034f70..1278e685ce 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/Schema.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/Schema.kt @@ -1,13 +1,9 @@ -@file:DeleteForDJVM package net.corda.serialization.internal.carpenter -import net.corda.core.DeleteForDJVM -import net.corda.core.KeepForDJVM import org.objectweb.asm.ClassWriter import org.objectweb.asm.Opcodes.* import java.util.* -@KeepForDJVM enum class SchemaFlags { SimpleFieldAccess, CordaSerializable } @@ -20,7 +16,6 @@ enum class SchemaFlags { * - [InterfaceSchema] * - [EnumSchema] */ -@KeepForDJVM abstract class Schema( val name: String, var fields: Map, @@ -45,7 +40,6 @@ abstract class Schema( fun descriptorsIncludingSuperclasses(): Map = (superclass?.descriptorsIncludingSuperclasses() ?: emptyMap()) + fields.descriptors() - @DeleteForDJVM abstract fun generateFields(cw: ClassWriter) val jvmName: String @@ -70,7 +64,6 @@ fun EnumMap.simpleFieldAccess(): Boolean { /** * Represents a concrete object. */ -@DeleteForDJVM class ClassSchema( name: String, fields: Map, @@ -86,7 +79,6 @@ class ClassSchema( * Represents an interface. Carpented interfaces can be used within [ClassSchema]s * if that class should be implementing that interface. */ -@DeleteForDJVM class InterfaceSchema( name: String, fields: Map, @@ -101,7 +93,6 @@ class InterfaceSchema( /** * Represents an enumerated type. */ -@DeleteForDJVM class EnumSchema( name: String, fields: Map @@ -123,7 +114,6 @@ class EnumSchema( * Factory object used by the serializer when building [Schema]s based * on an AMQP schema. */ -@DeleteForDJVM object CarpenterSchemaFactory { fun newInstance( name: String, diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/SchemaFields.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/SchemaFields.kt index 8e639ce810..03cc142a65 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/SchemaFields.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/SchemaFields.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.carpenter -import net.corda.core.DeleteForDJVM import org.objectweb.asm.ClassWriter import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* @@ -16,9 +15,8 @@ abstract class Field(val field: Class) { var name: String = unsetName abstract val type: String - @DeleteForDJVM abstract fun generateField(cw: ClassWriter) - @DeleteForDJVM + abstract fun visitParameter(mv: MethodVisitor, idx: Int) } @@ -29,7 +27,6 @@ abstract class Field(val field: Class) { * - [NullableField] * - [NonNullableField] */ -@DeleteForDJVM abstract class ClassField(field: Class) : Field(field) { abstract val nullabilityAnnotation: String abstract fun nullTest(mv: MethodVisitor, slot: Int) @@ -63,7 +60,6 @@ abstract class ClassField(field: Class) : Field(field) { * * maps to AMQP mandatory = true fields */ -@DeleteForDJVM open class NonNullableField(field: Class) : ClassField(field) { override val nullabilityAnnotation = "Ljavax/annotation/Nonnull;" @@ -93,7 +89,6 @@ open class NonNullableField(field: Class) : ClassField(field) { * * maps to AMQP mandatory = false fields */ -@DeleteForDJVM class NullableField(field: Class) : ClassField(field) { override val nullabilityAnnotation = "Ljavax/annotation/Nullable;" @@ -115,7 +110,6 @@ class NullableField(field: Class) : ClassField(field) { /** * Represents enum constants within an enum */ -@DeleteForDJVM class EnumField : Field(Enum::class.java) { override var descriptor: String? = null @@ -136,7 +130,6 @@ class EnumField : Field(Enum::class.java) { * Constructs a Field Schema object of the correct type depending weather * the AMQP schema indicates it's mandatory (non nullable) or not (nullable) */ -@DeleteForDJVM object FieldFactory { fun newInstance(mandatory: Boolean, name: String, field: Class) = if (mandatory) NonNullableField(name, field) else NullableField(name, field) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/DefaultCacheProvider.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/DefaultCacheProvider.kt deleted file mode 100644 index 4995c1dbb2..0000000000 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/model/DefaultCacheProvider.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.serialization.internal.model - -import java.util.concurrent.ConcurrentHashMap - -/** - * We can't have [ConcurrentHashMap]s in the DJVM, so it must supply its own version of this object which returns - * plain old [MutableMap]s instead. - */ -object DefaultCacheProvider { - fun createCache(): MutableMap = ConcurrentHashMap() -} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeModel.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeModel.kt index 7cfdfa3cfc..97d5016289 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeModel.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeModel.kt @@ -1,6 +1,7 @@ package net.corda.serialization.internal.model import java.lang.reflect.* +import java.util.concurrent.ConcurrentHashMap import java.util.function.Function import java.util.function.Predicate @@ -55,9 +56,9 @@ interface LocalTypeModel { * * @param typeModelConfiguration Configuration controlling the behaviour of the [LocalTypeModel]'s type inspection. */ -class ConfigurableLocalTypeModel(private val typeModelConfiguration: LocalTypeModelConfiguration): LocalTypeModel { +class ConfigurableLocalTypeModel(private val typeModelConfiguration: LocalTypeModelConfiguration) : LocalTypeModel { - private val typeInformationCache = DefaultCacheProvider.createCache() + private val typeInformationCache = ConcurrentHashMap() /** * We need to provide the [LocalTypeInformationBuilder] with a temporary local cache, so that it doesn't leak diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeIdentifier.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeIdentifier.kt index 3477c02a48..9c4787a880 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeIdentifier.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeIdentifier.kt @@ -206,11 +206,7 @@ sealed class TypeIdentifier { override fun toString() = "Parameterised(${prettyPrint()})" override fun getLocalType(classLoader: ClassLoader): Type { - // We need to invoke ClassLoader.loadClass() directly, because - // the JVM will complain if Class.forName() returns a class - // that has a name other than the requested one. This will happen - // for "transformative" class loaders, i.e. `A` -> `sandbox.A`. - val rawType = classLoader.loadClass(name) + val rawType = Class.forName(name, false, classLoader) if (rawType.typeParameters.size != parameters.size) { throw IncompatibleTypeIdentifierException( "Class $rawType expects ${rawType.typeParameters.size} type arguments, " + diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeLoader.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeLoader.kt index 046abb138d..bf604846f1 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeLoader.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeLoader.kt @@ -3,6 +3,7 @@ package net.corda.serialization.internal.model import net.corda.core.serialization.SerializationContext import net.corda.serialization.internal.carpenter.* import java.lang.reflect.Type +import java.util.concurrent.ConcurrentHashMap /** * A [TypeLoader] obtains local types whose [TypeIdentifier]s will reflect those of remote types. @@ -20,9 +21,9 @@ interface TypeLoader { * A [TypeLoader] that uses the [ClassCarpenter] to build a class matching the supplied [RemoteTypeInformation] if none * is visible from the current classloader. */ -class ClassCarpentingTypeLoader(private val carpenter: RemoteTypeCarpenter, private val classLoader: ClassLoader): TypeLoader { +class ClassCarpentingTypeLoader(private val carpenter: RemoteTypeCarpenter, private val classLoader: ClassLoader) : TypeLoader { - val cache = DefaultCacheProvider.createCache() + val cache = ConcurrentHashMap() override fun load( remoteTypeInformation: Collection, diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt index 8965a5c8e1..429dddd044 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt @@ -6,6 +6,7 @@ import net.corda.core.utilities.toBase64 import net.corda.serialization.internal.amqp.* import net.corda.serialization.internal.model.TypeIdentifier.* import java.lang.reflect.ParameterizedType +import java.util.concurrent.ConcurrentHashMap /** * A fingerprinter that fingerprints [LocalTypeInformation]. @@ -33,7 +34,7 @@ class TypeModellingFingerPrinter( private val classLoader: ClassLoader, private val debugEnabled: Boolean = false) : FingerPrinter { - private val cache: MutableMap = DefaultCacheProvider.createCache() + private val cache: MutableMap = ConcurrentHashMap() override fun fingerprint(typeInformation: LocalTypeInformation): String = /* diff --git a/serialization/src/test/README.md b/serialization/src/test/README.md index a98dff4c03..5c1c4a0c6f 100644 --- a/serialization/src/test/README.md +++ b/serialization/src/test/README.md @@ -2,10 +2,3 @@ Any tests that do not require further Corda dependencies (other than `core`) should be added to this module, anything that requires additional Corda dependencies needs to go into `serialization-tests`. - -The Corda Serialization module should be self-contained and compilable to Java 8 (for the DJVM) bytecode when using a Java 11 compiler. -Prior to this change, it was impossible to use a Java 11 compiler to compile this module to Java 8 bytecode due to its dependencies on other -modules compiled to Java 11 (`node-driver` and transitive dependencies including: `test-utils`, `node`, `test-common`, `common-logging`, `node-api`, -`client-mock`. `tools-cliutils`). -Therefore, any tests that require further Corda dependencies need to be defined in the module `serialization-tests`, which has the full set -of dependencies including `node-driver`. \ No newline at end of file diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerializationOutputTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerializationOutputTests.java index 37c3afa53f..0194361360 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerializationOutputTests.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerializationOutputTests.java @@ -183,7 +183,7 @@ public class JavaSerializationOutputTests { DecoderImpl decoder = new DecoderImpl(); - decoder.register(Envelope.Companion.getDESCRIPTOR(), Envelope.Companion); + decoder.register(Envelope.Companion.getDESCRIPTOR(), new Envelope.FastPathConstructor(decoder)); decoder.register(Schema.Companion.getDESCRIPTOR(), Schema.Companion); decoder.register(Descriptor.Companion.getDESCRIPTOR(), Descriptor.Companion); decoder.register(Field.Companion.getDESCRIPTOR(), Field.Companion); diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt index 9514a65626..0c2ecdb8c9 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt @@ -4,6 +4,7 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.crypto.SignedData import net.corda.core.crypto.sign +import net.corda.core.flows.MaybeSerializedSignedTransaction import net.corda.core.flows.NotarisationRequest import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -21,6 +22,7 @@ import net.corda.serialization.internal.amqp.testutils.serialize import net.corda.serialization.internal.amqp.testutils.serializeAndReturnSchema import net.corda.serialization.internal.amqp.testutils.testDefaultFactory import net.corda.serialization.internal.amqp.testutils.testName +import org.assertj.core.api.Assertions.assertThat import org.junit.Ignore import org.junit.Test import org.junit.jupiter.api.Assertions.assertNotSame @@ -921,4 +923,19 @@ class EvolvabilityTests { assertNotSame(deserialized2.statesToConsume[0].txhash, deserialized2.statesToConsume[1].txhash) assertNotSame(deserialized2.statesToConsume[2].txhash, deserialized2.statesToConsume[3].txhash) } + + @Test(timeout = 300_000) + fun maybeSerializedTransaction() { + val sf = testDefaultFactory() + val resource = "EvolvabilityTests.maybeSerializedTransaction" + + //val A = MaybeSerializedSignedTransaction(SecureHash.randomSHA256(), null, null) + //File(URI("$localPath/$resource")).writeBytes(SerializationOutput(sf).serialize(A).bytes) + + val url = EvolvabilityTests::class.java.getResource(resource) + val sc2 = url.readBytes() + val deserializedA = DeserializationInput(sf).deserialize(SerializedBytes(sc2)) + + assertThat(deserializedA).isInstanceOf(MaybeSerializedSignedTransaction::class.java) + } } diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/EvolvabilityTests.maybeSerializedTransaction b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EvolvabilityTests.maybeSerializedTransaction new file mode 100644 index 0000000000..8da04d34d3 Binary files /dev/null and b/serialization/src/test/resources/net/corda/serialization/internal/amqp/EvolvabilityTests.maybeSerializedTransaction differ diff --git a/settings.gradle b/settings.gradle index bd4aef6bd8..eb8a9cc0ec 100644 --- a/settings.gradle +++ b/settings.gradle @@ -41,7 +41,6 @@ include 'node-api' include 'node-api-tests' include 'node' include 'node:capsule' -include 'node:djvm' include 'client:jackson' include 'client:jfx' include 'client:mock' @@ -56,7 +55,6 @@ include 'experimental:quasar-hook' include 'experimental:corda-utils' include 'experimental:nodeinfo' include 'experimental:netparams' -include 'jdk8u-deterministic' include 'test-common' include 'test-cli' include 'test-utils' @@ -96,13 +94,12 @@ include 'samples:cordapp-configuration:workflows' include 'samples:network-verifier:contracts' include 'samples:network-verifier:workflows' include 'serialization' -include 'serialization-djvm' -include 'serialization-djvm:deserializers' include 'serialization-tests' include 'testing:cordapps:dbfailure:dbfcontracts' include 'testing:cordapps:dbfailure:dbfworkflows' include 'testing:cordapps:missingmigration' include 'testing:cordapps:sleeping' +include 'testing:cordapps:cashobservers' // Common libraries - start include 'common-validation' @@ -116,11 +113,6 @@ include 'common-logging' project(":common-logging").projectDir = new File("$settingsDir/common/logging") // Common libraries - end -include 'core-deterministic' -include 'core-deterministic:testing' -include 'core-deterministic:testing:data' -include 'core-deterministic:testing:verifier' -include 'serialization-deterministic' include 'detekt-plugins' include 'tools:error-tool' diff --git a/testing/cordapps/cashobservers/build.gradle b/testing/cordapps/cashobservers/build.gradle new file mode 100644 index 0000000000..8222caae16 --- /dev/null +++ b/testing/cordapps/cashobservers/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'kotlin' +apply plugin: 'net.corda.plugins.cordapp' +//apply plugin: 'net.corda.plugins.quasar-utils' + +dependencies { + cordaCompile project(":core") + cordapp project(':finance:workflows') +} + +jar { + baseName "testing-cashobservers-cordapp" + manifest { + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + attributes('Corda-Testing': true) + } +} + +cordapp { + targetPlatformVersion corda_platform_version.toInteger() + minimumPlatformVersion 1 + workflow { + name "Corda Cash Observers Test CorDapp" + versionId 1 + vendor "R3" + licence "Open Source (Apache 2)" + } + signing { + enabled false + } +} \ No newline at end of file diff --git a/testing/cordapps/cashobservers/src/main/kotlin/net/corda/finance/test/flows/CashIssueWithObserversFlow.kt b/testing/cordapps/cashobservers/src/main/kotlin/net/corda/finance/test/flows/CashIssueWithObserversFlow.kt new file mode 100644 index 0000000000..282b49c3cb --- /dev/null +++ b/testing/cordapps/cashobservers/src/main/kotlin/net/corda/finance/test/flows/CashIssueWithObserversFlow.kt @@ -0,0 +1,62 @@ +package net.corda.finance.test.flows + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.contracts.Amount +import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.NotaryException +import net.corda.core.flows.ReceiveFinalityFlow +import net.corda.core.flows.StartableByRPC +import net.corda.core.identity.Party +import net.corda.core.node.StatesToRecord +import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.OpaqueBytes +import net.corda.finance.contracts.asset.Cash +import net.corda.finance.flows.AbstractCashFlow +import net.corda.finance.flows.CashException +import net.corda.finance.issuedBy +import java.util.Currency + +@StartableByRPC +@InitiatingFlow +class CashIssueWithObserversFlow(private val amount: Amount, + private val issuerBankPartyRef: OpaqueBytes, + private val notary: Party, + private val observers: Set) : AbstractCashFlow(tracker()) { + @Suspendable + override fun call(): Result { + progressTracker.currentStep = Companion.GENERATING_TX + val builder = TransactionBuilder(notary) + val issuer = ourIdentity.ref(issuerBankPartyRef) + val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), ourIdentity, notary) + progressTracker.currentStep = Companion.SIGNING_TX + val tx = serviceHub.signInitialTransaction(builder, signers) + progressTracker.currentStep = Companion.FINALISING_TX + val observerSessions = observers.map { initiateFlow(it) } + val notarised = finalise(tx, observerSessions, "Unable to notarise issue") + return Result(notarised, ourIdentity) + } + + @Suspendable + private fun finalise(tx: SignedTransaction, observerSessions: Collection, message: String): SignedTransaction { + try { + return subFlow(FinalityFlow(tx, sessions = emptySet(), observerSessions = observerSessions)) + } catch (e: NotaryException) { + throw CashException(message, e) + } + } +} + +@InitiatedBy(CashIssueWithObserversFlow::class) +class CashIssueReceiverFlowWithObservers(private val otherSide: FlowSession) : FlowLogic() { + @Suspendable + override fun call() { + if (!serviceHub.myInfo.isLegalIdentity(otherSide.counterparty)) { + subFlow(ReceiveFinalityFlow(otherSide, statesToRecord = StatesToRecord.ALL_VISIBLE)) + } + } +} \ No newline at end of file diff --git a/testing/cordapps/cashobservers/src/main/kotlin/net/corda/finance/test/flows/CashPaymentWithObserversFlow.kt b/testing/cordapps/cashobservers/src/main/kotlin/net/corda/finance/test/flows/CashPaymentWithObserversFlow.kt new file mode 100644 index 0000000000..5192ac3ad4 --- /dev/null +++ b/testing/cordapps/cashobservers/src/main/kotlin/net/corda/finance/test/flows/CashPaymentWithObserversFlow.kt @@ -0,0 +1,82 @@ +package net.corda.finance.test.flows + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.contracts.Amount +import net.corda.core.contracts.InsufficientBalanceException +import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.NotaryException +import net.corda.core.flows.ReceiveFinalityFlow +import net.corda.core.flows.StartableByRPC +import net.corda.core.identity.Party +import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.TransactionBuilder +import net.corda.finance.flows.AbstractCashFlow +import net.corda.finance.flows.CashException +import net.corda.finance.workflows.asset.CashUtils +import java.util.Currency + +@StartableByRPC +@InitiatingFlow +open class CashPaymentWithObserversFlow( + val amount: Amount, + val recipient: Party, + val observers: Set, + private val useObserverSessions: Boolean = false +) : AbstractCashFlow(tracker()) { + + @Suspendable + override fun call(): SignedTransaction { + val recipientSession = initiateFlow(recipient) + val observerSessions = observers.map { initiateFlow(it) } + val builder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first()) + logger.info("Generating spend for: ${builder.lockId}") + val (spendTX, keysForSigning) = try { + CashUtils.generateSpend( + serviceHub, + builder, + amount, + ourIdentityAndCert, + recipient + ) + } catch (e: InsufficientBalanceException) { + throw CashException("Insufficient cash for spend: ${e.message}", e) + } + + logger.info("Signing transaction for: ${spendTX.lockId}") + val tx = serviceHub.signInitialTransaction(spendTX, keysForSigning) + + logger.info("Finalising transaction for: ${tx.id}") + val sessionsForFinality = if (serviceHub.myInfo.isLegalIdentity(recipient)) emptyList() else listOf(recipientSession) + val notarised = finalise(tx, sessionsForFinality, observerSessions) + logger.info("Finalised transaction for: ${notarised.id}") + return notarised + } + + @Suspendable + private fun finalise(tx: SignedTransaction, + sessions: Collection, + observerSessions: Collection): SignedTransaction { + try { + return if (useObserverSessions) + subFlow(FinalityFlow(tx, sessions, observerSessions = observerSessions)) + else + subFlow(FinalityFlow(tx, sessions + observerSessions)) + } catch (e: NotaryException) { + throw CashException("Unable to notarise spend", e) + } + } +} + +@InitiatedBy(CashPaymentWithObserversFlow::class) +class CashPaymentReceiverWithObserversFlow(private val otherSide: FlowSession) : FlowLogic() { + @Suspendable + override fun call() { + if (!serviceHub.myInfo.isLegalIdentity(otherSide.counterparty)) { + subFlow(ReceiveFinalityFlow(otherSide)) + } + } +} diff --git a/testing/cordapps/dbfailure/dbfworkflows/build.gradle b/testing/cordapps/dbfailure/dbfworkflows/build.gradle index 26e0058cc5..221b063236 100644 --- a/testing/cordapps/dbfailure/dbfworkflows/build.gradle +++ b/testing/cordapps/dbfailure/dbfworkflows/build.gradle @@ -1,10 +1,10 @@ apply plugin: 'kotlin' -//apply plugin: 'net.corda.plugins.cordapp' +apply plugin: 'net.corda.plugins.cordapp' //apply plugin: 'net.corda.plugins.quasar-utils' dependencies { - compile project(":core") - compile project(":testing:cordapps:dbfailure:dbfcontracts") + cordaCompile project(":core") + cordapp project(":testing:cordapps:dbfailure:dbfcontracts") } jar { @@ -14,4 +14,18 @@ jar { // Driver will not include it as part of an out-of-process node. attributes('Corda-Testing': true) } +} + +cordapp { + targetPlatformVersion corda_platform_version.toInteger() + minimumPlatformVersion 1 + workflow { + name "Corda DB Failure Test CorDapp" + versionId 1 + vendor "R3" + licence "Open Source (Apache 2)" + } + signing { + enabled false + } } \ No newline at end of file diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index c0f0e6a120..4e5cf3893a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -213,8 +213,6 @@ fun driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr notaryCustomOverrides = defaultParameters.notaryCustomOverrides, inMemoryDB = defaultParameters.inMemoryDB, cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes), - djvmBootstrapSource = defaultParameters.djvmBootstrapSource, - djvmCordaSource = defaultParameters.djvmCordaSource, environmentVariables = defaultParameters.environmentVariables, allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema, premigrateH2Database = defaultParameters.premigrateH2Database, @@ -255,8 +253,6 @@ fun driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr * the data is not persisted between node restarts). Has no effect if node is configured * in any way to use database other than H2. * @property cordappsForAllNodes [TestCordapp]s that will be added to each node started by the [DriverDSL]. - * @property djvmBootstrapSource Location of a JAR containing the Java APIs for the DJVM to use. - * @property djvmCordaSource Locations of JARs of user-supplied classes to execute within the DJVM sandbox. * @property premigrateH2Database Whether to use a prebuilt H2 database schema or start from an empty schema. * @property notaryHandleTimeout Specifies how long to wait to receive a notary handle. This waiting includes waiting for * the notary to start. @@ -281,8 +277,6 @@ data class DriverParameters( val notaryCustomOverrides: Map = emptyMap(), val inMemoryDB: Boolean = false, val cordappsForAllNodes: Collection? = null, - val djvmBootstrapSource: Path? = null, - val djvmCordaSource: List = emptyList(), val environmentVariables: Map = emptyMap(), val allowHibernateToManageAppSchema: Boolean = true, val premigrateH2Database: Boolean = true, @@ -323,10 +317,6 @@ data class DriverParameters( notaryCustomOverrides, inMemoryDB, cordappsForAllNodes, - - // These fields have been added in v4.4 - djvmBootstrapSource = null, - djvmCordaSource = emptyList(), environmentVariables = emptyMap() ) @@ -410,8 +400,6 @@ data class DriverParameters( notaryCustomOverrides: Map = emptyMap(), inMemoryDB: Boolean = false, cordappsForAllNodes: Collection? = null, - djvmBootstrapSource: Path? = null, - djvmCordaSource: List = emptyList(), environmentVariables: Map = emptyMap(), allowHibernateToManageAppSchema: Boolean = true ) : this( @@ -430,8 +418,6 @@ data class DriverParameters( notaryCustomOverrides, inMemoryDB, cordappsForAllNodes, - djvmBootstrapSource, - djvmCordaSource, environmentVariables, allowHibernateToManageAppSchema, premigrateH2Database = true @@ -485,8 +471,6 @@ data class DriverParameters( notaryCustomOverrides: Map, inMemoryDB: Boolean, cordappsForAllNodes: Collection?, - djvmBootstrapSource: Path?, - djvmCordaSource: List, environmentVariables: Map, allowHibernateToManageAppSchema: Boolean, premigrateH2Database: Boolean = true @@ -506,8 +490,6 @@ data class DriverParameters( notaryCustomOverrides, inMemoryDB, cordappsForAllNodes, - djvmBootstrapSource, - djvmCordaSource, environmentVariables, allowHibernateToManageAppSchema, premigrateH2Database, @@ -533,8 +515,6 @@ data class DriverParameters( fun withNotaryCustomOverrides(notaryCustomOverrides: Map): DriverParameters = copy(notaryCustomOverrides = notaryCustomOverrides) fun withInMemoryDB(inMemoryDB: Boolean): DriverParameters = copy(inMemoryDB = inMemoryDB) fun withCordappsForAllNodes(cordappsForAllNodes: Collection?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes) - fun withDjvmBootstrapSource(djvmBootstrapSource: Path?): DriverParameters = copy(djvmBootstrapSource = djvmBootstrapSource) - fun withDjvmCordaSource(djvmCordaSource: List): DriverParameters = copy(djvmCordaSource = djvmCordaSource) fun withEnvironmentVariables(variables: Map): DriverParameters = copy(environmentVariables = variables) fun withAllowHibernateToManageAppSchema(value: Boolean): DriverParameters = copy(allowHibernateToManageAppSchema = value) fun withNotaryHandleTimeout(value: Duration): DriverParameters = copy(notaryHandleTimeout = value) @@ -633,9 +613,6 @@ data class DriverParameters( notaryCustomOverrides = notaryCustomOverrides, inMemoryDB = inMemoryDB, cordappsForAllNodes = cordappsForAllNodes, - // These fields have been added in v4.4 - djvmBootstrapSource = djvmBootstrapSource, - djvmCordaSource = djvmCordaSource, environmentVariables = environmentVariables ) @@ -656,8 +633,6 @@ data class DriverParameters( notaryCustomOverrides: Map, inMemoryDB: Boolean, cordappsForAllNodes: Collection?, - djvmBootstrapSource: Path?, - djvmCordaSource: List, environmentVariables: Map, allowHibernateToManageAppSchema: Boolean ) = this.copy( @@ -676,8 +651,6 @@ data class DriverParameters( notaryCustomOverrides = notaryCustomOverrides, inMemoryDB = inMemoryDB, cordappsForAllNodes = cordappsForAllNodes, - djvmBootstrapSource = djvmBootstrapSource, - djvmCordaSource = djvmCordaSource, environmentVariables = environmentVariables, allowHibernateToManageAppSchema = allowHibernateToManageAppSchema, premigrateH2Database = true @@ -700,8 +673,6 @@ data class DriverParameters( notaryCustomOverrides: Map, inMemoryDB: Boolean, cordappsForAllNodes: Collection?, - djvmBootstrapSource: Path?, - djvmCordaSource: List, environmentVariables: Map, allowHibernateToManageAppSchema: Boolean, premigrateH2Database: Boolean @@ -721,8 +692,6 @@ data class DriverParameters( notaryCustomOverrides = notaryCustomOverrides, inMemoryDB = inMemoryDB, cordappsForAllNodes = cordappsForAllNodes, - djvmBootstrapSource = djvmBootstrapSource, - djvmCordaSource = djvmCordaSource, environmentVariables = environmentVariables, allowHibernateToManageAppSchema = allowHibernateToManageAppSchema, premigrateH2Database = premigrateH2Database, diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/NodeParameters.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/NodeParameters.kt index b1604b94df..12b55fc146 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/NodeParameters.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/NodeParameters.kt @@ -36,7 +36,8 @@ data class NodeParameters( val additionalCordapps: Collection = emptySet(), val flowOverrides: Map>, Class>> = emptyMap(), val logLevelOverride: String? = null, - val rpcAddress: NetworkHostAndPort? = null + val rpcAddress: NetworkHostAndPort? = null, + val systemProperties: Map = emptyMap() ) { /** * Create a new node parameters object with default values. Each parameter can be specified with its wither method which returns a copy @@ -127,4 +128,97 @@ data class NodeParameters( flowOverrides = flowOverrides, logLevelOverride = logLevelOverride) + constructor( + providedName: CordaX500Name?, + rpcUsers: List, + verifierType: VerifierType, + customOverrides: Map, + startInSameProcess: Boolean?, + maximumHeapSize: String, + additionalCordapps: Collection = emptySet(), + flowOverrides: Map>, Class>>, + logLevelOverride: String? = null + ) : this( + providedName, + rpcUsers, + verifierType, + customOverrides, + startInSameProcess, + maximumHeapSize, + additionalCordapps, + flowOverrides, + logLevelOverride, + rpcAddress = null) + + @Suppress("LongParameterList") + fun copy( + providedName: CordaX500Name?, + rpcUsers: List, + verifierType: VerifierType, + customOverrides: Map, + startInSameProcess: Boolean?, + maximumHeapSize: String, + additionalCordapps: Collection = emptySet(), + flowOverrides: Map>, Class>>, + logLevelOverride: String? = null + ) = this.copy( + providedName = providedName, + rpcUsers = rpcUsers, + verifierType = verifierType, + customOverrides = customOverrides, + startInSameProcess = startInSameProcess, + maximumHeapSize = maximumHeapSize, + additionalCordapps = additionalCordapps, + flowOverrides = flowOverrides, + logLevelOverride = logLevelOverride, + rpcAddress = rpcAddress) + + constructor( + providedName: CordaX500Name?, + rpcUsers: List, + verifierType: VerifierType, + customOverrides: Map, + startInSameProcess: Boolean?, + maximumHeapSize: String, + additionalCordapps: Collection = emptySet(), + flowOverrides: Map>, Class>>, + logLevelOverride: String? = null, + rpcAddress: NetworkHostAndPort? = null + ) : this( + providedName, + rpcUsers, + verifierType, + customOverrides, + startInSameProcess, + maximumHeapSize, + additionalCordapps, + flowOverrides, + logLevelOverride, + rpcAddress, + systemProperties = emptyMap()) + + @Suppress("LongParameterList") + fun copy( + providedName: CordaX500Name?, + rpcUsers: List, + verifierType: VerifierType, + customOverrides: Map, + startInSameProcess: Boolean?, + maximumHeapSize: String, + additionalCordapps: Collection = emptySet(), + flowOverrides: Map>, Class>>, + logLevelOverride: String? = null, + rpcAddress: NetworkHostAndPort? = null + ) = this.copy( + providedName = providedName, + rpcUsers = rpcUsers, + verifierType = verifierType, + customOverrides = customOverrides, + startInSameProcess = startInSameProcess, + maximumHeapSize = maximumHeapSize, + additionalCordapps = additionalCordapps, + flowOverrides = flowOverrides, + logLevelOverride = logLevelOverride, + rpcAddress = rpcAddress, + systemProperties = systemProperties) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/flows/FlowTestsUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/flows/FlowTestsUtils.kt index 78dd3a7035..bcbfdf85bc 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/flows/FlowTestsUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/flows/FlowTestsUtils.kt @@ -1,6 +1,7 @@ package net.corda.testing.flows import co.paralleluniverse.fibers.Suspendable +import co.paralleluniverse.strands.Strand import net.corda.core.concurrent.CordaFuture import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowSession @@ -8,6 +9,7 @@ import net.corda.core.toFuture import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.unwrap import net.corda.node.internal.InitiatedFlowFactory +import net.corda.testing.driver.NodeHandle import net.corda.testing.node.internal.TestStartedNode import rx.Observable import kotlin.reflect.KClass @@ -95,4 +97,13 @@ fun > TestStartedNode.registerCoreFlowFactory(initiatingFlowCla initiatedFlowClass: Class, flowFactory: (FlowSession) -> T, track: Boolean): Observable { return this.internals.registerInitiatedFlowFactory(initiatingFlowClass, initiatedFlowClass, InitiatedFlowFactory.Core(flowFactory), track) +} + +fun waitForAllFlowsToComplete(nodeHandle: NodeHandle, maxIterations: Int = 60, iterationDelay: Long = 500) { + repeat((0..maxIterations).count()) { + if (nodeHandle.rpc.stateMachinesSnapshot().isEmpty()) { + return + } + Strand.sleep(iterationDelay) + } } \ No newline at end of file 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 a020b2a5e6..273c4bbd59 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 @@ -14,29 +14,50 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.PLATFORM_VERSION +import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.requireSupportedHashType +import net.corda.core.internal.telemetry.TelemetryComponent +import net.corda.core.internal.telemetry.TelemetryServiceImpl import net.corda.core.messaging.DataFeed import net.corda.core.messaging.FlowHandle import net.corda.core.messaging.FlowProgressHandle import net.corda.core.messaging.StateMachineTransactionMapping -import net.corda.core.node.* -import net.corda.core.node.services.* +import net.corda.core.node.AppServiceHub +import net.corda.core.node.NetworkParameters +import net.corda.core.node.NodeInfo +import net.corda.core.node.ServiceHub +import net.corda.core.node.ServicesForResolution +import net.corda.core.node.StatesToRecord +import net.corda.core.node.services.ContractUpgradeService +import net.corda.core.node.services.CordaService +import net.corda.core.node.services.IdentityService +import net.corda.core.node.services.KeyManagementService +import net.corda.core.node.services.NetworkMapCache +import net.corda.core.node.services.NetworkParametersService +import net.corda.core.node.services.ServiceLifecycleObserver +import net.corda.core.node.services.TransactionStorage +import net.corda.core.node.services.TransactionVerifierService +import net.corda.core.node.services.VaultService import net.corda.core.node.services.diagnostics.DiagnosticsService -import net.corda.core.internal.telemetry.TelemetryComponent -import net.corda.core.internal.telemetry.TelemetryServiceImpl import net.corda.core.node.services.vault.CordaTransactionSupport import net.corda.core.serialization.SerializeAsToken import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort +import net.corda.coretesting.internal.DEV_ROOT_CA import net.corda.node.VersionInfo import net.corda.node.internal.ServicesForResolutionImpl import net.corda.node.internal.NodeServicesForResolution import net.corda.node.internal.cordapp.JarScanningCordappLoader -import net.corda.node.services.api.* +import net.corda.node.services.api.SchemaService +import net.corda.node.services.api.ServiceHubInternal +import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage +import net.corda.node.services.api.VaultServiceInternal +import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.diagnostics.NodeDiagnosticsService import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.BasicHSMKeyManagementService +import net.corda.node.services.network.PersistentNetworkMapCache import net.corda.node.services.persistence.PublicKeyToOwningIdentityCacheImpl import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.transactions.InMemoryTransactionVerifierService @@ -47,12 +68,16 @@ import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.contextTransaction import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.TestIdentity -import net.corda.coretesting.internal.DEV_ROOT_CA -import net.corda.node.services.network.PersistentNetworkMapCache import net.corda.testing.internal.MockCordappProvider import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.internal.configureDatabase -import net.corda.testing.node.internal.* +import net.corda.testing.node.internal.DriverDSLImpl +import net.corda.testing.node.internal.MockCryptoService +import net.corda.testing.node.internal.MockKeyManagementService +import net.corda.testing.node.internal.MockNetworkParametersStorage +import net.corda.testing.node.internal.MockTransactionStorage +import net.corda.testing.node.internal.cordappsForPackages +import net.corda.testing.node.internal.getCallerPackage import net.corda.testing.services.MockAttachmentStorage import java.io.ByteArrayOutputStream import java.nio.file.Paths @@ -60,7 +85,7 @@ import java.security.KeyPair import java.sql.Connection import java.time.Clock import java.time.Instant -import java.util.* +import java.util.Properties import java.util.function.Consumer import java.util.jar.JarFile import java.util.zip.ZipEntry @@ -117,7 +142,7 @@ open class MockServices private constructor( } val props = Properties() props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource") - props.setProperty("dataSource.url", "jdbc:h2:file:$dbPath;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE") + props.setProperty("dataSource.url", "jdbc:h2:file:$dbPath;NON_KEYWORDS=KEY,VALUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE") props.setProperty("dataSource.user", "sa") props.setProperty("dataSource.password", "") return props @@ -430,8 +455,18 @@ open class MockServices private constructor( val cordappClassloader: ClassLoader get() = cordappLoader.appClassLoader - override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) { + override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) = + recordTransactions(txs, false) + + @VisibleForTesting + fun recordTransactions(txn: SignedTransaction, disableSignatureVerification: Boolean) = + recordTransactions(listOf(txn), disableSignatureVerification) + + @VisibleForTesting + fun recordTransactions(txs: Iterable, disableSignatureVerification: Boolean) { txs.forEach { + if (!disableSignatureVerification) + it.verifyRequiredSignatures() (validatedTransactions as WritableTransactionStorage).addTransaction(it) } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index da438db83b..7c80476025 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -150,8 +150,6 @@ class DriverDSLImpl( val notaryCustomOverrides: Map, val inMemoryDB: Boolean, val cordappsForAllNodes: Collection?, - val djvmBootstrapSource: Path?, - val djvmCordaSource: List, val environmentVariables: Map, val allowHibernateToManageAppSchema: Boolean = true, val premigrateH2Database: Boolean = true, @@ -353,7 +351,7 @@ class DriverDSLImpl( baseDirectory = baseDirectory, allowMissingConfig = true, configOverrides = if (overrides.hasPath("devMode")) overrides else overrides + mapOf("devMode" to true) - ).withDJVMConfig(djvmBootstrapSource, djvmCordaSource) + ) ).checkAndOverrideForInMemoryDB() } @@ -761,7 +759,7 @@ class DriverDSLImpl( debugPort, bytemanJarPath, bytemanPort, - systemProperties, + systemProperties + parameters.systemProperties, parameters.maximumHeapSize, parameters.logLevelOverride, identifier, @@ -896,24 +894,6 @@ class DriverDSLImpl( CORDAPP_WORKFLOW_VERSION )) - /** - * Add the DJVM's sources to the node's configuration file. - * These will all be ignored unless devMode is also true. - */ - private fun Config.withDJVMConfig(bootstrapSource: Path?, cordaSource: List): Config { - return if (hasPath("devMode")) { - if (getBoolean("devMode")) { - withOptionalValue("devModeOptions.djvm.bootstrapSource", bootstrapSource) { path -> - valueFor(path.toString()) - }.withValue("devModeOptions.djvm.cordaSource", valueFor(cordaSource.map(Path::toString))) - } else { - withoutPath("devModeOptions") - } - } else { - this - } - } - private inline fun Config.withOptionalValue(key: String, obj: T?, body: (T) -> ConfigValue): Config { return if (obj == null) { this @@ -993,13 +973,13 @@ class DriverDSLImpl( val excludePackagePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;" + "com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;" + "com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;" + - "io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.corda.djvm**;djvm.**;net.bytebuddy**;" + + "io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;" + "net.i2p**;org.apache**;" + "org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;" + "org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;" + "org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;" + "com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**;)" - val excludeClassloaderPattern = "l(net.corda.djvm.**;net.corda.core.serialization.internal.**)" + val excludeClassloaderPattern = "l(net.corda.core.serialization.internal.**)" val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } + "-javaagent:$quasarJarPath=$excludePackagePattern$excludeClassloaderPattern" @@ -1337,8 +1317,6 @@ fun genericDriver( notaryCustomOverrides = defaultParameters.notaryCustomOverrides, inMemoryDB = defaultParameters.inMemoryDB, cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes), - djvmBootstrapSource = defaultParameters.djvmBootstrapSource, - djvmCordaSource = defaultParameters.djvmCordaSource, environmentVariables = defaultParameters.environmentVariables, allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema, premigrateH2Database = defaultParameters.premigrateH2Database, @@ -1437,8 +1415,6 @@ fun internalDriver( notaryCustomOverrides: Map = DriverParameters().notaryCustomOverrides, inMemoryDB: Boolean = DriverParameters().inMemoryDB, cordappsForAllNodes: Collection? = null, - djvmBootstrapSource: Path? = null, - djvmCordaSource: List = emptyList(), environmentVariables: Map = emptyMap(), allowHibernateToManageAppSchema: Boolean = true, premigrateH2Database: Boolean = true, @@ -1463,8 +1439,6 @@ fun internalDriver( notaryCustomOverrides = notaryCustomOverrides, inMemoryDB = inMemoryDB, cordappsForAllNodes = cordappsForAllNodes, - djvmBootstrapSource = djvmBootstrapSource, - djvmCordaSource = djvmCordaSource, environmentVariables = environmentVariables, allowHibernateToManageAppSchema = allowHibernateToManageAppSchema, premigrateH2Database = premigrateH2Database, 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 ad1feadc4e..b3cfdfaf8c 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 @@ -582,12 +582,15 @@ open class InternalMockNetwork(cordappPackages: List = emptyList(), val allActiveFlows = allNodes.flatMap { it.smm.snapshot() } return allActiveFlows.any { - val flowState = it.snapshot().checkpoint.flowState - flowState is FlowState.Started && when (flowState.flowIORequest) { - is FlowIORequest.ExecuteAsyncOperation -> true - is FlowIORequest.Sleep -> true - else -> false - } + val flowSnapshot = it.snapshot() + if (!flowSnapshot.isFlowResumed && flowSnapshot.isWaitingForFuture) { + val flowState = flowSnapshot.checkpoint.flowState + flowState is FlowState.Started && when (flowState.flowIORequest) { + is FlowIORequest.ExecuteAsyncOperation -> true + is FlowIORequest.Sleep -> true + else -> false + } + } else false } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockEncryptionService.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockEncryptionService.kt new file mode 100644 index 0000000000..1c3875191c --- /dev/null +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockEncryptionService.kt @@ -0,0 +1,39 @@ +package net.corda.testing.node.internal + +import net.corda.core.internal.copyBytes +import net.corda.node.services.EncryptionService +import net.corda.nodeapi.internal.crypto.AesEncryption +import java.nio.ByteBuffer +import javax.crypto.SecretKey + +class MockEncryptionService(private val aesKey: SecretKey = AesEncryption.randomKey()) : EncryptionService { + override fun encrypt(plaintext: ByteArray, additionalData: ByteArray?): ByteArray { + val ciphertext = AesEncryption.encrypt(aesKey, plaintext, additionalData) + val buffer = ByteBuffer.allocate(Integer.BYTES + (additionalData?.size ?: 0) + ciphertext.size) + if (additionalData != null) { + buffer.putInt(additionalData.size) + buffer.put(additionalData) + } else { + buffer.putInt(0) + } + buffer.put(ciphertext) + return buffer.array() + } + + override fun decrypt(ciphertext: ByteArray): EncryptionService.PlaintextAndAAD { + val buffer = ByteBuffer.wrap(ciphertext) + val additionalData = buffer.getAdditionaData() + val plaintext = AesEncryption.decrypt(aesKey, buffer.copyBytes(), additionalData) + // Only now is the additional data authenticated + return EncryptionService.PlaintextAndAAD(plaintext, additionalData) + } + + override fun extractUnauthenticatedAdditionalData(ciphertext: ByteArray): ByteArray? { + return ByteBuffer.wrap(ciphertext).getAdditionaData() + } + + private fun ByteBuffer.getAdditionaData(): ByteArray? { + val additionalDataSize = getInt() + return if (additionalDataSize > 0) ByteArray(additionalDataSize).also { get(it) } else null + } +} diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockTransactionStorage.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockTransactionStorage.kt index c1cebf95e1..ee4a4a91f5 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockTransactionStorage.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockTransactionStorage.kt @@ -2,12 +2,17 @@ package net.corda.testing.node.internal import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.TransactionSignature import net.corda.core.internal.concurrent.doneFuture import net.corda.core.messaging.DataFeed import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.toFuture import net.corda.core.transactions.SignedTransaction import net.corda.node.services.api.WritableTransactionStorage +import net.corda.core.flows.TransactionMetadata +import net.corda.core.identity.CordaX500Name +import net.corda.core.node.services.SignedTransactionWithStatus +import net.corda.core.node.services.TransactionStatus import net.corda.testing.node.MockServices import rx.Observable import rx.subjects.PublishSubject @@ -42,11 +47,36 @@ open class MockTransactionStorage : WritableTransactionStorage, SingletonSeriali } override fun addTransaction(transaction: SignedTransaction): Boolean { - val current = txns.putIfAbsent(transaction.id, TxHolder(transaction, isVerified = true)) + val current = txns.putIfAbsent(transaction.id, TxHolder(transaction, status = TransactionStatus.VERIFIED)) return if (current == null) { notify(transaction) } else if (!current.isVerified) { - current.isVerified = true + notify(transaction) + } else { + false + } + } + + override fun addUnnotarisedTransaction(transaction: SignedTransaction): Boolean { + return txns.putIfAbsent(transaction.id, TxHolder(transaction, status = TransactionStatus.IN_FLIGHT)) == null + } + + override fun addSenderTransactionRecoveryMetadata(txId: SecureHash, metadata: TransactionMetadata): ByteArray? { return null } + + override fun addReceiverTransactionRecoveryMetadata(txId: SecureHash, + sender: CordaX500Name, + metadata: TransactionMetadata) { } + + override fun removeUnnotarisedTransaction(id: SecureHash): Boolean { + return txns.remove(id) != null + } + + override fun finalizeTransaction(transaction: SignedTransaction) = + addTransaction(transaction) + + override fun finalizeTransactionWithExtraSignatures(transaction: SignedTransaction, signatures: Collection): Boolean { + val current = txns.replace(transaction.id, TxHolder(transaction, status = TransactionStatus.VERIFIED)) + return if (current != null) { notify(transaction) } else { false @@ -54,12 +84,14 @@ open class MockTransactionStorage : WritableTransactionStorage, SingletonSeriali } override fun addUnverifiedTransaction(transaction: SignedTransaction) { - txns.putIfAbsent(transaction.id, TxHolder(transaction, isVerified = false)) + txns.putIfAbsent(transaction.id, TxHolder(transaction, status = TransactionStatus.UNVERIFIED)) } override fun getTransaction(id: SecureHash): SignedTransaction? = txns[id]?.let { if (it.isVerified) it.stx else null } - override fun getTransactionInternal(id: SecureHash): Pair? = txns[id]?.let { Pair(it.stx, it.isVerified) } + override fun getTransactionWithStatus(id: SecureHash): SignedTransactionWithStatus? = txns[id]?.let { SignedTransactionWithStatus(it.stx, it.status) } - private class TxHolder(val stx: SignedTransaction, var isVerified: Boolean) + private class TxHolder(val stx: SignedTransaction, var status: TransactionStatus) { + val isVerified = status == TransactionStatus.VERIFIED + } } \ No newline at end of file diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt index 1d8106993d..dcb12782c5 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt @@ -134,8 +134,6 @@ fun rpcDriver( notaryCustomOverrides: Map = emptyMap(), inMemoryDB: Boolean = true, cordappsForAllNodes: Collection? = null, - djvmBootstrapSource: Path? = null, - djvmCordaSource: List = emptyList(), environmentVariables: Map = emptyMap(), dsl: RPCDriverDSL.() -> A ): A { @@ -158,8 +156,6 @@ fun rpcDriver( notaryCustomOverrides = notaryCustomOverrides, inMemoryDB = inMemoryDB, cordappsForAllNodes = cordappsForAllNodes, - djvmBootstrapSource = djvmBootstrapSource, - djvmCordaSource = djvmCordaSource, environmentVariables = environmentVariables ), externalTrace ), diff --git a/testing/node-driver/src/main/resources/databasesnapshots/4.5.1/persistence.mv.db b/testing/node-driver/src/main/resources/databasesnapshots/4.5.1/persistence.mv.db index d5f6cdd09f..e69de29bb2 100644 Binary files a/testing/node-driver/src/main/resources/databasesnapshots/4.5.1/persistence.mv.db and b/testing/node-driver/src/main/resources/databasesnapshots/4.5.1/persistence.mv.db differ diff --git a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt index 27d61381f4..9b0935f471 100644 --- a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt +++ b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt @@ -10,6 +10,7 @@ import java.time.Duration import java.time.Instant @JvmOverloads +@Suppress("LongParameterList") fun testNetworkParameters( notaries: List = emptyList(), minimumPlatformVersion: Int = 1, @@ -20,7 +21,9 @@ fun testNetworkParameters( whitelistedContractImplementations: Map> = emptyMap(), epoch: Int = 1, eventHorizon: Duration = 30.days, - packageOwnership: Map = emptyMap() + packageOwnership: Map = emptyMap(), + recoveryMaximumBackupInterval: Duration = 30.days, + confidentialIdentityMinimumBackupInterval: Duration = 30.days ): NetworkParameters { return NetworkParameters( minimumPlatformVersion = minimumPlatformVersion, @@ -31,7 +34,9 @@ fun testNetworkParameters( epoch = epoch, whitelistedContractImplementations = whitelistedContractImplementations, eventHorizon = eventHorizon, - packageOwnership = packageOwnership + packageOwnership = packageOwnership, + recoveryMaximumBackupInterval = recoveryMaximumBackupInterval, + confidentialIdentityMinimumBackupInterval = confidentialIdentityMinimumBackupInterval ) } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt index 07b3ef1a3b..1f941ecf96 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt @@ -6,12 +6,16 @@ import net.corda.core.contracts.* import net.corda.core.cordapp.CordappProvider import net.corda.core.crypto.NullKeys.NULL_SIGNATURE import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.TransactionSignature import net.corda.core.flows.FlowException +import net.corda.core.flows.TransactionMetadata +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.* import net.corda.core.internal.notary.NotaryService import net.corda.core.node.ServiceHub import net.corda.core.node.ServicesForResolution +import net.corda.core.node.StatesToRecord import net.corda.core.node.services.AttachmentId import net.corda.core.contracts.RotatedKeys import net.corda.core.node.services.TransactionStorage @@ -21,6 +25,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.node.services.DbTransactionsResolver +import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.attachments.NodeAttachmentTrustCalculator import net.corda.node.services.persistence.AttachmentStorageInternal import net.corda.testing.core.dummyCommand @@ -138,6 +143,18 @@ data class TestTransactionDSLInterpreter private constructor( override val notaryService: NotaryService? = null override val attachmentsClassLoaderCache: AttachmentsClassLoaderCache = AttachmentsClassLoaderCacheImpl(TestingNamedCacheFactory()) + + override fun recordUnnotarisedTransaction(txn: SignedTransaction) {} + + override fun removeUnnotarisedTransaction(id: SecureHash) {} + + override fun finalizeTransactionWithExtraSignatures(txn: SignedTransaction, sigs: Collection, statesToRecord: StatesToRecord) {} + + override fun finalizeTransaction(txn: SignedTransaction, statesToRecord: StatesToRecord) {} + + override fun recordSenderTransactionRecoveryMetadata(txnId: SecureHash, txnMetadata: TransactionMetadata): ByteArray? { return null } + + override fun recordReceiverTransactionRecoveryMetadata(txnId: SecureHash, sender: CordaX500Name, txnMetadata: TransactionMetadata) {} } private fun copy(): TestTransactionDSLInterpreter = @@ -364,7 +381,10 @@ data class TestLedgerDSLInterpreter private constructor( override fun verifies(): EnforceVerifyOrFail { try { val usedInputs = mutableSetOf() - services.recordTransactions(transactionsUnverified.map { SignedTransaction(it, listOf(NULL_SIGNATURE)) }) + transactionsUnverified.map { + (services.validatedTransactions as WritableTransactionStorage).addTransaction(SignedTransaction(it, listOf(NULL_SIGNATURE))) + } + for ((_, value) in transactionWithLocations) { val wtx = value.transaction val ltx = wtx.toLedgerTransaction(services) @@ -377,7 +397,7 @@ data class TestLedgerDSLInterpreter private constructor( throw DoubleSpentInputs(txIds) } usedInputs.addAll(wtx.inputs) - services.recordTransactions(SignedTransaction(wtx, listOf(NULL_SIGNATURE))) + (services.validatedTransactions as WritableTransactionStorage).addTransaction(SignedTransaction(wtx, listOf(NULL_SIGNATURE))) } return EnforceVerifyOrFail.Token } catch (exception: TransactionVerificationException) { diff --git a/tools/checkpoint-agent/build.gradle b/tools/checkpoint-agent/build.gradle index 3242ab88cb..838d70c652 100644 --- a/tools/checkpoint-agent/build.gradle +++ b/tools/checkpoint-agent/build.gradle @@ -1,32 +1,3 @@ -buildscript { - // For sharing constants between builds - Properties constants = new Properties() - file("$projectDir/../../constants.properties").withInputStream { constants.load(it) } - - ext.kotlin_version = constants.getProperty("kotlinVersion") - ext.javaassist_version = "3.12.1.GA" - - repositories { - mavenLocal() - mavenCentral() - maven { - url "${publicArtifactURL}/jcenter-backup" - } - } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -repositories { - mavenLocal() - mavenCentral() - maven { - url "${publicArtifactURL}/jcenter-backup" - } -} - apply plugin: 'kotlin' apply plugin: 'idea' apply plugin: 'net.corda.plugins.publish-utils' @@ -37,7 +8,7 @@ description 'A javaagent to allow hooking into Kryo checkpoints' dependencies { compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" compileOnly "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - compileOnly "javassist:javassist:$javaassist_version" + compileOnly "org.javassist:javassist:$javaassist_version" compileOnly "com.esotericsoftware:kryo:$kryo_version" compileOnly "co.paralleluniverse:quasar-core:$quasar_version" diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt index 1d3452bca1..bfd2747f05 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt @@ -1,6 +1,5 @@ package net.corda.demobench.model -import javafx.application.Application.Parameters import javafx.application.Platform import javafx.beans.binding.IntegerExpression import javafx.beans.property.SimpleBooleanProperty @@ -31,7 +30,6 @@ import java.util.logging.Level import kotlin.math.max class NodeController( - djvmEnabled: Boolean = readDJVMEnabled(), check: atRuntime = ::checkExists ) : Controller() { companion object { @@ -42,22 +40,8 @@ class NodeController( private const val MB = 1024 * 1024 const val maxMessageSize = 10 * MB const val maxTransactionSize = 10 * MB - - private fun readDJVMEnabled(): Boolean { - return FX.application.parameters?.let(::parseDJVMEnabled) ?: false - } - - private fun parseDJVMEnabled(parameters: Parameters): Boolean { - val isEnabled = parameters.named["djvm"] - return if (isEnabled == null) { - parameters.unnamed.contains("--djvm") - } else { - java.lang.Boolean.parseBoolean(isEnabled) - } - } } - val djvmEnabled = SimpleBooleanProperty(djvmEnabled) val allowHibernateToManageAppSchema = SimpleBooleanProperty(false) private val jvm by inject() @@ -113,7 +97,6 @@ class NodeController( h2port = nodeData.h2Port.value, issuableCurrencies = nodeData.extraServices.filterIsInstance().map { it.currency.toString() }, systemProperties = mapOf( - "net.corda.djvm" to djvmEnabled.value, "co.paralleluniverse.fibers.verifyInstrumentation" to false ) ) diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt index c308d1771f..595844406f 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt @@ -152,9 +152,6 @@ class NodeTabView : Fragment() { } separator() - checkbox("Deterministic Contract Verification", nodeController.djvmEnabled).apply { - styleClass += "djvm" - } checkbox("Allow Hibernate to manage app schema", nodeController.allowHibernateToManageAppSchema).apply { styleClass += "hibernate" } diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt index d6f407cbd5..6cfc9344de 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt @@ -11,7 +11,7 @@ import kotlin.test.* class NodeControllerTest { private val baseDir: Path = Paths.get(".").toAbsolutePath() - private val controller = NodeController(false) { _, _ -> } + private val controller = NodeController { _, _ -> } private val node1Name = "Organisation 1" private val organisation2Name = "Organisation 2" @@ -70,7 +70,6 @@ class NodeControllerTest { val wrapper = controller.validate(data) ?: fail("No wrapped configuration!") val systemProperties = wrapper.nodeConfig.systemProperties - assertFalse(systemProperties["net.corda.djvm"] as Boolean) assertFalse(systemProperties["co.paralleluniverse.fibers.verifyInstrumentation"] as Boolean) }