mirror of
https://github.com/corda/corda.git
synced 2024-12-24 07:06:44 +00:00
Merge branch 'release/os/4.11' into merge-release/os/4.10-release/os/4.11-2024-10-31-394
This commit is contained in:
commit
d28ced3ba7
@ -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<net.corda.core.flows.FlowSession> 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<? extends java.security.cert.X509Certificate>, 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<kotlin.Unit>, java.time.Duration, kotlin.jvm.functions.Function0<? extends T>)
|
||||
@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 <init>(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 <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>)
|
||||
public <init>(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 <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.nio.file.Path, java.util.List<? extends java.nio.file.Path>, java.util.Map<String, String>, boolean)
|
||||
public <init>(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 <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.nio.file.Path, java.util.List<? extends java.nio.file.Path>, java.util.Map<String, String>, boolean, boolean)
|
||||
public <init>(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 <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.util.Map<String, String>, boolean)
|
||||
public <init>(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 <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.util.Map<String, String>, boolean, boolean)
|
||||
public <init>(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 <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.util.Map<String, String>, boolean, boolean, java.time.Duration)
|
||||
public <init>(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 <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, 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<net.corda.testing.node.TestCordapp> component15()
|
||||
@Nullable
|
||||
public final java.nio.file.Path component16()
|
||||
@NotNull
|
||||
public final java.util.List<java.nio.file.Path> component17()
|
||||
public final java.util.Map<String, String> component16()
|
||||
public final boolean component17()
|
||||
public final boolean component18()
|
||||
@NotNull
|
||||
public final java.util.Map<String, String> 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<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>)
|
||||
@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<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.nio.file.Path, java.util.List<? extends java.nio.file.Path>, java.util.Map<String, String>, 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<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.util.Map<String, String>, 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<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.nio.file.Path, java.util.List<? extends java.nio.file.Path>, java.util.Map<String, String>, 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<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.util.Map<String, String>, 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<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.util.Map<String, String>, 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<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Set<? extends net.corda.testing.node.TestCordapp>)
|
||||
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<net.corda.testing.node.TestCordapp> getCordappsForAllNodes()
|
||||
@NotNull
|
||||
public final net.corda.testing.driver.PortAllocation getDebugPortAllocation()
|
||||
@Nullable
|
||||
public final java.nio.file.Path getDjvmBootstrapSource()
|
||||
@NotNull
|
||||
public final java.util.List<java.nio.file.Path> 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<String, Object> getNotaryCustomOverrides()
|
||||
@NotNull
|
||||
public final java.time.Duration getNotaryHandleTimeout()
|
||||
@NotNull
|
||||
public final java.util.List<net.corda.testing.node.NotarySpec> 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<? extends java.nio.file.Path>)
|
||||
@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<String, String>)
|
||||
@ -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<String, ?>)
|
||||
@NotNull
|
||||
public final net.corda.testing.driver.DriverParameters withNotaryHandleTimeout(java.time.Duration)
|
||||
@NotNull
|
||||
public final net.corda.testing.driver.DriverParameters withNotarySpecs(java.util.List<net.corda.testing.node.NotarySpec>)
|
||||
@NotNull
|
||||
public final net.corda.testing.driver.DriverParameters withPortAllocation(net.corda.testing.driver.PortAllocation)
|
||||
|
4
.ci/dev/forward-merge/Jenkinsfile
vendored
4
.ci/dev/forward-merge/Jenkinsfile
vendored
@ -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
|
||||
|
1
.ci/dev/pr-code-checks/Jenkinsfile
vendored
1
.ci/dev/pr-code-checks/Jenkinsfile
vendored
@ -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)
|
||||
}
|
||||
}
|
||||
|
8
.github/CODEOWNERS
vendored
8
.github/CODEOWNERS
vendored
@ -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
|
||||
|
||||
|
2
.github/workflows/check-pr-title.yml
vendored
2
.github/workflows/check-pr-title.yml
vendored
@ -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 }}"
|
||||
|
8
.github/workflows/jira_assign_issue.yml
vendored
8
.github/workflows/jira_assign_issue.yml
vendored
@ -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
|
||||
|
8
.github/workflows/jira_close_issue.yml
vendored
8
.github/workflows/jira_close_issue.yml
vendored
@ -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
|
||||
|
9
.github/workflows/jira_create_issue.yml
vendored
9
.github/workflows/jira_create_issue.yml
vendored
@ -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 }}
|
||||
|
39
build.gradle
39
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'
|
||||
]
|
||||
}
|
||||
|
@ -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"
|
||||
internal const val CURRENT_MAJOR_RELEASE = "4.11-SNAPSHOT"
|
@ -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=4.0.3
|
||||
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
|
||||
|
@ -1,2 +0,0 @@
|
||||
## corda-core-deterministic.
|
||||
This artifact is a deterministic subset of the binary contents of `corda-core`.
|
@ -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 <methods>; }'
|
||||
}
|
||||
|
||||
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 <methods>; }'
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
@ -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<DigestAlgorithm> {
|
||||
override fun get(): DigestAlgorithm = DigestAlgorithmFactory.create(algorithm)
|
||||
val digestLength: Int by lazy { get().digestLength }
|
||||
}
|
@ -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<T>(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<T>(name: String, private val once: Boolean = false) : ToggleField<T>(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<T>(name: String) : ToggleField<T>(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<T>(name: String,
|
||||
private val log: Logger = staticLog,
|
||||
private val isAGlobalThreadBeingCreated: (Array<StackTraceElement>) -> Boolean) : ToggleField<T>(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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package net.corda.core.internal.utilities
|
||||
|
||||
import net.corda.core.KeepForDJVM
|
||||
|
||||
@KeepForDJVM
|
||||
class PrivateInterner<T>(val verifier: IternabilityVerifier<T> = AlwaysInternableVerifier()) {
|
||||
// DJVM implementation does not intern and does not use Guava
|
||||
fun <S : T> intern(sample: S): S = sample
|
||||
|
||||
@KeepForDJVM
|
||||
companion object {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun findFor(clazz: Class<*>?): PrivateInterner<Any>? = 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 <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, 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 <T : Any> deserializeWithCompatibleContext(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): ObjectWithCompatibleContext<T>
|
||||
|
||||
/**
|
||||
* 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 <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T>
|
||||
|
||||
/**
|
||||
* 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 <T> 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 <T> 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
|
||||
}
|
||||
}
|
@ -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<URL, Pair<URL, Attachment>>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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<TransactionVerificationRequest>()
|
||||
.toLedgerTransaction()
|
||||
.verify()
|
||||
|
||||
cl.loadResource("txverify/tx-failure.bin")
|
||||
.deserialize<TransactionVerificationRequest>()
|
||||
.toLedgerTransaction()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassLoader.loadResource(resourceName: String): ByteArray {
|
||||
return getResourceAsStream(resourceName)?.use { it.readBytes() }
|
||||
?: throw FileNotFoundException(resourceName)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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<IdentityService>().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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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); }
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package net.corda.core.internal
|
||||
|
||||
/**
|
||||
* Stubbing out non-deterministic method.
|
||||
*/
|
||||
fun <T: Any> createInstancesOfClassesImplementing(@Suppress("UNUSED_PARAMETER") classloader: ClassLoader, @Suppress("UNUSED_PARAMETER") clazz: Class<T>,
|
||||
@Suppress("UNUSED_PARAMETER") classVersionRange: IntRange? = null): Set<T> {
|
||||
return emptySet()
|
||||
}
|
@ -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 = "<none>"
|
||||
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 = "<none>"
|
||||
override fun getEncoded() = byteArrayOf()
|
||||
}
|
||||
val BOB = Party(BOB_NAME, BOB_KEY)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun testCordaException() {
|
||||
val ex = assertFailsWith<CordaException> { throw CordaException("BAD THING") }
|
||||
assertEquals("BAD THING", ex.message)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun testAttachmentResolutionException() {
|
||||
val ex = assertFailsWith<AttachmentResolutionException> { throw AttachmentResolutionException(TEST_HASH) }
|
||||
assertEquals(TEST_HASH, ex.hash)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun testTransactionResolutionException() {
|
||||
val ex = assertFailsWith<TransactionResolutionException> { throw TransactionResolutionException(TEST_HASH) }
|
||||
assertEquals(TEST_HASH, ex.hash)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun testConflictingAttachmentsRejection() {
|
||||
val ex = assertFailsWith<ConflictingAttachmentsRejection> { 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<NotaryChangeInWrongTransactionType> { throw NotaryChangeInWrongTransactionType(TX_ID, ALICE, BOB) }
|
||||
assertEquals(TX_ID, ex.txId)
|
||||
assertEquals(ALICE, ex.txNotary)
|
||||
assertEquals(BOB, ex.outputNotary)
|
||||
}
|
||||
}
|
@ -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<TrustAnchor>
|
||||
= aliases.map { alias -> TrustAnchor(keyStore.getCertificate(alias) as X509Certificate, null) }.toSet()
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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 = "<none>"
|
||||
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<PublicKey>
|
||||
get() = listOf(ALICE_KEY)
|
||||
override val id: SecureHash
|
||||
get() = SecureHash.allOnesHash
|
||||
override val signers: List<Party>
|
||||
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)
|
||||
}
|
||||
}
|
@ -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<IllegalArgumentException> { PrivacySalt(ByteArray(SALT_SIZE)) }
|
||||
assertEquals("Privacy salt should not be all zeros.", ex.message)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun testTooShortPrivacySaltForSHA256() {
|
||||
val ex = assertFailsWith<IllegalArgumentException> { PrivacySalt(ByteArray(SALT_SIZE - 1) { 0x7f }) }
|
||||
assertEquals("Privacy salt should be at least 32 bytes.", ex.message)
|
||||
}
|
||||
}
|
@ -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<IllegalArgumentException> { UniqueIdentifier::class.constructors.first().call() }
|
||||
assertThat(ex).hasMessage("Callable expects 2 arguments, but 0 were provided.")
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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<SecureHash> =
|
||||
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)
|
||||
// }
|
||||
}
|
@ -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)
|
||||
}
|
@ -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<NoSuchAlgorithmException> { SecureRandom.getInstance("CordaPRNG") }
|
||||
assertThat(error).hasMessage("CordaPRNG SecureRandom not available")
|
||||
}
|
||||
}
|
@ -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<SecureHash> = 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<SignatureException> { 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<IllegalArgumentException> { Crypto.doVerify(it, txSignatureWithTree) }
|
||||
}
|
||||
|
||||
// Test that the Merkle tree consists of hash(txId), not txId.
|
||||
assertFailsWith<MerkleTreeException> { 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<SignatureException> { Crypto.doVerify(txId.sha256(), txSignature) }
|
||||
}
|
||||
|
||||
// Returns a TransactionSignature over the Merkle root, but the partial tree is null.
|
||||
private fun signMultipleTx(txIds: List<SecureHash>, 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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<PublicKey>
|
||||
get() = emptySet()
|
||||
override val sigs: List<TransactionSignature>
|
||||
get() = emptyList()
|
||||
|
||||
override fun getKeyDescriptions(keys: Set<PublicKey>): List<String> {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
tx.verifyRequiredSignatures()
|
||||
tx.checkSignaturesAreValid()
|
||||
tx.getMissingSigners()
|
||||
tx.verifySignaturesExcept()
|
||||
tx.verifySignaturesExcept(emptySet())
|
||||
}
|
||||
}
|
@ -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<Exception> { verifyTransaction(bytesOfResource("txverify/tx-failure.bin")) }
|
||||
assertThat(e).hasMessageContaining("Required ${Move::class.java.canonicalName} command")
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="info">
|
||||
<Appenders>
|
||||
<Console name="Console-Appender" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%date %highlight{%level %c{1}.%method - %msg%n}{INFO=white,WARN=red,FATAL=bright red}"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="Console-Appender"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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<SerializationCustomSerializer<*, *>>,
|
||||
cordappSerializationWhitelists: Set<SerializationWhitelist>,
|
||||
serializerFactoriesForContexts: AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>
|
||||
) : 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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<PublicKey> = emptyList(),
|
||||
override val signers: List<Party> = emptyList()
|
||||
) : AbstractAttachment({ simpleZip }, "app")
|
@ -1,6 +0,0 @@
|
||||
@file:JvmName("SampleData")
|
||||
package net.corda.deterministic.verifier
|
||||
|
||||
import net.corda.core.contracts.TypeOnlyCommandData
|
||||
|
||||
object SampleCommandData : TypeOnlyCommandData()
|
@ -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<WireTransaction>,
|
||||
val dependencies: Array<SerializedBytes<WireTransaction>>,
|
||||
val attachments: Array<ByteArray>,
|
||||
val networkParameters: SerializedBytes<NetworkParameters>) {
|
||||
fun toLedgerTransaction(): LedgerTransaction {
|
||||
val deps = dependencies.map { it.deserialize() }.associateBy(WireTransaction::id)
|
||||
val attachments = attachments.map { it.deserialize<Attachment>() }
|
||||
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() }
|
||||
)
|
||||
}
|
||||
}
|
@ -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<TransactionVerificationRequest>()
|
||||
.toLedgerTransaction()
|
||||
}
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<TestCordappInternal> = 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<DummyContract.SingleOwnerState>(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, StatesToRecord>(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, StatesToRecord>(
|
||||
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<SenderDistributionRecord>, 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, StatesToRecord>(SecureHash.sha256(BOB_NAME.toString()) to StatesToRecord.ONLY_RELEVANT), hashedDL.peerHashToStatesToRecord)
|
||||
}
|
||||
validateSenderAndReceiverTimestamps(sdr, rdr!!)
|
||||
}
|
||||
|
||||
private fun getSenderRecoveryData(id: SecureHash, database: CordaPersistence): List<SenderDistributionRecord> {
|
||||
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<StateAndRef<DummyContract.SingleOwnerState>>() {
|
||||
|
||||
@Suspendable
|
||||
override fun call(): StateAndRef<DummyContract.SingleOwnerState> {
|
||||
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<FlowSession>()))
|
||||
return notarised.coreTransaction.outRef(0)
|
||||
}
|
||||
}
|
||||
|
||||
@StartableByRPC
|
||||
@InitiatingFlow
|
||||
class SpendFlow(private val stateAndRef: StateAndRef<DummyContract.SingleOwnerState>, private val newOwner: Party,
|
||||
private val handlePropagatedNotaryError: Boolean = false) : FlowLogic<SignedTransaction>() {
|
||||
|
||||
@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<Unit>() {
|
||||
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
val handleNotaryError = otherSide.receive<Boolean>().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<DummyContract.SingleOwnerState>, private val newOwner: Party) : FlowLogic<SignedTransaction>() {
|
||||
|
||||
@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<SignedTransaction>() {
|
||||
|
||||
@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<List<TransactionSignature>>()
|
||||
.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<TransactionSignature>) : FlowLogic<SignedTransaction>() {
|
||||
|
||||
@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<DummyContract.SingleOwnerState>, private val newOwner: Party) : FlowLogic<SignedTransaction>() {
|
||||
// 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<Unit>() {
|
||||
@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<TestCordappInternal> = 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<TestCordappInternal> = 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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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<SignedTransaction>() {
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
@Suppress("DEPRECATION")
|
||||
return subFlow(FinalityFlow(transaction))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,12 +99,17 @@ class ResolveTransactionsFlowTest {
|
||||
// DOCEND 1
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `dependency with an error`() {
|
||||
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()
|
||||
assertFailsWith(SignedTransaction.SignaturesMissingException::class) { future.getOrThrow() }
|
||||
future.getOrThrow()
|
||||
}
|
||||
assertTrue(exception.cause.toString().contains("SignaturesMissingException"))
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
|
@ -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`() {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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];
|
||||
|
@ -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).
|
||||
|
@ -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
|
@ -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<Throwable>)
|
||||
}
|
||||
|
||||
@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 {
|
||||
|
@ -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"
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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)
|
@ -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,7 +49,6 @@ 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)
|
||||
}
|
||||
|
@ -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<T : Any>(val quantity: Long, val displayTokenSize: BigDecimal, val token: T) : Comparable<Amount<T>> {
|
||||
// TODO Proper lookup of currencies in a locale and context sensitive fashion is not supported and is left to the application.
|
||||
|
@ -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 {
|
||||
|
@ -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}")
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
/**
|
||||
|
@ -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.
|
||||
|
@ -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 <X>, etc.) and any additional metadata (issuer, grade, class, etc.).
|
||||
*/
|
||||
@KeepForDJVM
|
||||
interface FungibleAsset<T : Any> : FungibleState<Issued<T>>, OwnableState {
|
||||
/**
|
||||
* Amount represents a positive quantity of some issued product which can be cash, tokens, assets, or generally
|
||||
|
@ -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<T : Any> : ContractState {
|
||||
/**
|
||||
* Amount represents a positive quantity of some token which can be cash, tokens, stock, agreements, or generally
|
||||
|
@ -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<T : ContractState> {
|
||||
|
||||
@ -70,7 +67,6 @@ sealed class StatePointer<T : ContractState> {
|
||||
*
|
||||
* @param services a [ServiceHub] implementation is required to resolve the pointer.
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
abstract fun resolve(services: ServiceHub): StateAndRef<T>
|
||||
|
||||
/**
|
||||
@ -89,7 +85,6 @@ sealed class StatePointer<T : ContractState> {
|
||||
* - 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<T : ContractState>(
|
||||
override val pointer: StateRef,
|
||||
override val type: Class<T>,
|
||||
@ -110,7 +105,6 @@ class StaticPointer<T : ContractState>(
|
||||
*/
|
||||
@Throws(TransactionResolutionException::class)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@DeleteForDJVM
|
||||
override fun resolve(services: ServiceHub): StateAndRef<T> {
|
||||
val transactionState = services.loadState(pointer) as TransactionState<T>
|
||||
val castState: T = type.cast(transactionState.data)
|
||||
@ -148,7 +142,6 @@ class StaticPointer<T : ContractState>(
|
||||
* 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<T : LinearState>(
|
||||
override val pointer: UniqueIdentifier,
|
||||
override val type: Class<T>,
|
||||
@ -171,7 +164,6 @@ class LinearPointer<T : LinearState>(
|
||||
* @param services a [ServiceHub] implementation is required to perform a vault query.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@DeleteForDJVM
|
||||
override fun resolve(services: ServiceHub): StateAndRef<T> {
|
||||
// Return the latest version of the linear state.
|
||||
// This query will only ever return one or zero states.
|
||||
|
@ -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<out P : Any>(val issuer: PartyAndReference, val product: P) {
|
||||
init {
|
||||
@ -72,13 +68,11 @@ fun <T : Any> Amount<Issued<T>>.withoutIssuer(): Amount<T> = 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<out T : ContractState>(val state: TransactionState<T>, val ref: StateRef) {
|
||||
@ -179,7 +166,6 @@ data class StateAndRef<out T : ContractState>(val state: TransactionState<T>, 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<out T : ContractState>(val stateAndRef: StateAndRef<T>)
|
||||
|
||||
/** Filters a list of [StateAndRef] objects according to the type of the states */
|
||||
@ -191,7 +177,6 @@ inline fun <reified T : ContractState> Iterable<StateAndRef<ContractState>>.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<T : CommandData>(val value: T, val signers: List<PublicKey>) {
|
||||
// TODO Introduce NonEmptyList?
|
||||
@ -224,7 +207,6 @@ data class Command<T : CommandData>(val value: T, val signers: List<PublicKey>)
|
||||
}
|
||||
|
||||
/** 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<out T : CommandData>(
|
||||
val signers: List<PublicKey>,
|
||||
@ -256,7 +237,6 @@ data class CommandWithParties<out T : CommandData>(
|
||||
*
|
||||
* 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<in OldState : ContractState, out NewState : ContractState> : 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<in OldState : ContractState, out NewState : ContractS
|
||||
* This interface allows specifying a custom legacy contract constraint for upgraded contracts. The default for [UpgradedContract]
|
||||
* is [WhitelistedByZoneAttachmentConstraint].
|
||||
*/
|
||||
@KeepForDJVM
|
||||
interface UpgradedContractWithLegacyConstraint<in OldState : ContractState, out NewState : ContractState> : UpgradedContract<OldState, NewState> {
|
||||
/**
|
||||
* A validator for the legacy (pre-upgrade) contract attachments on the transaction.
|
||||
@ -325,14 +303,11 @@ interface UpgradedContractWithLegacyConstraint<in OldState : ContractState, out
|
||||
* but it is highlighted that one should always ensure it has sufficient entropy.
|
||||
*/
|
||||
@CordaSerializable
|
||||
@KeepForDJVM
|
||||
class PrivacySalt(bytes: ByteArray) : OpaqueBytes(bytes) {
|
||||
/** Constructs a salt with a randomly-generated saltLength byte value. */
|
||||
@DeleteForDJVM
|
||||
constructor(saltLength: Int) : this(secureRandomBytes(saltLength))
|
||||
|
||||
/** Constructs a salt with a randomly-generated 32 byte value. */
|
||||
@DeleteForDJVM
|
||||
constructor() : this(MINIMUM_SIZE)
|
||||
|
||||
init {
|
||||
@ -343,7 +318,6 @@ class PrivacySalt(bytes: ByteArray) : OpaqueBytes(bytes) {
|
||||
companion object {
|
||||
private const val MINIMUM_SIZE = 32
|
||||
|
||||
@DeleteForDJVM
|
||||
@JvmStatic
|
||||
fun createFor(algorithm: String): PrivacySalt {
|
||||
return PrivacySalt(SecureHash.digestLengthFor(algorithm))
|
||||
@ -357,5 +331,4 @@ class PrivacySalt(bytes: ByteArray) : OpaqueBytes(bytes) {
|
||||
* @property state A state
|
||||
* @property contract The contract that should verify the state
|
||||
*/
|
||||
@KeepForDJVM
|
||||
data class StateAndContract(val state: ContractState, val contract: ContractClassName)
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.corda.core.contracts
|
||||
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.until
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
@ -79,7 +78,6 @@ abstract class TimeWindow {
|
||||
/** Returns true iff the given [instant] is within the time interval of this [TimeWindow]. */
|
||||
abstract operator fun contains(instant: Instant): Boolean
|
||||
|
||||
@KeepForDJVM
|
||||
private data class From(override val fromTime: Instant) : TimeWindow() {
|
||||
override val untilTime: Instant? get() = null
|
||||
override val midpoint: Instant? get() = null
|
||||
@ -87,7 +85,6 @@ abstract class TimeWindow {
|
||||
override fun toString(): String = "[$fromTime, ∞)"
|
||||
}
|
||||
|
||||
@KeepForDJVM
|
||||
private data class Until(override val untilTime: Instant) : TimeWindow() {
|
||||
override val fromTime: Instant? get() = null
|
||||
override val midpoint: Instant? get() = null
|
||||
@ -95,7 +92,6 @@ abstract class TimeWindow {
|
||||
override fun toString(): String = "(∞, $untilTime)"
|
||||
}
|
||||
|
||||
@KeepForDJVM
|
||||
private data class Between(override val fromTime: Instant, override val untilTime: Instant) : TimeWindow() {
|
||||
init {
|
||||
require(fromTime < untilTime) { "fromTime must be earlier than untilTime" }
|
||||
|
@ -1,7 +1,5 @@
|
||||
@file:KeepForDJVM
|
||||
package net.corda.core.contracts
|
||||
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.requiredContractClassName
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
|
@ -1,8 +1,6 @@
|
||||
package net.corda.core.contracts
|
||||
|
||||
import net.corda.core.CordaException
|
||||
import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.identity.Party
|
||||
@ -18,7 +16,6 @@ import java.security.PublicKey
|
||||
*
|
||||
* @property hash Merkle root of the transaction being resolved, see [net.corda.core.transactions.WireTransaction.id]
|
||||
*/
|
||||
@KeepForDJVM
|
||||
open class TransactionResolutionException @JvmOverloads constructor(val hash: SecureHash, message: String = "Transaction resolution failure for $hash") : FlowException(message) {
|
||||
/**
|
||||
* Thrown if a transaction specifies a set of parameters that aren't stored locally yet verification is requested.
|
||||
@ -35,7 +32,6 @@ open class TransactionResolutionException @JvmOverloads constructor(val hash: Se
|
||||
*
|
||||
* @property hash Hash of the bytes of the attachment, see [Attachment.id]
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class AttachmentResolutionException(val hash: AttachmentId) : FlowException("Attachment resolution failure for $hash")
|
||||
|
||||
/**
|
||||
@ -43,7 +39,6 @@ class AttachmentResolutionException(val hash: AttachmentId) : FlowException("Att
|
||||
* for this error is provided via the [message] and [cause].
|
||||
* @property attachmentId
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class BrokenAttachmentException(val attachmentId: AttachmentId, message: String?, cause: Throwable?)
|
||||
: FlowException("Attachment $attachmentId has error (${message ?: "no message"})", cause)
|
||||
|
||||
@ -64,7 +59,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
*
|
||||
* @property contractClass The fully qualified class name of the failing contract.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class ContractRejection internal constructor(txId: SecureHash, val contractClass: String, cause: Throwable?, message: String) : TransactionVerificationException(txId, "Contract verification failed: $message, contract: $contractClass", cause) {
|
||||
internal constructor(txId: SecureHash, contract: Contract, cause: Throwable) : this(txId, contract.javaClass.name, cause, cause.message ?: "")
|
||||
}
|
||||
@ -78,7 +72,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
* @property inputConstraint The constraint of the input state.
|
||||
* @property outputConstraint The constraint of the outputs state.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class ConstraintPropagationRejection(txId: SecureHash, message: String) : TransactionVerificationException(txId, message, null) {
|
||||
constructor(txId: SecureHash,
|
||||
contractClass: String,
|
||||
@ -97,7 +90,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
*
|
||||
* @property contractClass The fully qualified class name of the failing contract.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class ContractConstraintRejection(txId: SecureHash, val contractClass: String)
|
||||
: TransactionVerificationException(txId, "Contract constraints failed for $contractClass", null)
|
||||
|
||||
@ -107,7 +99,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
* @property contractClass The fully qualified class name of the failing contract.
|
||||
* @property reason a message containing the reason the constraint is invalid included in thrown the exception.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class InvalidConstraintRejection(txId: SecureHash, val contractClass: String, val reason: String)
|
||||
: TransactionVerificationException(txId, "Contract constraints failed for $contractClass. $reason", null)
|
||||
|
||||
@ -117,7 +108,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
*
|
||||
* @property contractClass The fully qualified class name of the failing contract.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class MissingAttachmentRejection(txId: SecureHash, val contractClass: String)
|
||||
: TransactionVerificationException(txId, "Contract constraints failed, could not find attachment for: $contractClass", null)
|
||||
|
||||
@ -130,14 +120,12 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
*
|
||||
* @property contractClass The fully qualified class name of the failing contract.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class ConflictingAttachmentsRejection(txId: SecureHash, val contractClass: String)
|
||||
: TransactionVerificationException(txId, "Contract constraints failed for: $contractClass, because multiple attachments providing this contract were attached.", null)
|
||||
|
||||
/**
|
||||
* Indicates that the same attachment has been added multiple times to a transaction.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class DuplicateAttachmentsRejection(txId: SecureHash, val attachmentId: Attachment)
|
||||
: TransactionVerificationException(txId, "The attachment: $attachmentId was added multiple times.", null)
|
||||
|
||||
@ -147,7 +135,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
*
|
||||
* @property contractClass The fully qualified class name of the failing contract.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class ContractCreationError internal constructor(txId: SecureHash, val contractClass: String, cause: Throwable?, message: String)
|
||||
: TransactionVerificationException(txId, "Contract verification failed: $message, could not create contract class: $contractClass", cause) {
|
||||
internal constructor(txId: SecureHash, contractClass: String, cause: Throwable) : this(txId, contractClass, cause, cause.message ?: "")
|
||||
@ -159,7 +146,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
* @property txNotary the [Party] specified by the transaction header.
|
||||
* @property outputNotary the [Party] specified by the errant state.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class NotaryChangeInWrongTransactionType(txId: SecureHash, val txNotary: Party, val outputNotary: Party)
|
||||
: TransactionVerificationException(txId, "Found unexpected notary change in transaction. Tx notary: $txNotary, found: $outputNotary", null)
|
||||
|
||||
@ -172,7 +158,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
* @property missing the index of the state missing the encumbrance.
|
||||
* @property inOut whether the issue exists in the input list or output list.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class TransactionMissingEncumbranceException(txId: SecureHash, val missing: Int, val inOut: Direction)
|
||||
: TransactionVerificationException(txId, "Missing required encumbrance $missing in $inOut", null)
|
||||
|
||||
@ -180,7 +165,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
* If two or more states refer to another state (as their encumbrance), then the bi-directionality property cannot
|
||||
* be satisfied.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class TransactionDuplicateEncumbranceException(txId: SecureHash, message: String)
|
||||
: TransactionVerificationException(txId, message, null) {
|
||||
constructor(txId: SecureHash, index: Int) : this(txId, "The bi-directionality property of encumbered output states " +
|
||||
@ -191,7 +175,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
* An encumbered state should also be referenced as the encumbrance of another state in order to satisfy the
|
||||
* bi-directionality property (a full cycle should be present).
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class TransactionNonMatchingEncumbranceException(txId: SecureHash, message: String)
|
||||
: TransactionVerificationException(txId, message, null) {
|
||||
constructor(txId: SecureHash, nonMatching: Collection<Int>) : 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<ContractState>, 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<ContractState>) : 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<StateRef>)
|
||||
: 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<PublicKey>) : 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<SecureHash>) :
|
||||
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)
|
||||
|
||||
|
@ -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<UniqueIdentifier> {
|
||||
data class UniqueIdentifier @JvmOverloads constructor(val externalId: String? = null, val id: UUID = UUID.randomUUID()) : Comparable<UniqueIdentifier> {
|
||||
override fun toString(): String = if (externalId != null) "${externalId}_$id" else id.toString()
|
||||
|
||||
companion object {
|
||||
|
@ -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<String>
|
||||
|
@ -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?,
|
||||
|
@ -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
|
||||
|
@ -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<NodeAndWeight>) : PublicKey {
|
||||
companion object {
|
||||
const val KEY_ALGORITHM = "COMPOSITE"
|
||||
@ -144,7 +149,6 @@ class CompositeKey private constructor(val threshold: Int, children: List<NodeAn
|
||||
* Holds node - weight pairs for a CompositeKey. Ordered first by weight, then by node's hashCode.
|
||||
* Each node should be assigned with a positive weight to avoid certain types of weight underflow attacks.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
@CordaSerializable
|
||||
data class NodeAndWeight(val node: PublicKey, val weight: Int) : Comparable<NodeAndWeight>, ASN1Object() {
|
||||
init {
|
||||
@ -162,7 +166,7 @@ class CompositeKey private constructor(val threshold: Int, children: List<NodeAn
|
||||
|
||||
override fun toASN1Primitive(): ASN1Primitive {
|
||||
val vector = ASN1EncodableVector()
|
||||
vector.add(DERBitString(node.encoded))
|
||||
vector.add(DERBitString(Crypto.encodePublicKey(node)))
|
||||
vector.add(ASN1Integer(weight.toLong()))
|
||||
return DERSequence(vector)
|
||||
}
|
||||
@ -243,7 +247,6 @@ class CompositeKey private constructor(val threshold: Int, children: List<NodeAn
|
||||
override fun toString() = "(${children.joinToString()})"
|
||||
|
||||
/** A helper class for building a [CompositeKey]. */
|
||||
@KeepForDJVM
|
||||
class Builder {
|
||||
private val children: MutableList<NodeAndWeight> = mutableListOf()
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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<TransactionSignature>) {
|
||||
companion object {
|
||||
val EMPTY = CompositeSignaturesWithKeys(emptyList())
|
||||
|
@ -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<Pair<String, String>, Optional<Service>>()
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
private fun superGetService(type: String, algorithm: String): Service? = super.getService(type, algorithm)
|
||||
|
||||
@StubOutForDJVM
|
||||
private fun makeCachingFactory(): Function2<String, String, Service?> {
|
||||
return object : Function2<String, String, Service?> {
|
||||
private val services = ConcurrentHashMap<Pair<String, String>, Optional<Service>>()
|
||||
|
||||
override fun invoke(type: String, algorithm: String): Service? {
|
||||
override fun getService(type: String, algorithm: String): Service? {
|
||||
return services.getOrPut(Pair(type, algorithm)) {
|
||||
Optional.ofNullable(superGetService(type, algorithm))
|
||||
}.orElse(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeFactory(): Function2<String, String, Service?> {
|
||||
return object : Function2<String, String, Service?> {
|
||||
override fun invoke(type: String, algorithm: String): Service? {
|
||||
return superGetService(type, algorithm)
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun superGetService(type: String, algorithm: String): Service? = super.getService(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,
|
||||
|
@ -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
|
||||
* <li>SPHINCS256_SHA512 (SPHINCS-256 hash-based signature scheme using SHA512 as hash algorithm).
|
||||
* </ul>
|
||||
*/
|
||||
@KeepForDJVM
|
||||
object Crypto {
|
||||
/**
|
||||
* RSA PKCS#1 signature scheme using SHA256 for message hashing.
|
||||
@ -227,7 +226,6 @@ object Crypto {
|
||||
@JvmStatic
|
||||
fun supportedSignatureSchemes(): List<SignatureScheme> = 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 {
|
||||
return PublicKeyCache.publicKeyForCachedBytes(ByteSequence.of(encodedKey)) ?: {
|
||||
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey)
|
||||
val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm)
|
||||
val keyFactory = keyFactory(signatureScheme)
|
||||
return convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)))
|
||||
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<PublicKey>()
|
||||
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())
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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<PublicKey> {
|
||||
override fun getAlgorithm() = "NULL"
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user