mirror of
https://github.com/corda/corda.git
synced 2025-01-21 03:55:00 +00:00
Merge branch 'master' into shams-master-merge-271117
This commit is contained in:
commit
9fefabbb88
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@ -57,6 +57,8 @@
|
|||||||
<module name="finance_integrationTest" target="1.8" />
|
<module name="finance_integrationTest" target="1.8" />
|
||||||
<module name="finance_main" target="1.8" />
|
<module name="finance_main" target="1.8" />
|
||||||
<module name="finance_test" target="1.8" />
|
<module name="finance_test" target="1.8" />
|
||||||
|
<module name="gradle-plugins-cordapp_main" target="1.8" />
|
||||||
|
<module name="gradle-plugins-cordapp_test" target="1.8" />
|
||||||
<module name="graphs_main" target="1.8" />
|
<module name="graphs_main" target="1.8" />
|
||||||
<module name="graphs_test" target="1.8" />
|
<module name="graphs_test" target="1.8" />
|
||||||
<module name="irs-demo-cordapp_integrationTest" target="1.8" />
|
<module name="irs-demo-cordapp_integrationTest" target="1.8" />
|
||||||
|
@ -63,21 +63,21 @@ import javax.crypto.spec.SecretKeySpec
|
|||||||
*/
|
*/
|
||||||
object Crypto {
|
object Crypto {
|
||||||
/**
|
/**
|
||||||
* RSA_SHA256 signature scheme using SHA256 as hash algorithm and MGF1 (with SHA256) as mask generation function.
|
* RSA_SHA256 signature scheme using SHA256 as hash algorithm.
|
||||||
* Note: Recommended key size >= 3072 bits.
|
* Note: Recommended key size >= 3072 bits.
|
||||||
*/
|
*/
|
||||||
@JvmField
|
@JvmField
|
||||||
val RSA_SHA256 = SignatureScheme(
|
val RSA_SHA256 = SignatureScheme(
|
||||||
1,
|
1,
|
||||||
"RSA_SHA256",
|
"RSA_SHA256",
|
||||||
AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null),
|
AlgorithmIdentifier(PKCSObjectIdentifiers.sha256WithRSAEncryption, null),
|
||||||
emptyList(),
|
listOf(AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null)),
|
||||||
BouncyCastleProvider.PROVIDER_NAME,
|
BouncyCastleProvider.PROVIDER_NAME,
|
||||||
"RSA",
|
"RSA",
|
||||||
"SHA256WITHRSAANDMGF1",
|
"SHA256WITHRSAEncryption",
|
||||||
null,
|
null,
|
||||||
3072,
|
3072,
|
||||||
"RSA_SHA256 signature scheme using SHA256 as hash algorithm and MGF1 (with SHA256) as mask generation function."
|
"RSA_SHA256 signature scheme using SHA256 as hash algorithm."
|
||||||
)
|
)
|
||||||
|
|
||||||
/** ECDSA signature scheme using the secp256k1 Koblitz curve. */
|
/** ECDSA signature scheme using the secp256k1 Koblitz curve. */
|
||||||
@ -117,7 +117,7 @@ object Crypto {
|
|||||||
"EDDSA_ED25519_SHA512",
|
"EDDSA_ED25519_SHA512",
|
||||||
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00
|
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00
|
||||||
AlgorithmIdentifier(ASN1ObjectIdentifier("1.3.101.112"), null),
|
AlgorithmIdentifier(ASN1ObjectIdentifier("1.3.101.112"), null),
|
||||||
emptyList(),
|
emptyList(), // Both keys and the signature scheme use the same OID in i2p library.
|
||||||
// We added EdDSA to bouncy castle for certificate signing.
|
// We added EdDSA to bouncy castle for certificate signing.
|
||||||
BouncyCastleProvider.PROVIDER_NAME,
|
BouncyCastleProvider.PROVIDER_NAME,
|
||||||
"1.3.101.112",
|
"1.3.101.112",
|
||||||
|
@ -348,6 +348,12 @@ abstract class FlowLogic<out T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a pair of the current progress step index (as integer) in steps tree of current [progressTracker], and an observable
|
||||||
|
* of its upcoming changes.
|
||||||
|
*
|
||||||
|
* @return Returns null if this flow has no progress tracker.
|
||||||
|
*/
|
||||||
fun trackStepsTreeIndex(): DataFeed<Int, Int>? {
|
fun trackStepsTreeIndex(): DataFeed<Int, Int>? {
|
||||||
// TODO this is not threadsafe, needs an atomic get-step-and-subscribe
|
// TODO this is not threadsafe, needs an atomic get-step-and-subscribe
|
||||||
return progressTracker?.let {
|
return progressTracker?.let {
|
||||||
@ -355,6 +361,12 @@ abstract class FlowLogic<out T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a pair of the current steps tree of current [progressTracker] as pairs of zero-based depth and stringified step
|
||||||
|
* label and observable of upcoming changes to the structure.
|
||||||
|
*
|
||||||
|
* @return Returns null if this flow has no progress tracker.
|
||||||
|
*/
|
||||||
fun trackStepsTree(): DataFeed<List<Pair<Int,String>>, List<Pair<Int,String>>>? {
|
fun trackStepsTree(): DataFeed<List<Pair<Int,String>>, List<Pair<Int,String>>>? {
|
||||||
// TODO this is not threadsafe, needs an atomic get-step-and-subscribe
|
// TODO this is not threadsafe, needs an atomic get-step-and-subscribe
|
||||||
return progressTracker?.let {
|
return progressTracker?.let {
|
||||||
|
@ -304,6 +304,6 @@ fun TransactionBuilder.toWireTransaction(services: ServicesForResolution, serial
|
|||||||
fun TransactionBuilder.toLedgerTransaction(services: ServiceHub, serializationContext: SerializationContext) = toLedgerTransactionWithContext(services, serializationContext)
|
fun TransactionBuilder.toLedgerTransaction(services: ServiceHub, serializationContext: SerializationContext) = toLedgerTransactionWithContext(services, serializationContext)
|
||||||
|
|
||||||
/** Convenience method to get the package name of a class literal. */
|
/** Convenience method to get the package name of a class literal. */
|
||||||
val KClass<*>.packageName get() = java.`package`.name
|
val KClass<*>.packageName: String get() = java.`package`.name
|
||||||
|
|
||||||
fun URL.openHttpConnection(): HttpURLConnection = openConnection() as HttpURLConnection
|
fun URL.openHttpConnection(): HttpURLConnection = openConnection() as HttpURLConnection
|
||||||
|
@ -37,7 +37,7 @@ object NodeInfoSchemaV1 : MappedSchema(
|
|||||||
|
|
||||||
@Column(name = "legal_identities_certs")
|
@Column(name = "legal_identities_certs")
|
||||||
@ManyToMany(cascade = arrayOf(CascadeType.ALL))
|
@ManyToMany(cascade = arrayOf(CascadeType.ALL))
|
||||||
@JoinTable(name = "link_nodeinfo_party",
|
@JoinTable(name = "node_link_nodeinfo_party",
|
||||||
joinColumns = arrayOf(JoinColumn(name = "node_info_id")),
|
joinColumns = arrayOf(JoinColumn(name = "node_info_id")),
|
||||||
inverseJoinColumns = arrayOf(JoinColumn(name = "party_name")))
|
inverseJoinColumns = arrayOf(JoinColumn(name = "party_name")))
|
||||||
val legalIdentitiesAndCerts: List<DBPartyAndCertificate>,
|
val legalIdentitiesAndCerts: List<DBPartyAndCertificate>,
|
||||||
|
@ -8,13 +8,17 @@ import rx.Observable
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* [FlowHandle] is a serialisable handle for the started flow, parameterised by the type of the flow's return value.
|
* [FlowHandle] is a serialisable handle for the started flow, parameterised by the type of the flow's return value.
|
||||||
*
|
|
||||||
* @property id The started state machine's ID.
|
|
||||||
* @property returnValue A [CordaFuture] of the flow's return value.
|
|
||||||
*/
|
*/
|
||||||
@DoNotImplement
|
@DoNotImplement
|
||||||
interface FlowHandle<A> : AutoCloseable {
|
interface FlowHandle<A> : AutoCloseable {
|
||||||
|
/**
|
||||||
|
* The started state machine's ID.
|
||||||
|
*/
|
||||||
val id: StateMachineRunId
|
val id: StateMachineRunId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [CordaFuture] of the flow's return value.
|
||||||
|
*/
|
||||||
val returnValue: CordaFuture<A>
|
val returnValue: CordaFuture<A>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,15 +29,23 @@ interface FlowHandle<A> : AutoCloseable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* [FlowProgressHandle] is a serialisable handle for the started flow, parameterised by the type of the flow's return value.
|
* [FlowProgressHandle] is a serialisable handle for the started flow, parameterised by the type of the flow's return value.
|
||||||
*
|
|
||||||
* @property progress The stream of progress tracker events.
|
|
||||||
*/
|
*/
|
||||||
interface FlowProgressHandle<A> : FlowHandle<A> {
|
interface FlowProgressHandle<A> : FlowHandle<A> {
|
||||||
|
/**
|
||||||
|
* The stream of progress tracker events.
|
||||||
|
*/
|
||||||
val progress: Observable<String>
|
val progress: Observable<String>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [DataFeed] of current step in the steps tree, see [ProgressTracker]
|
||||||
|
*/
|
||||||
val stepsTreeIndexFeed: DataFeed<Int, Int>?
|
val stepsTreeIndexFeed: DataFeed<Int, Int>?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [DataFeed] of current steps tree, see [ProgressTracker]
|
||||||
|
*/
|
||||||
val stepsTreeFeed: DataFeed<List<Pair<Int, String>>, List<Pair<Int, String>>>?
|
val stepsTreeFeed: DataFeed<List<Pair<Int, String>>, List<Pair<Int, String>>>?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this function for flows whose returnValue and progress are not going to be used or tracked, so as to free up
|
* Use this function for flows whose returnValue and progress are not going to be used or tracked, so as to free up
|
||||||
* server resources.
|
* server resources.
|
||||||
|
@ -99,6 +99,7 @@ class ProgressTracker(vararg steps: Step) {
|
|||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The zero-bases index of the current step in a [allStepsLabels] list */
|
||||||
var stepsTreeIndex: Int = -1
|
var stepsTreeIndex: Int = -1
|
||||||
private set(value) {
|
private set(value) {
|
||||||
field = value
|
field = value
|
||||||
@ -226,6 +227,10 @@ class ProgressTracker(vararg steps: Step) {
|
|||||||
*/
|
*/
|
||||||
val allSteps: List<Pair<Int, Step>> get() = _allStepsCache
|
val allSteps: List<Pair<Int, Step>> get() = _allStepsCache
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of all steps label in this ProgressTracker and the children, with the indent level provided starting at zero.
|
||||||
|
* Note that UNSTARTED is never counted, and DONE is only counted at the calling level.
|
||||||
|
*/
|
||||||
val allStepsLabels: List<Pair<Int, String>> get() = _allStepsLabels()
|
val allStepsLabels: List<Pair<Int, String>> get() = _allStepsLabels()
|
||||||
|
|
||||||
private var curChangeSubscription: Subscription? = null
|
private var curChangeSubscription: Subscription? = null
|
||||||
@ -245,8 +250,14 @@ class ProgressTracker(vararg steps: Step) {
|
|||||||
*/
|
*/
|
||||||
val changes: Observable<Change> get() = _changes
|
val changes: Observable<Change> get() = _changes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An observable stream of changes to the [allStepsLabels]
|
||||||
|
*/
|
||||||
val stepsTreeChanges: Observable<List<Pair<Int,String>>> get() = _stepsTreeChanges
|
val stepsTreeChanges: Observable<List<Pair<Int,String>>> get() = _stepsTreeChanges
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An observable stream of changes to the [stepsTreeIndex]
|
||||||
|
*/
|
||||||
val stepsTreeIndexChanges: Observable<Int> get() = _stepsTreeIndexChanges
|
val stepsTreeIndexChanges: Observable<Int> get() = _stepsTreeIndexChanges
|
||||||
|
|
||||||
/** Returns true if the progress tracker has ended, either by reaching the [DONE] step or prematurely with an error */
|
/** Returns true if the progress tracker has ended, either by reaching the [DONE] step or prematurely with an error */
|
||||||
|
@ -8,7 +8,7 @@ import net.corda.core.internal.div
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.toBase58String
|
import net.corda.core.utilities.toBase58String
|
||||||
import net.corda.node.utilities.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.testing.kryoSpecific
|
import net.corda.testing.kryoSpecific
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
@ -3,7 +3,7 @@ package net.corda.core.crypto
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.toTypedArray
|
import net.corda.core.internal.toTypedArray
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.internal.cert
|
||||||
import net.corda.node.utilities.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
@ -50,7 +50,7 @@ class X509NameConstraintsTest {
|
|||||||
|
|
||||||
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
|
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
|
||||||
val pathValidator = CertPathValidator.getInstance("PKIX")
|
val pathValidator = CertPathValidator.getInstance("PKIX")
|
||||||
val certFactory = CertificateFactory.getInstance("X509")
|
val certFactory = X509CertificateFactory().delegate
|
||||||
|
|
||||||
assertFailsWith(CertPathValidatorException::class) {
|
assertFailsWith(CertPathValidatorException::class) {
|
||||||
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank B"), nameConstraints)
|
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank B"), nameConstraints)
|
||||||
@ -85,7 +85,7 @@ class X509NameConstraintsTest {
|
|||||||
.map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray()
|
.map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray()
|
||||||
|
|
||||||
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
|
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
|
||||||
val certFactory = CertificateFactory.getInstance("X509")
|
val certFactory = X509CertificateFactory().delegate
|
||||||
Crypto.ECDSA_SECP256R1_SHA256
|
Crypto.ECDSA_SECP256R1_SHA256
|
||||||
val pathValidator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME)
|
val pathValidator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME)
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ import net.corda.core.crypto.entropyToKeyPair
|
|||||||
import net.corda.core.internal.read
|
import net.corda.core.internal.read
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.node.utilities.KEYSTORE_TYPE
|
import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE
|
||||||
import net.corda.node.utilities.save
|
import net.corda.nodeapi.internal.crypto.save
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.getTestPartyAndCertificate
|
import net.corda.testing.getTestPartyAndCertificate
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
@ -98,7 +98,7 @@ class ProgressTrackerTest {
|
|||||||
|
|
||||||
val allSteps = pt.allSteps
|
val allSteps = pt.allSteps
|
||||||
|
|
||||||
//capture notifications
|
// Capture notifications.
|
||||||
val stepsIndexNotifications = LinkedList<Int>()
|
val stepsIndexNotifications = LinkedList<Int>()
|
||||||
pt.stepsTreeIndexChanges.subscribe {
|
pt.stepsTreeIndexChanges.subscribe {
|
||||||
stepsIndexNotifications += it
|
stepsIndexNotifications += it
|
||||||
@ -113,7 +113,7 @@ class ProgressTrackerTest {
|
|||||||
assertEquals(step, allSteps[pt.stepsTreeIndex].second)
|
assertEquals(step, allSteps[pt.stepsTreeIndex].second)
|
||||||
}
|
}
|
||||||
|
|
||||||
//travel tree
|
// Travel tree.
|
||||||
pt.currentStep = SimpleSteps.ONE
|
pt.currentStep = SimpleSteps.ONE
|
||||||
assertCurrentStepsTree(0, SimpleSteps.ONE)
|
assertCurrentStepsTree(0, SimpleSteps.ONE)
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ class ProgressTrackerTest {
|
|||||||
pt.currentStep = SimpleSteps.THREE
|
pt.currentStep = SimpleSteps.THREE
|
||||||
assertCurrentStepsTree(5, SimpleSteps.THREE)
|
assertCurrentStepsTree(5, SimpleSteps.THREE)
|
||||||
|
|
||||||
//assert no structure changes and proper steps propagation
|
// Assert no structure changes and proper steps propagation.
|
||||||
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(0, 1, 3, 5))
|
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(0, 1, 3, 5))
|
||||||
assertThat(stepsTreeNotification).isEmpty()
|
assertThat(stepsTreeNotification).isEmpty()
|
||||||
}
|
}
|
||||||
@ -135,13 +135,13 @@ class ProgressTrackerTest {
|
|||||||
fun `structure changes are pushed down when progress trackers are added`() {
|
fun `structure changes are pushed down when progress trackers are added`() {
|
||||||
pt.setChildProgressTracker(SimpleSteps.TWO, pt2)
|
pt.setChildProgressTracker(SimpleSteps.TWO, pt2)
|
||||||
|
|
||||||
//capture notifications
|
// Capture notifications.
|
||||||
val stepsIndexNotifications = LinkedList<Int>()
|
val stepsIndexNotifications = LinkedList<Int>()
|
||||||
pt.stepsTreeIndexChanges.subscribe {
|
pt.stepsTreeIndexChanges.subscribe {
|
||||||
stepsIndexNotifications += it
|
stepsIndexNotifications += it
|
||||||
}
|
}
|
||||||
|
|
||||||
//put current state as a first change for simplicity when asserting
|
// Put current state as a first change for simplicity when asserting.
|
||||||
val stepsTreeNotification = mutableListOf(pt.allStepsLabels)
|
val stepsTreeNotification = mutableListOf(pt.allStepsLabels)
|
||||||
println(pt.allStepsLabels)
|
println(pt.allStepsLabels)
|
||||||
pt.stepsTreeChanges.subscribe {
|
pt.stepsTreeChanges.subscribe {
|
||||||
@ -164,7 +164,7 @@ class ProgressTrackerTest {
|
|||||||
|
|
||||||
assertCurrentStepsTree(9, SimpleSteps.FOUR)
|
assertCurrentStepsTree(9, SimpleSteps.FOUR)
|
||||||
|
|
||||||
//assert no structure changes and proper steps propagation
|
// Assert no structure changes and proper steps propagation.
|
||||||
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 6, 9))
|
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 6, 9))
|
||||||
assertThat(stepsTreeNotification).hasSize(2) // 1 change + 1 our initial state
|
assertThat(stepsTreeNotification).hasSize(2) // 1 change + 1 our initial state
|
||||||
}
|
}
|
||||||
@ -173,13 +173,13 @@ class ProgressTrackerTest {
|
|||||||
fun `structure changes are pushed down when progress trackers are removed`() {
|
fun `structure changes are pushed down when progress trackers are removed`() {
|
||||||
pt.setChildProgressTracker(SimpleSteps.TWO, pt2)
|
pt.setChildProgressTracker(SimpleSteps.TWO, pt2)
|
||||||
|
|
||||||
//capture notifications
|
// Capture notifications.
|
||||||
val stepsIndexNotifications = LinkedList<Int>()
|
val stepsIndexNotifications = LinkedList<Int>()
|
||||||
pt.stepsTreeIndexChanges.subscribe {
|
pt.stepsTreeIndexChanges.subscribe {
|
||||||
stepsIndexNotifications += it
|
stepsIndexNotifications += it
|
||||||
}
|
}
|
||||||
|
|
||||||
//put current state as a first change for simplicity when asserting
|
// Put current state as a first change for simplicity when asserting.
|
||||||
val stepsTreeNotification = mutableListOf(pt.allStepsLabels)
|
val stepsTreeNotification = mutableListOf(pt.allStepsLabels)
|
||||||
pt.stepsTreeChanges.subscribe {
|
pt.stepsTreeChanges.subscribe {
|
||||||
stepsTreeNotification += it
|
stepsTreeNotification += it
|
||||||
@ -199,9 +199,9 @@ class ProgressTrackerTest {
|
|||||||
|
|
||||||
assertCurrentStepsTree(2, BabySteps.UNOS)
|
assertCurrentStepsTree(2, BabySteps.UNOS)
|
||||||
|
|
||||||
//assert no structure changes and proper steps propagation
|
// Assert no structure changes and proper steps propagation.
|
||||||
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 4, 2))
|
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 4, 2))
|
||||||
assertThat(stepsTreeNotification).hasSize(2) // 1 change + 1 our initial state
|
assertThat(stepsTreeNotification).hasSize(2) // 1 change + 1 our initial state.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -91,6 +91,9 @@ UNRELEASED
|
|||||||
|
|
||||||
* ``waitForAllNodesToFinish()`` method in ``DriverDSLExposedInterface`` has instead become a parameter on driver creation.
|
* ``waitForAllNodesToFinish()`` method in ``DriverDSLExposedInterface`` has instead become a parameter on driver creation.
|
||||||
|
|
||||||
|
* ``database.transactionIsolationLevel`` values now follow the ``java.sql.Connection`` int constants but without the
|
||||||
|
"TRANSACTION_" prefix, i.e. "NONE", "READ_UNCOMMITTED", etc.
|
||||||
|
|
||||||
.. _changelog_v1:
|
.. _changelog_v1:
|
||||||
|
|
||||||
Release 1.0
|
Release 1.0
|
||||||
|
@ -68,6 +68,13 @@ path to the node's base directory.
|
|||||||
|
|
||||||
.. note:: Longer term these keys will be managed in secure hardware devices.
|
.. note:: Longer term these keys will be managed in secure hardware devices.
|
||||||
|
|
||||||
|
:database: Database configuration:
|
||||||
|
|
||||||
|
:initDatabase: Boolean on whether to initialise the database or just validate the schema. Defaults to true.
|
||||||
|
:serverNameTablePrefix: Prefix string to apply to all the database tables. The default is no prefix.
|
||||||
|
:transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in
|
||||||
|
``java.sql.Connection``, but without the "TRANSACTION_" prefix. Defaults to REPEATABLE_READ.
|
||||||
|
|
||||||
:dataSourceProperties: This section is used to configure the jdbc connection and database driver used for the nodes persistence.
|
:dataSourceProperties: This section is used to configure the jdbc connection and database driver used for the nodes persistence.
|
||||||
Currently the defaults in ``/node/src/main/resources/reference.conf`` are as shown in the first example. This is currently
|
Currently the defaults in ``/node/src/main/resources/reference.conf`` are as shown in the first example. This is currently
|
||||||
the only configuration that has been tested, although in the future full support for other storage layers will be validated.
|
the only configuration that has been tested, although in the future full support for other storage layers will be validated.
|
||||||
|
@ -6,4 +6,5 @@ Corda networks
|
|||||||
|
|
||||||
setting-up-a-corda-network
|
setting-up-a-corda-network
|
||||||
permissioning
|
permissioning
|
||||||
versioning
|
network-map
|
||||||
|
versioning
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
Network Map
|
Network Map
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
The network map stores a collection of ``NodeInfo`` objects, each representing another node with which the node can interact.
|
||||||
|
There two sources from which a Corda node can retrieve ``NodeInfo`` objects:
|
||||||
|
|
||||||
|
1. the REST protocol with the network map service, which also provides a publishing API,
|
||||||
|
|
||||||
|
2. the ``additional-node-infos`` directory.
|
||||||
|
|
||||||
|
|
||||||
Protocol Design
|
Protocol Design
|
||||||
---------------
|
---------------
|
||||||
The node info publishing protocol:
|
The node info publishing protocol:
|
||||||
@ -35,4 +43,15 @@ Network Map service REST API:
|
|||||||
| GET | /api/network-map/parameters/{hash}| Retrieve ``NetworkParameters`` object with the same hash. |
|
| GET | /api/network-map/parameters/{hash}| Retrieve ``NetworkParameters`` object with the same hash. |
|
||||||
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
TODO: Access control of the network map will be added in the future.
|
TODO: Access control of the network map will be added in the future.
|
||||||
|
|
||||||
|
|
||||||
|
The ``additional-node-infos`` directory
|
||||||
|
---------------------------------------
|
||||||
|
Each Corda node reads, and continuously polls, the files contained in a directory named ``additional-node-infos`` inside the node base directory.
|
||||||
|
|
||||||
|
Nodes expect to find a serialized ``SignedData<NodeInfo>`` object, the same object which is sent to network map server.
|
||||||
|
|
||||||
|
Whenever a node starts it writes on disk a file containing its own ``NodeInfo``, this file is called ``nodeInfo-XXX`` where ``XXX`` is a long string.
|
||||||
|
|
||||||
|
Hence if an operator wants node A to see node B they can pick B's ``NodeInfo`` file from B base directory and drop it into A's ``additional-node-infos`` directory.
|
||||||
|
@ -1,104 +1,124 @@
|
|||||||
Network permissioning (Doorman)
|
Network Permissioning
|
||||||
===============================
|
=====================
|
||||||
|
|
||||||
The keystore located in ``<workspace>/certificates/sslkeystore.jks`` is required to connect to the Corda network securely.
|
.. contents::
|
||||||
In development mode (when ``devMode = true``, see ":doc:`corda-configuration-file`" for more information) a pre-configured
|
|
||||||
keystore will be used if the keystore does not exist. This is to ensure developers can get the nodes working as quickly
|
|
||||||
as possible.
|
|
||||||
|
|
||||||
However this is not secure for the real network. This documentation will explain the procedure of obtaining a signed
|
Corda networks are *permissioned*. To connect to a network, a node needs three keystores in its
|
||||||
certificate for TestNet.
|
``<workspace>/certificates/`` folder:
|
||||||
|
|
||||||
Initial Registration
|
* ``truststore.jks``, which stores trusted public keys and certificates (in our case, those of the network root CA)
|
||||||
--------------------
|
* ``nodekeystore.jks``, which stores the node’s identity keypairs and certificates
|
||||||
|
* ``sslkeystore.jks``, which stores the node’s TLS keypairs and certificates
|
||||||
|
|
||||||
The certificate signing request will be created based on node information obtained from the node configuration.
|
In development mode (i.e. when ``devMode = true``, see :doc:`corda-configuration-file` for more information),
|
||||||
The following information from the node configuration file is needed to generate the request.
|
pre-configured keystores are used if the required keystores do not exist. This ensures that developers can get the
|
||||||
|
nodes working as quickly as possible.
|
||||||
|
|
||||||
:myLegalName: Your company's legal name as an X.500 string. X.500 allows differentiation between entities with the same
|
However, these pre-configured keystores are not secure. Production deployments require a secure certificate authority.
|
||||||
name as the legal name needs to be unique on the network. If another node has already been permissioned with this
|
Most production deployments will use an existing certificate authority or construct one using software that will be
|
||||||
name then the permissioning server will automatically reject the request. The request will also be rejected if it
|
made available in the coming months. Until then, the documentation below can be used to create your own certificate
|
||||||
violates legal name rules, see `Legal Name Constraints`_ for more information.
|
authority.
|
||||||
|
|
||||||
:emailAddress: e.g. "admin@company.com"
|
Network structure
|
||||||
|
-----------------
|
||||||
|
A Corda network has three types of certificate authorities (CAs):
|
||||||
|
|
||||||
:compatibilityZoneURL: Corda compatibility zone network management service root URL.
|
* The **root network CA**
|
||||||
|
* The **intermediate network CA**
|
||||||
|
|
||||||
A new pair of private and public keys generated by the Corda node will be used to create the request.
|
* The intermediate network CA is used instead of the root network CA for day-to-day
|
||||||
|
key signing to reduce the risk of the root network CA's private key being compromised
|
||||||
|
|
||||||
The utility will submit the request to the doorman server and poll for a result periodically to retrieve the certificates.
|
* The **node CAs**
|
||||||
Once the request has been approved and the certificates downloaded from the server, the node will create the keystore and trust store using the certificates and the generated private key.
|
|
||||||
|
|
||||||
.. note:: You can exit the utility at any time if the approval process is taking longer than expected. The request process will resume on restart.
|
* Each node serves as its own CA in issuing the child certificates that it uses to sign its identity
|
||||||
|
keys, anonymous keys and TLS certificates
|
||||||
|
|
||||||
This process only is needed when the node connects to the network for the first time, or when the certificate expires.
|
Keypair and certificate formats
|
||||||
|
-------------------------------
|
||||||
|
You can use any standard key tools or Corda's ``X509Utilities`` (which uses Bouncy Castle) to create the required
|
||||||
|
public/private keypairs and certificates. The keypairs and certificates should obey the following restrictions:
|
||||||
|
|
||||||
Legal Name Constraints
|
* The certificates must follow the `X.509 standard <https://tools.ietf.org/html/rfc5280>`_
|
||||||
----------------------
|
|
||||||
The legal name is the unique identifier in the Corda network, so constraints have been set out to prevent encoding attacks and visual spoofing.
|
|
||||||
|
|
||||||
The legal name validator (see ``LegalNameValidator.kt``) is used to enforce rules on Corda's legal names, it is intended to be used by the network operator and Corda node during the node registration process.
|
* We recommend X.509 v3 for forward compatibility
|
||||||
It has two functions, a function to normalize legal names, and a function to validate legal names.
|
|
||||||
|
|
||||||
The normalize function performs the following transformations:
|
* The TLS certificates must follow the `TLS v1.2 standard <https://tools.ietf.org/html/rfc5246>`_
|
||||||
|
|
||||||
* Remove leading and trailing whitespaces.
|
Creating the root and intermediate network CAs
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
* Replace multiple whitespaces with a single space.
|
Creating the root network CA's keystore and truststore
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
* Normalize the string according to `NFKC normalization form <https://en.wikipedia.org/wiki/Unicode_equivalence#Normalization>`_.
|
1. Create a new keypair
|
||||||
|
|
||||||
The validation function will validate the input string using the following rules:
|
* This will be used as the root network CA's keypair
|
||||||
|
|
||||||
* No blacklisted words like "node", "server".
|
2. Create a self-signed certificate for the keypair. The basic constraints extension must be set to ``true``
|
||||||
|
|
||||||
* Restrict names to Latin scripts for now to avoid right-to-left issues, debugging issues when we can't pronounce names over the phone, and character confusability attacks.
|
* This will be used as the root network CA's certificate
|
||||||
|
|
||||||
* Should start with a capital letter.
|
3. Create a new keystore and store the root network CA's keypair and certificate in it for later use
|
||||||
|
|
||||||
* No commas or equals signs.
|
* This keystore will be used by the root network CA to sign the intermediate network CA's certificate
|
||||||
|
|
||||||
* No dollars or quote marks, although we may relax the quote mark constraint in future to handle Irish company names.
|
4. Create a new Java keystore named ``truststore.jks`` and store the root network CA's certificate in it using the
|
||||||
|
alias ``cordarootca``
|
||||||
|
|
||||||
Starting the Registration
|
* This keystore will be provisioned to the individual nodes later
|
||||||
-------------------------
|
|
||||||
|
|
||||||
You will need to specify the working directory of your Corda node using ``--base-dir`` flag. This is defaulted to current directory if left blank.
|
.. warning:: The root network CA's private key should be protected and kept safe.
|
||||||
You can also specify the location of ``node.conf`` with ``--config-file`` flag if it's not in the working directory.
|
|
||||||
|
|
||||||
**To start the registration**::
|
Creating the intermediate network CA's keystore
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
java -jar corda.jar --initial-registration --base-dir <<optional>> --config-file <<optional>>
|
1. Create a new keypair
|
||||||
|
|
||||||
A ``certificates`` folder containing the keystore and trust store will be created in the base directory when the process is completed.
|
* This will be used as the intermediate network CA's keypair
|
||||||
|
|
||||||
.. warning:: The keystore is protected by the keystore password from the node configuration file. The password should kept safe to protect the private key and certificate.
|
2. Obtain a certificate for the keypair signed with the root network CA key. The basic constraints extension must be
|
||||||
|
set to ``true``
|
||||||
|
|
||||||
|
* This will be used as the intermediate network CA's certificate
|
||||||
|
|
||||||
Protocol Design
|
3. Create a new keystore and store the intermediate network CA's keypair and certificate chain
|
||||||
---------------
|
(i.e. the intermediate network CA certificate *and* the root network CA certificate) in it for later use
|
||||||
.. note:: This section is intended for developers who want to implement their own doorman service.
|
|
||||||
|
|
||||||
The certificate signing protocol:
|
* This keystore will be used by the intermediate network CA to sign the nodes' identity certificates
|
||||||
|
|
||||||
* Generate a keypair, save it to disk.
|
Creating the node CA keystores and TLS keystores
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
* Generate a CSR using Bouncy Castle or the java crypto APIs containing myLegalName from the config file. We should also have an admin email address in the config file and CSR so we know who to email if anything goes wrong. Sign it with the private key.
|
Creating the node CA keystores
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
* HTTPS POST the CSR to the doorman. It creates the server-side records of this request, allocates an ID for it, and then sends back an HTTP redirect to another URL that contains that request ID (which should be sufficiently large that it's not predictable or brute forceable).
|
1. For each node, create a new keypair
|
||||||
|
|
||||||
* Store that URL to disk.
|
2. Obtain a certificate for the keypair signed with the intermediate network CA key. The basic constraints extension must be
|
||||||
|
set to ``true``
|
||||||
|
|
||||||
* Server goes into a slow polling loop, in which every 10 minutes or so it fetches the URL it was given in the redirect. Mostly it will get 204 No Content. Eventually it will get 200 OK and download the signed certificate in binary form, which it can then stash in its local keystore file.
|
3. Create a new Java keystore named ``nodekeystore.jks`` and store the keypair in it using the alias ``cordaclientca``
|
||||||
|
|
||||||
The initial registration process uses the following web api to communicate with the doorman service:
|
* The node will store this keystore locally to sign its identity keys and anonymous keys
|
||||||
|
|
||||||
+----------------+------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
Creating the node TLS keystores
|
||||||
| Request method | Path | Description |
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
+================+==============================+========================================================================================================================================================+
|
|
||||||
| POST | /api/certificate | Create new certificate request record and stored for further approval process, server will response with a request ID if the request has been accepted.|
|
|
||||||
+----------------+------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
||||||
| GET | /api/certificate/{requestId} | Retrieve certificates for requestId, the server will return HTTP 204 if request is not yet approved or HTTP 401 if it has been rejected. |
|
|
||||||
+----------------+------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
||||||
|
|
||||||
See ``NetworkRegistrationHelper`` and ``X509Utilities`` for examples of certificate signing request creation and certificate signing using Bouncy Castle.
|
1. For each node, create a new keypair
|
||||||
|
|
||||||
|
2. Create a certificate for the keypair signed with the node CA key. The basic constraints extension must be set to
|
||||||
|
``false``
|
||||||
|
|
||||||
|
3. Create a new Java keystore named ``sslkeystore.jks`` and store the key and certificates in it using the alias
|
||||||
|
``cordaclienttls``
|
||||||
|
|
||||||
|
* The node will store this keystore locally to sign its TLS certificates
|
||||||
|
|
||||||
|
Installing the certificates on the nodes
|
||||||
|
----------------------------------------
|
||||||
|
For each node, copy the following files to the node's certificate directory (``<workspace>/certificates/``):
|
||||||
|
|
||||||
|
1. The node's ``nodekeystore.jks`` keystore
|
||||||
|
2. The node's ``sslkeystore.jks`` keystore
|
||||||
|
3. The root network CA's ``truststore.jks`` keystore
|
||||||
|
@ -35,9 +35,9 @@ Shell can also be accessible via SSH. By default SSH server is *disabled*. To en
|
|||||||
|
|
||||||
Authentication and authorization
|
Authentication and authorization
|
||||||
--------------------------------
|
--------------------------------
|
||||||
SSH require user to login first - using the same users as RPC system. In fact, shell serves as a proxy to RPC and communicates
|
SSH requires users to login first - using the same users as RPC system. In fact, the shell serves as a proxy to RPC and communicates
|
||||||
with node using RPC calls. This also means that RPC permissions are enforced. No permissions are required to allow the connection
|
with the node using RPC calls. This also means that RPC permissions are enforced. No permissions are required to allow the connection
|
||||||
and login in.
|
and log in.
|
||||||
Watching flows (``flow watch``) requires ``InvokeRpc.stateMachinesFeed`` while starting flows requires
|
Watching flows (``flow watch``) requires ``InvokeRpc.stateMachinesFeed`` while starting flows requires
|
||||||
``InvokeRpc.startTrackedFlowDynamic`` and ``InvokeRpc.registeredFlows`` in addition to a permission for a particular flow.
|
``InvokeRpc.startTrackedFlowDynamic`` and ``InvokeRpc.registeredFlows`` in addition to a permission for a particular flow.
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ errors.
|
|||||||
Connecting
|
Connecting
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Linux and MacOS computers usually come with SSH client preinstalled. On Windows it usually require extra download.
|
Linux and MacOS computers usually come with SSH client preinstalled. On Windows it usually requires extra download.
|
||||||
Usual connection syntax is ``ssh user@host -p 2222`` - where ``user`` is a RPC username, and ``-p`` specifies a port parameters -
|
Usual connection syntax is ``ssh user@host -p 2222`` - where ``user`` is a RPC username, and ``-p`` specifies a port parameters -
|
||||||
it's the same as setup in ``node.conf`` file. ``host`` should point to a node hostname, usually ``localhost`` if connecting and
|
it's the same as setup in ``node.conf`` file. ``host`` should point to a node hostname, usually ``localhost`` if connecting and
|
||||||
running node on the same computer. Password will be asked after establishing connection.
|
running node on the same computer. Password will be asked after establishing connection.
|
||||||
|
@ -35,14 +35,17 @@ We start with the empty ledger:
|
|||||||
|
|
||||||
.. sourcecode:: java
|
.. sourcecode:: java
|
||||||
|
|
||||||
import static net.corda.core.testing.JavaTestHelpers.*;
|
import org.junit.Test;
|
||||||
import static net.corda.core.contracts.JavaTestHelpers.*;
|
|
||||||
|
|
||||||
@Test
|
import static net.corda.testing.NodeTestUtils.ledger;
|
||||||
public void emptyLedger() {
|
|
||||||
ledger(l -> {
|
public class CommercialPaperTest {
|
||||||
return Unit.INSTANCE; // We need to return this explicitly
|
@Test
|
||||||
});
|
public void emptyLedger() {
|
||||||
|
ledger(l -> {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
The DSL keyword ``ledger`` takes a closure that can build up several transactions and may verify their overall
|
The DSL keyword ``ledger`` takes a closure that can build up several transactions and may verify their overall
|
||||||
|
@ -131,7 +131,13 @@ private fun Config.defaultToOldPath(property: KProperty<*>): String {
|
|||||||
|
|
||||||
private fun parseEnum(enumType: Class<*>, name: String): Enum<*> = enumBridge<Proxy.Type>(uncheckedCast(enumType), name) // Any enum will do
|
private fun parseEnum(enumType: Class<*>, name: String): Enum<*> = enumBridge<Proxy.Type>(uncheckedCast(enumType), name) // Any enum will do
|
||||||
|
|
||||||
private fun <T : Enum<T>> enumBridge(clazz: Class<T>, name: String): T = java.lang.Enum.valueOf(clazz, name)
|
private fun <T : Enum<T>> enumBridge(clazz: Class<T>, name: String): T {
|
||||||
|
try {
|
||||||
|
return java.lang.Enum.valueOf(clazz, name)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
throw IllegalArgumentException("$name is not one of { ${clazz.enumConstants.joinToString()} }")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the receiver object into a [Config]. This does the inverse action of [parseAs].
|
* Convert the receiver object into a [Config]. This does the inverse action of [parseAs].
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.node.utilities
|
package net.corda.nodeapi.internal.crypto
|
||||||
|
|
||||||
import net.corda.core.crypto.SignatureScheme
|
import net.corda.core.crypto.SignatureScheme
|
||||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
|
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
|
@ -1,9 +1,8 @@
|
|||||||
@file:JvmName("KeyStoreUtilities")
|
@file:JvmName("KeyStoreUtilities")
|
||||||
|
|
||||||
package net.corda.node.utilities
|
package net.corda.nodeapi.internal.crypto
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -11,9 +10,7 @@ import java.io.InputStream
|
|||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.*
|
import java.security.*
|
||||||
import java.security.cert.CertPath
|
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
const val KEYSTORE_TYPE = "JKS"
|
const val KEYSTORE_TYPE = "JKS"
|
||||||
@ -169,44 +166,3 @@ fun KeyStore.getSupportedKey(alias: String, keyPassword: String): PrivateKey {
|
|||||||
val key = getKey(alias, keyPass) as PrivateKey
|
val key = getKey(alias, keyPass) as PrivateKey
|
||||||
return Crypto.toSupportedPrivateKey(key)
|
return Crypto.toSupportedPrivateKey(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
class KeyStoreWrapper(private val storePath: Path, private val storePassword: String) {
|
|
||||||
private val keyStore = storePath.read { loadKeyStore(it, storePassword) }
|
|
||||||
|
|
||||||
private fun createCertificate(serviceName: CordaX500Name, pubKey: PublicKey): CertPath {
|
|
||||||
val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
|
||||||
// Assume key password = store password.
|
|
||||||
val clientCA = certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
|
||||||
// Create new keys and store in keystore.
|
|
||||||
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey)
|
|
||||||
val certPath = CertificateFactory.getInstance("X509").generateCertPath(listOf(cert.cert) + clientCertPath)
|
|
||||||
require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" }
|
|
||||||
// TODO: X509Utilities.validateCertificateChain()
|
|
||||||
return certPath
|
|
||||||
}
|
|
||||||
|
|
||||||
fun signAndSaveNewKeyPair(serviceName: CordaX500Name, privateKeyAlias: String, keyPair: KeyPair) {
|
|
||||||
val certPath = createCertificate(serviceName, keyPair.public)
|
|
||||||
// Assume key password = store password.
|
|
||||||
keyStore.addOrReplaceKey(privateKeyAlias, keyPair.private, storePassword.toCharArray(), certPath.certificates.toTypedArray())
|
|
||||||
keyStore.save(storePath, storePassword)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun savePublicKey(serviceName: CordaX500Name, pubKeyAlias: String, pubKey: PublicKey) {
|
|
||||||
val certPath = createCertificate(serviceName, pubKey)
|
|
||||||
// Assume key password = store password.
|
|
||||||
keyStore.addOrReplaceCertificate(pubKeyAlias, certPath.certificates.first())
|
|
||||||
keyStore.save(storePath, storePassword)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate methods to keystore. Sadly keystore doesn't have an interface.
|
|
||||||
fun containsAlias(alias: String) = keyStore.containsAlias(alias)
|
|
||||||
|
|
||||||
fun getX509Certificate(alias: String) = keyStore.getX509Certificate(alias)
|
|
||||||
|
|
||||||
fun getCertificateChain(alias: String): Array<out Certificate> = keyStore.getCertificateChain(alias)
|
|
||||||
|
|
||||||
fun getCertificate(alias: String): Certificate = keyStore.getCertificate(alias)
|
|
||||||
|
|
||||||
fun certificateAndKeyPair(alias: String): CertificateAndKeyPair = keyStore.getCertificateAndKeyPair(alias, storePassword)
|
|
||||||
}
|
|
@ -0,0 +1,52 @@
|
|||||||
|
package net.corda.nodeapi.internal.crypto
|
||||||
|
|
||||||
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.internal.cert
|
||||||
|
import net.corda.core.internal.read
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.security.KeyPair
|
||||||
|
import java.security.PublicKey
|
||||||
|
import java.security.cert.CertPath
|
||||||
|
import java.security.cert.Certificate
|
||||||
|
import java.security.cert.CertificateFactory
|
||||||
|
|
||||||
|
class KeyStoreWrapper(private val storePath: Path, private val storePassword: String) {
|
||||||
|
private val keyStore = storePath.read { loadKeyStore(it, storePassword) }
|
||||||
|
|
||||||
|
private fun createCertificate(serviceName: CordaX500Name, pubKey: PublicKey): CertPath {
|
||||||
|
val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||||
|
// Assume key password = store password.
|
||||||
|
val clientCA = certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||||
|
// Create new keys and store in keystore.
|
||||||
|
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey)
|
||||||
|
val certPath = X509CertificateFactory().delegate.generateCertPath(listOf(cert.cert) + clientCertPath)
|
||||||
|
require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" }
|
||||||
|
// TODO: X509Utilities.validateCertificateChain()
|
||||||
|
return certPath
|
||||||
|
}
|
||||||
|
|
||||||
|
fun signAndSaveNewKeyPair(serviceName: CordaX500Name, privateKeyAlias: String, keyPair: KeyPair) {
|
||||||
|
val certPath = createCertificate(serviceName, keyPair.public)
|
||||||
|
// Assume key password = store password.
|
||||||
|
keyStore.addOrReplaceKey(privateKeyAlias, keyPair.private, storePassword.toCharArray(), certPath.certificates.toTypedArray())
|
||||||
|
keyStore.save(storePath, storePassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun savePublicKey(serviceName: CordaX500Name, pubKeyAlias: String, pubKey: PublicKey) {
|
||||||
|
val certPath = createCertificate(serviceName, pubKey)
|
||||||
|
// Assume key password = store password.
|
||||||
|
keyStore.addOrReplaceCertificate(pubKeyAlias, certPath.certificates.first())
|
||||||
|
keyStore.save(storePath, storePassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate methods to keystore. Sadly keystore doesn't have an interface.
|
||||||
|
fun containsAlias(alias: String) = keyStore.containsAlias(alias)
|
||||||
|
|
||||||
|
fun getX509Certificate(alias: String) = keyStore.getX509Certificate(alias)
|
||||||
|
|
||||||
|
fun getCertificateChain(alias: String): Array<out Certificate> = keyStore.getCertificateChain(alias)
|
||||||
|
|
||||||
|
fun getCertificate(alias: String): Certificate = keyStore.getCertificate(alias)
|
||||||
|
|
||||||
|
fun certificateAndKeyPair(alias: String): CertificateAndKeyPair = keyStore.getCertificateAndKeyPair(alias, storePassword)
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
package net.corda.node.utilities
|
package net.corda.nodeapi.internal.crypto
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SignatureScheme
|
import net.corda.core.crypto.SignatureScheme
|
||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.internal.read
|
||||||
|
import net.corda.core.internal.write
|
||||||
import net.corda.core.internal.x500Name
|
import net.corda.core.internal.x500Name
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
import net.corda.core.utilities.millis
|
import net.corda.core.utilities.millis
|
||||||
@ -25,10 +27,10 @@ import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder
|
|||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder
|
||||||
import org.bouncycastle.util.io.pem.PemReader
|
import org.bouncycastle.util.io.pem.PemReader
|
||||||
import java.io.FileReader
|
|
||||||
import java.io.FileWriter
|
import java.io.FileWriter
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -52,6 +54,7 @@ object X509Utilities {
|
|||||||
const val CORDA_CLIENT_CA_CN = "Corda Client CA Certificate"
|
const val CORDA_CLIENT_CA_CN = "Corda Client CA Certificate"
|
||||||
|
|
||||||
private val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
|
private val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to return the latest out of an instant and an optional date.
|
* Helper function to return the latest out of an instant and an optional date.
|
||||||
*/
|
*/
|
||||||
@ -89,7 +92,9 @@ object X509Utilities {
|
|||||||
* Create a de novo root self-signed X509 v3 CA cert.
|
* Create a de novo root self-signed X509 v3 CA cert.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createSelfSignedCACertificate(subject: CordaX500Name, keyPair: KeyPair, validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder {
|
fun createSelfSignedCACertificate(subject: CordaX500Name,
|
||||||
|
keyPair: KeyPair,
|
||||||
|
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder {
|
||||||
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
|
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
|
||||||
return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window)
|
return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window)
|
||||||
}
|
}
|
||||||
@ -114,8 +119,9 @@ object X509Utilities {
|
|||||||
subject: CordaX500Name,
|
subject: CordaX500Name,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
||||||
nameConstraints: NameConstraints? = null): X509CertificateHolder
|
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
||||||
= createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
return createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
|
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
|
||||||
@ -145,10 +151,9 @@ object X509Utilities {
|
|||||||
@Throws(CertPathValidatorException::class)
|
@Throws(CertPathValidatorException::class)
|
||||||
fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: Certificate) {
|
fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: Certificate) {
|
||||||
require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" }
|
require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" }
|
||||||
val certFactory = CertificateFactory.getInstance("X509")
|
|
||||||
val params = PKIXParameters(setOf(TrustAnchor(trustedRoot, null)))
|
val params = PKIXParameters(setOf(TrustAnchor(trustedRoot, null)))
|
||||||
params.isRevocationEnabled = false
|
params.isRevocationEnabled = false
|
||||||
val certPath = certFactory.generateCertPath(certificates.toList())
|
val certPath = X509CertificateFactory().delegate.generateCertPath(certificates.toList())
|
||||||
val pathValidator = CertPathValidator.getInstance("PKIX")
|
val pathValidator = CertPathValidator.getInstance("PKIX")
|
||||||
pathValidator.validate(certPath, params)
|
pathValidator.validate(certPath, params)
|
||||||
}
|
}
|
||||||
@ -156,30 +161,29 @@ object X509Utilities {
|
|||||||
/**
|
/**
|
||||||
* Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection.
|
* Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection.
|
||||||
* @param x509Certificate certificate to save.
|
* @param x509Certificate certificate to save.
|
||||||
* @param filename Target filename.
|
* @param file Target file.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun saveCertificateAsPEMFile(x509Certificate: X509CertificateHolder, filename: Path) {
|
fun saveCertificateAsPEMFile(x509Certificate: X509CertificateHolder, file: Path) {
|
||||||
FileWriter(filename.toFile()).use {
|
JcaPEMWriter(file.toFile().writer()).use {
|
||||||
JcaPEMWriter(it).use {
|
it.writeObject(x509Certificate)
|
||||||
it.writeObject(x509Certificate)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to load back a .pem/.cer format file copy of a certificate.
|
* Helper method to load back a .pem/.cer format file copy of a certificate.
|
||||||
* @param filename Source filename.
|
* @param file Source file.
|
||||||
* @return The X509Certificate that was encoded in the file.
|
* @return The X509Certificate that was encoded in the file.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun loadCertificateFromPEMFile(filename: Path): X509CertificateHolder {
|
fun loadCertificateFromPEMFile(file: Path): X509CertificateHolder {
|
||||||
val reader = PemReader(FileReader(filename.toFile()))
|
val cert = file.read {
|
||||||
val pemObject = reader.readPemObject()
|
val reader = PemReader(it.reader())
|
||||||
val cert = X509CertificateHolder(pemObject.content)
|
val pemObject = reader.readPemObject()
|
||||||
return cert.apply {
|
X509CertificateHolder(pemObject.content)
|
||||||
isValidOn(Date())
|
|
||||||
}
|
}
|
||||||
|
cert.isValidOn(Date())
|
||||||
|
return cert
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -243,13 +247,13 @@ object X509Utilities {
|
|||||||
* @param validityWindow the time period the certificate is valid for.
|
* @param validityWindow the time period the certificate is valid for.
|
||||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||||
*/
|
*/
|
||||||
internal fun createCertificate(certificateType: CertificateType,
|
fun createCertificate(certificateType: CertificateType,
|
||||||
issuer: X500Name,
|
issuer: X500Name,
|
||||||
issuerSigner: ContentSigner,
|
issuerSigner: ContentSigner,
|
||||||
subject: CordaX500Name,
|
subject: CordaX500Name,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Date, Date>,
|
validityWindow: Pair<Date, Date>,
|
||||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
||||||
val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
||||||
return builder.build(issuerSigner).apply {
|
return builder.build(issuerSigner).apply {
|
||||||
require(isValidOn(Date()))
|
require(isValidOn(Date()))
|
||||||
@ -266,11 +270,13 @@ object X509Utilities {
|
|||||||
* @param validityWindow the time period the certificate is valid for.
|
* @param validityWindow the time period the certificate is valid for.
|
||||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||||
*/
|
*/
|
||||||
internal fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerKeyPair: KeyPair,
|
fun createCertificate(certificateType: CertificateType,
|
||||||
subject: X500Name, subjectPublicKey: PublicKey,
|
issuer: X500Name,
|
||||||
validityWindow: Pair<Date, Date>,
|
issuerKeyPair: KeyPair,
|
||||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
subject: X500Name,
|
||||||
|
subjectPublicKey: PublicKey,
|
||||||
|
validityWindow: Pair<Date, Date>,
|
||||||
|
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
||||||
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
|
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
|
||||||
val provider = Crypto.findProvider(signatureScheme.providerName)
|
val provider = Crypto.findProvider(signatureScheme.providerName)
|
||||||
val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||||
@ -285,28 +291,71 @@ object X509Utilities {
|
|||||||
/**
|
/**
|
||||||
* Create certificate signing request using provided information.
|
* Create certificate signing request using provided information.
|
||||||
*/
|
*/
|
||||||
internal fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest {
|
private fun createCertificateSigningRequest(subject: CordaX500Name,
|
||||||
|
email: String,
|
||||||
|
keyPair: KeyPair,
|
||||||
|
signatureScheme: SignatureScheme): PKCS10CertificationRequest {
|
||||||
val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
|
val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
|
||||||
return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
|
return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair) = createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
|
fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
|
||||||
|
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
class CertificateStream(val input: InputStream) {
|
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
|
||||||
private val certificateFactory = CertificateFactory.getInstance("X.509")
|
* so assume this class is not.
|
||||||
|
*/
|
||||||
fun nextCertificate(): X509Certificate = certificateFactory.generateCertificate(input) as X509Certificate
|
class X509CertificateFactory {
|
||||||
|
val delegate: CertificateFactory = CertificateFactory.getInstance("X.509")
|
||||||
|
fun generateCertificate(input: InputStream): X509Certificate {
|
||||||
|
return delegate.generateCertificate(input) as X509Certificate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean) {
|
enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean) {
|
||||||
ROOT_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true),
|
ROOT_CA(
|
||||||
INTERMEDIATE_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true),
|
KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign),
|
||||||
CLIENT_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true),
|
KeyPurposeId.id_kp_serverAuth,
|
||||||
TLS(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyEncipherment or KeyUsage.keyAgreement), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = false),
|
KeyPurposeId.id_kp_clientAuth,
|
||||||
|
KeyPurposeId.anyExtendedKeyUsage,
|
||||||
|
isCA = true
|
||||||
|
),
|
||||||
|
|
||||||
|
INTERMEDIATE_CA(
|
||||||
|
KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign),
|
||||||
|
KeyPurposeId.id_kp_serverAuth,
|
||||||
|
KeyPurposeId.id_kp_clientAuth,
|
||||||
|
KeyPurposeId.anyExtendedKeyUsage,
|
||||||
|
isCA = true
|
||||||
|
),
|
||||||
|
|
||||||
|
CLIENT_CA(
|
||||||
|
KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign),
|
||||||
|
KeyPurposeId.id_kp_serverAuth,
|
||||||
|
KeyPurposeId.id_kp_clientAuth,
|
||||||
|
KeyPurposeId.anyExtendedKeyUsage,
|
||||||
|
isCA = true
|
||||||
|
),
|
||||||
|
|
||||||
|
TLS(
|
||||||
|
KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyEncipherment or KeyUsage.keyAgreement),
|
||||||
|
KeyPurposeId.id_kp_serverAuth,
|
||||||
|
KeyPurposeId.id_kp_clientAuth,
|
||||||
|
KeyPurposeId.anyExtendedKeyUsage,
|
||||||
|
isCA = false
|
||||||
|
),
|
||||||
|
|
||||||
// TODO: Identity certs should have only limited depth (i.e. 1) CA signing capability, with tight name constraints
|
// TODO: Identity certs should have only limited depth (i.e. 1) CA signing capability, with tight name constraints
|
||||||
IDENTITY(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true)
|
IDENTITY(
|
||||||
|
KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign),
|
||||||
|
KeyPurposeId.id_kp_serverAuth,
|
||||||
|
KeyPurposeId.id_kp_clientAuth,
|
||||||
|
KeyPurposeId.anyExtendedKeyUsage,
|
||||||
|
isCA = true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair)
|
data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair)
|
@ -1,9 +1,9 @@
|
|||||||
package net.corda.nodeapi.internal.serialization.amqp.custom
|
package net.corda.nodeapi.internal.serialization.amqp.custom
|
||||||
|
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.*
|
import net.corda.nodeapi.internal.serialization.amqp.*
|
||||||
import org.apache.qpid.proton.codec.Data
|
import org.apache.qpid.proton.codec.Data
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
object X509CertificateSerializer : CustomSerializer.Implements<X509Certificate>(X509Certificate::class.java) {
|
object X509CertificateSerializer : CustomSerializer.Implements<X509Certificate>(X509Certificate::class.java) {
|
||||||
@ -22,6 +22,6 @@ object X509CertificateSerializer : CustomSerializer.Implements<X509Certificate>(
|
|||||||
|
|
||||||
override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): X509Certificate {
|
override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): X509Certificate {
|
||||||
val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray
|
val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray
|
||||||
return CertificateFactory.getInstance("X.509").generateCertificate(bits.inputStream()) as X509Certificate
|
return X509CertificateFactory().generateCertificate(bits.inputStream())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import net.corda.core.serialization.SerializedBytes
|
|||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
import net.corda.core.toObservable
|
import net.corda.core.toObservable
|
||||||
import net.corda.core.transactions.*
|
import net.corda.core.transactions.*
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.serialization.CordaClassResolver
|
import net.corda.nodeapi.internal.serialization.CordaClassResolver
|
||||||
import net.corda.nodeapi.internal.serialization.serializationContextKey
|
import net.corda.nodeapi.internal.serialization.serializationContextKey
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
@ -486,8 +487,7 @@ object CertPathSerializer : Serializer<CertPath>() {
|
|||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
object X509CertificateSerializer : Serializer<X509Certificate>() {
|
object X509CertificateSerializer : Serializer<X509Certificate>() {
|
||||||
override fun read(kryo: Kryo, input: Input, type: Class<X509Certificate>): X509Certificate {
|
override fun read(kryo: Kryo, input: Input, type: Class<X509Certificate>): X509Certificate {
|
||||||
val factory = CertificateFactory.getInstance("X.509")
|
return X509CertificateFactory().generateCertificate(input.readBytesWithLength().inputStream())
|
||||||
return factory.generateCertificate(input.readBytesWithLength().inputStream()) as X509Certificate
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun write(kryo: Kryo, output: Output, obj: X509Certificate) {
|
override fun write(kryo: Kryo, output: Output, obj: X509Certificate) {
|
||||||
|
@ -8,6 +8,7 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -47,6 +48,14 @@ class ConfigParsingTest {
|
|||||||
testPropertyType<EnumData, EnumListData, TestEnum>(TestEnum.Value2, TestEnum.Value1, valuesToString = true)
|
testPropertyType<EnumData, EnumListData, TestEnum>(TestEnum.Value2, TestEnum.Value1, valuesToString = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `unknown Enum`() {
|
||||||
|
val config = config("value" to "UnknownValue")
|
||||||
|
assertThatThrownBy { config.parseAs<EnumData>() }
|
||||||
|
.hasMessageContaining(TestEnum.Value1.name)
|
||||||
|
.hasMessageContaining(TestEnum.Value2.name)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `LocalDate`() {
|
fun `LocalDate`() {
|
||||||
testPropertyType<LocalDateData, LocalDateListData, LocalDate>(LocalDate.now(), LocalDate.now().plusDays(1), valuesToString = true)
|
testPropertyType<LocalDateData, LocalDateListData, LocalDate>(LocalDate.now(), LocalDate.now().plusDays(1), valuesToString = true)
|
||||||
|
@ -139,8 +139,8 @@ class SSHServerTest {
|
|||||||
|
|
||||||
val response = String(Streams.readAll(channel.inputStream))
|
val response = String(Streams.readAll(channel.inputStream))
|
||||||
|
|
||||||
//There are ANSI control characters involved, so we want to avoid direct byte to byte matching
|
// There are ANSI control characters involved, so we want to avoid direct byte to byte matching.
|
||||||
assertThat(response.lines()).filteredOn( { it.contains("✓") && it.contains("Done")}).hasSize(1)
|
assertThat(response.lines()).filteredOn( { it.contains("Done")}).hasSize(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@ package net.corda.services.messaging
|
|||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.node.utilities.*
|
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
|
||||||
import net.corda.nodeapi.RPCApi
|
import net.corda.nodeapi.RPCApi
|
||||||
import net.corda.nodeapi.config.SSLConfiguration
|
import net.corda.nodeapi.config.SSLConfiguration
|
||||||
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.testing.MEGA_CORP
|
import net.corda.testing.MEGA_CORP
|
||||||
import net.corda.testing.MINI_CORP
|
import net.corda.testing.MINI_CORP
|
||||||
import net.corda.testing.messaging.SimpleMQClient
|
import net.corda.testing.messaging.SimpleMQClient
|
||||||
|
@ -4,7 +4,7 @@ package net.corda.node.shell;
|
|||||||
|
|
||||||
import net.corda.core.messaging.CordaRPCOps;
|
import net.corda.core.messaging.CordaRPCOps;
|
||||||
import net.corda.node.utilities.ANSIProgressRenderer;
|
import net.corda.node.utilities.ANSIProgressRenderer;
|
||||||
import net.corda.node.utilities.CRaSHNSIProgressRenderer;
|
import net.corda.node.utilities.CRaSHANSIProgressRenderer;
|
||||||
import org.crsh.cli.*;
|
import org.crsh.cli.*;
|
||||||
import org.crsh.command.*;
|
import org.crsh.command.*;
|
||||||
import org.crsh.text.*;
|
import org.crsh.text.*;
|
||||||
@ -12,7 +12,6 @@ import org.crsh.text.ui.TableElement;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static net.corda.node.services.messaging.RPCServerKt.CURRENT_RPC_CONTEXT;
|
|
||||||
import static net.corda.node.shell.InteractiveShell.*;
|
import static net.corda.node.shell.InteractiveShell.*;
|
||||||
|
|
||||||
@Man(
|
@Man(
|
||||||
@ -49,7 +48,7 @@ public class FlowShellCommand extends InteractiveShellCommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String inp = input == null ? "" : String.join(" ", input).trim();
|
String inp = input == null ? "" : String.join(" ", input).trim();
|
||||||
runFlowByNameFragment(name, inp, out, rpcOps, ansiProgressRenderer != null ? ansiProgressRenderer : new CRaSHNSIProgressRenderer(out) );
|
runFlowByNameFragment(name, inp, out, rpcOps, ansiProgressRenderer != null ? ansiProgressRenderer : new CRaSHANSIProgressRenderer(out) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command
|
@Command
|
||||||
|
@ -30,7 +30,7 @@ public class RunShellCommand extends InteractiveShellCommand {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return InteractiveShell.runRPCFromString(command, out, context);
|
return InteractiveShell.runRPCFromString(command, out, context, ops());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void emitHelp(InvocationContext<Map> context, StringToMethodCallParser<CordaRPCOps> parser) {
|
private void emitHelp(InvocationContext<Map> context, StringToMethodCallParser<CordaRPCOps> parser) {
|
||||||
|
@ -3,7 +3,7 @@ package net.corda.node.shell;
|
|||||||
// A simple forwarder to the "flow start" command, for easier typing.
|
// A simple forwarder to the "flow start" command, for easier typing.
|
||||||
|
|
||||||
import net.corda.node.utilities.ANSIProgressRenderer;
|
import net.corda.node.utilities.ANSIProgressRenderer;
|
||||||
import net.corda.node.utilities.CRaSHNSIProgressRenderer;
|
import net.corda.node.utilities.CRaSHANSIProgressRenderer;
|
||||||
import org.crsh.cli.*;
|
import org.crsh.cli.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -14,6 +14,6 @@ public class StartShellCommand extends InteractiveShellCommand {
|
|||||||
public void main(@Usage("The class name of the flow to run, or an unambiguous substring") @Argument String name,
|
public void main(@Usage("The class name of the flow to run, or an unambiguous substring") @Argument String name,
|
||||||
@Usage("The data to pass as input") @Argument(unquote = false) List<String> input) {
|
@Usage("The data to pass as input") @Argument(unquote = false) List<String> input) {
|
||||||
ANSIProgressRenderer ansiProgressRenderer = ansiProgressRenderer();
|
ANSIProgressRenderer ansiProgressRenderer = ansiProgressRenderer();
|
||||||
FlowShellCommand.startFlow(name, input, out, ops(), ansiProgressRenderer != null ? ansiProgressRenderer : new CRaSHNSIProgressRenderer(out));
|
FlowShellCommand.startFlow(name, input, out, ops(), ansiProgressRenderer != null ? ansiProgressRenderer : new CRaSHANSIProgressRenderer(out));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,10 @@ import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
|
|||||||
import net.corda.node.services.vault.NodeVaultService
|
import net.corda.node.services.vault.NodeVaultService
|
||||||
import net.corda.node.services.vault.VaultSoftLockManager
|
import net.corda.node.services.vault.VaultSoftLockManager
|
||||||
import net.corda.node.shell.InteractiveShell
|
import net.corda.node.shell.InteractiveShell
|
||||||
import net.corda.node.utilities.*
|
import net.corda.node.utilities.AffinityExecutor
|
||||||
|
import net.corda.node.utilities.CordaPersistence
|
||||||
|
import net.corda.node.utilities.configureDatabase
|
||||||
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -70,7 +73,6 @@ import java.lang.reflect.InvocationTargetException
|
|||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.KeyStoreException
|
import java.security.KeyStoreException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
@ -189,11 +191,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
val (keyPairs, info) = initNodeInfo()
|
val (keyPairs, info) = initNodeInfo()
|
||||||
readNetworkParameters()
|
readNetworkParameters()
|
||||||
val schemaService = NodeSchemaService(cordappLoader)
|
val schemaService = NodeSchemaService(cordappLoader)
|
||||||
|
val identityService = makeIdentityService(info)
|
||||||
// Do all of this in a database transaction so anything that might need a connection has one.
|
// Do all of this in a database transaction so anything that might need a connection has one.
|
||||||
val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService) { database ->
|
val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database ->
|
||||||
|
identityService.loadIdentities(info.legalIdentitiesAndCerts)
|
||||||
val transactionStorage = makeTransactionStorage(database)
|
val transactionStorage = makeTransactionStorage(database)
|
||||||
val stateLoader = StateLoaderImpl(transactionStorage)
|
val stateLoader = StateLoaderImpl(transactionStorage)
|
||||||
val nodeServices = makeServices(keyPairs, schemaService, transactionStorage, stateLoader, database, info)
|
val nodeServices = makeServices(keyPairs, schemaService, transactionStorage, stateLoader, database, info, identityService)
|
||||||
val notaryService = makeNotaryService(nodeServices, database)
|
val notaryService = makeNotaryService(nodeServices, database)
|
||||||
smm = makeStateMachineManager(database)
|
smm = makeStateMachineManager(database)
|
||||||
val flowStarter = FlowStarterImpl(serverThread, smm)
|
val flowStarter = FlowStarterImpl(serverThread, smm)
|
||||||
@ -502,12 +506,11 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
* Builds node internal, advertised, and plugin services.
|
* Builds node internal, advertised, and plugin services.
|
||||||
* Returns a list of tokenizable services to be added to the serialisation context.
|
* Returns a list of tokenizable services to be added to the serialisation context.
|
||||||
*/
|
*/
|
||||||
private fun makeServices(keyPairs: Set<KeyPair>, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, stateLoader: StateLoader, database: CordaPersistence, info: NodeInfo): MutableList<Any> {
|
private fun makeServices(keyPairs: Set<KeyPair>, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, stateLoader: StateLoader, database: CordaPersistence, info: NodeInfo, identityService: IdentityService): MutableList<Any> {
|
||||||
checkpointStorage = DBCheckpointStorage()
|
checkpointStorage = DBCheckpointStorage()
|
||||||
val metrics = MetricRegistry()
|
val metrics = MetricRegistry()
|
||||||
attachments = NodeAttachmentService(metrics)
|
attachments = NodeAttachmentService(metrics)
|
||||||
val cordappProvider = CordappProviderImpl(cordappLoader, attachments)
|
val cordappProvider = CordappProviderImpl(cordappLoader, attachments)
|
||||||
val identityService = makeIdentityService(info)
|
|
||||||
val keyManagementService = makeKeyManagementService(identityService, keyPairs)
|
val keyManagementService = makeKeyManagementService(identityService, keyPairs)
|
||||||
_services = ServiceHubInternalImpl(
|
_services = ServiceHubInternalImpl(
|
||||||
identityService,
|
identityService,
|
||||||
@ -561,10 +564,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
// Specific class so that MockNode can catch it.
|
// Specific class so that MockNode can catch it.
|
||||||
class DatabaseConfigurationException(msg: String) : CordaException(msg)
|
class DatabaseConfigurationException(msg: String) : CordaException(msg)
|
||||||
|
|
||||||
protected open fun <T> initialiseDatabasePersistence(schemaService: SchemaService, insideTransaction: (CordaPersistence) -> T): T {
|
protected open fun <T> initialiseDatabasePersistence(schemaService: SchemaService, identityService: IdentityService, insideTransaction: (CordaPersistence) -> T): T {
|
||||||
val props = configuration.dataSourceProperties
|
val props = configuration.dataSourceProperties
|
||||||
if (props.isNotEmpty()) {
|
if (props.isNotEmpty()) {
|
||||||
val database = configureDatabase(props, configuration.database, { _services.identityService }, schemaService)
|
val database = configureDatabase(props, configuration.database, identityService, schemaService)
|
||||||
// Now log the vendor string as this will also cause a connection to be tested eagerly.
|
// Now log the vendor string as this will also cause a connection to be tested eagerly.
|
||||||
database.transaction {
|
database.transaction {
|
||||||
log.info("Connected to ${database.dataSource.connection.metaData.databaseProductName} database.")
|
log.info("Connected to ${database.dataSource.connection.metaData.databaseProductName} database.")
|
||||||
@ -631,13 +634,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeIdentityService(info: NodeInfo): IdentityService {
|
private fun makeIdentityService(info: NodeInfo): PersistentIdentityService {
|
||||||
val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword)
|
val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword)
|
||||||
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||||
val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
val clientCa = caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
val clientCa = caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||||
val caCertificates = arrayOf(info.legalIdentitiesAndCerts[0].certificate, clientCa.certificate.cert)
|
val caCertificates = arrayOf(info.legalIdentitiesAndCerts[0].certificate, clientCa.certificate.cert)
|
||||||
return PersistentIdentityService(info.legalIdentitiesAndCerts, trustRoot = trustRoot, caCertificates = *caCertificates)
|
return PersistentIdentityService(trustRoot, *caCertificates)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun makeTransactionVerifierService(): TransactionVerifierService
|
protected abstract fun makeTransactionVerifierService(): TransactionVerifierService
|
||||||
@ -710,7 +713,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
throw ConfigurationException("The name '$singleName' for $id doesn't match what's in the key store: $subject")
|
throw ConfigurationException("The name '$singleName' for $id doesn't match what's in the key store: $subject")
|
||||||
}
|
}
|
||||||
|
|
||||||
val certPath = CertificateFactory.getInstance("X509").generateCertPath(certificates)
|
val certPath = X509CertificateFactory().delegate.generateCertPath(certificates)
|
||||||
return Pair(PartyAndCertificate(certPath), keyPair)
|
return Pair(PartyAndCertificate(certPath), keyPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import net.corda.core.internal.uncheckedCast
|
|||||||
import net.corda.core.messaging.RPCOps
|
import net.corda.core.messaging.RPCOps
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.node.services.TransactionVerifierService
|
import net.corda.core.node.services.TransactionVerifierService
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
||||||
@ -228,7 +229,7 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
* This is not using the H2 "automatic mixed mode" directly but leans on many of the underpinnings. For more details
|
* This is not using the H2 "automatic mixed mode" directly but leans on many of the underpinnings. For more details
|
||||||
* on H2 URLs and configuration see: http://www.h2database.com/html/features.html#database_url
|
* on H2 URLs and configuration see: http://www.h2database.com/html/features.html#database_url
|
||||||
*/
|
*/
|
||||||
override fun <T> initialiseDatabasePersistence(schemaService: SchemaService, insideTransaction: (CordaPersistence) -> T): T {
|
override fun <T> initialiseDatabasePersistence(schemaService: SchemaService, identityService: IdentityService, insideTransaction: (CordaPersistence) -> T): T {
|
||||||
val databaseUrl = configuration.dataSourceProperties.getProperty("dataSource.url")
|
val databaseUrl = configuration.dataSourceProperties.getProperty("dataSource.url")
|
||||||
val h2Prefix = "jdbc:h2:file:"
|
val h2Prefix = "jdbc:h2:file:"
|
||||||
if (databaseUrl != null && databaseUrl.startsWith(h2Prefix)) {
|
if (databaseUrl != null && databaseUrl.startsWith(h2Prefix)) {
|
||||||
@ -245,7 +246,7 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
printBasicNodeInfo("Database connection url is", "jdbc:h2:$url/node")
|
printBasicNodeInfo("Database connection url is", "jdbc:h2:$url/node")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.initialiseDatabasePersistence(schemaService, insideTransaction)
|
return super.initialiseDatabasePersistence(schemaService, identityService, insideTransaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _startupComplete = openFuture<Unit>()
|
private val _startupComplete = openFuture<Unit>()
|
||||||
|
@ -8,8 +8,8 @@ import net.corda.core.crypto.Crypto
|
|||||||
import net.corda.core.crypto.SignatureScheme
|
import net.corda.core.crypto.SignatureScheme
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.node.utilities.*
|
|
||||||
import net.corda.nodeapi.config.SSLConfiguration
|
import net.corda.nodeapi.config.SSLConfiguration
|
||||||
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
import org.bouncycastle.asn1.x509.NameConstraints
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
|
@ -12,8 +12,6 @@ import java.net.URL
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
data class DevModeOptions(val disableCheckpointChecker: Boolean = false)
|
|
||||||
|
|
||||||
interface NodeConfiguration : NodeSSLConfiguration {
|
interface NodeConfiguration : NodeSSLConfiguration {
|
||||||
// myLegalName should be only used in the initial network registration, we should use the name from the certificate instead of this.
|
// myLegalName should be only used in the initial network registration, we should use the name from the certificate instead of this.
|
||||||
// TODO: Remove this so we don't accidentally use this identity in the code?
|
// TODO: Remove this so we don't accidentally use this identity in the code?
|
||||||
@ -21,7 +19,7 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
|||||||
val emailAddress: String
|
val emailAddress: String
|
||||||
val exportJMXto: String
|
val exportJMXto: String
|
||||||
val dataSourceProperties: Properties
|
val dataSourceProperties: Properties
|
||||||
val database: Properties?
|
val database: DatabaseConfig
|
||||||
val rpcUsers: List<User>
|
val rpcUsers: List<User>
|
||||||
val devMode: Boolean
|
val devMode: Boolean
|
||||||
val devModeOptions: DevModeOptions?
|
val devModeOptions: DevModeOptions?
|
||||||
@ -42,6 +40,27 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
|||||||
val sshd: SSHDConfiguration?
|
val sshd: SSHDConfiguration?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class DevModeOptions(val disableCheckpointChecker: Boolean = false)
|
||||||
|
|
||||||
|
data class DatabaseConfig(
|
||||||
|
val initDatabase: Boolean = true,
|
||||||
|
val serverNameTablePrefix: String = "",
|
||||||
|
val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class TransactionIsolationLevel {
|
||||||
|
NONE,
|
||||||
|
READ_UNCOMMITTED,
|
||||||
|
READ_COMMITTED,
|
||||||
|
REPEATABLE_READ,
|
||||||
|
SERIALIZABLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JDBC constant value of the same name but with prefixed with TRANSACTION_ defined in [java.sql.Connection].
|
||||||
|
*/
|
||||||
|
val jdbcValue: Int = java.sql.Connection::class.java.getField("TRANSACTION_$name").get(null) as Int
|
||||||
|
}
|
||||||
|
|
||||||
fun NodeConfiguration.shouldCheckCheckpoints(): Boolean {
|
fun NodeConfiguration.shouldCheckCheckpoints(): Boolean {
|
||||||
return this.devMode && this.devModeOptions?.disableCheckpointChecker != true
|
return this.devMode && this.devModeOptions?.disableCheckpointChecker != true
|
||||||
}
|
}
|
||||||
@ -89,7 +108,7 @@ data class NodeConfigurationImpl(
|
|||||||
override val keyStorePassword: String,
|
override val keyStorePassword: String,
|
||||||
override val trustStorePassword: String,
|
override val trustStorePassword: String,
|
||||||
override val dataSourceProperties: Properties,
|
override val dataSourceProperties: Properties,
|
||||||
override val database: Properties?,
|
override val database: DatabaseConfig = DatabaseConfig(),
|
||||||
override val compatibilityZoneURL: URL? = null,
|
override val compatibilityZoneURL: URL? = null,
|
||||||
override val rpcUsers: List<User>,
|
override val rpcUsers: List<User>,
|
||||||
override val verifierType: VerifierType,
|
override val verifierType: VerifierType,
|
||||||
|
@ -10,6 +10,7 @@ import net.corda.core.node.services.UnknownAnonymousPartyException
|
|||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
import java.security.InvalidAlgorithmParameterException
|
import java.security.InvalidAlgorithmParameterException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -63,7 +64,7 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
|||||||
log.error("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.")
|
log.error("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.")
|
||||||
log.error("Certificate path :")
|
log.error("Certificate path :")
|
||||||
identity.certPath.certificates.reversed().forEachIndexed { index, certificate ->
|
identity.certPath.certificates.reversed().forEachIndexed { index, certificate ->
|
||||||
val space = (0 until index).map { " " }.joinToString("")
|
val space = (0 until index).joinToString("") { " " }
|
||||||
log.error("$space${certificate.toX509CertHolder().subject}")
|
log.error("$space${certificate.toX509CertHolder().subject}")
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
@ -78,8 +79,7 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
|||||||
if (firstCertWithThisName != identity.certificate) {
|
if (firstCertWithThisName != identity.certificate) {
|
||||||
val certificates = identity.certPath.certificates
|
val certificates = identity.certPath.certificates
|
||||||
val idx = certificates.lastIndexOf(firstCertWithThisName)
|
val idx = certificates.lastIndexOf(firstCertWithThisName)
|
||||||
val certFactory = CertificateFactory.getInstance("X509")
|
val firstPath = X509CertificateFactory().delegate.generateCertPath(certificates.slice(idx until certificates.size))
|
||||||
val firstPath = certFactory.generateCertPath(certificates.slice(idx..certificates.size - 1))
|
|
||||||
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
|||||||
val candidate = partyFromKey(party.owningKey)
|
val candidate = partyFromKey(party.owningKey)
|
||||||
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
||||||
return if (candidate != null) {
|
return if (candidate != null) {
|
||||||
require(party.nameOrNull() == null || party.nameOrNull() == candidate.name) { "Candidate party ${candidate} does not match expected ${party}" }
|
require(party.nameOrNull() == null || party.nameOrNull() == candidate.name) { "Candidate party $candidate does not match expected $party" }
|
||||||
wellKnownPartyFromX500Name(candidate.name)
|
wellKnownPartyFromX500Name(candidate.name)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
@ -9,13 +9,13 @@ import net.corda.core.internal.toX509CertHolder
|
|||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.utilities.debug
|
|
||||||
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
|
import net.corda.core.utilities.debug
|
||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||||
import net.corda.node.utilities.NODE_DATABASE_PREFIX
|
import net.corda.node.utilities.NODE_DATABASE_PREFIX
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.security.InvalidAlgorithmParameterException
|
import java.security.InvalidAlgorithmParameterException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.*
|
import java.security.cert.*
|
||||||
@ -26,26 +26,21 @@ import javax.persistence.Id
|
|||||||
import javax.persistence.Lob
|
import javax.persistence.Lob
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class PersistentIdentityService(identities: Iterable<PartyAndCertificate> = emptySet(),
|
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||||
confidentialIdentities: Iterable<PartyAndCertificate> = emptySet(),
|
|
||||||
override val trustRoot: X509Certificate,
|
|
||||||
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityService {
|
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityService {
|
||||||
constructor(wellKnownIdentities: Iterable<PartyAndCertificate> = emptySet(),
|
constructor(trustRoot: X509CertificateHolder) : this(trustRoot.cert)
|
||||||
confidentialIdentities: Iterable<PartyAndCertificate> = emptySet(),
|
|
||||||
trustRoot: X509CertificateHolder) : this(wellKnownIdentities, confidentialIdentities, trustRoot.cert)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
private val certFactory: CertificateFactory = CertificateFactory.getInstance("X.509")
|
|
||||||
|
|
||||||
fun createPKMap(): AppendOnlyPersistentMap<SecureHash, PartyAndCertificate, PersistentIdentity, String> {
|
fun createPKMap(): AppendOnlyPersistentMap<SecureHash, PartyAndCertificate, PersistentIdentity, String> {
|
||||||
return AppendOnlyPersistentMap(
|
return AppendOnlyPersistentMap(
|
||||||
toPersistentEntityKey = { it.toString() },
|
toPersistentEntityKey = { it.toString() },
|
||||||
fromPersistentEntity = {
|
fromPersistentEntity = {
|
||||||
Pair(SecureHash.parse(it.publicKeyHash),
|
Pair(
|
||||||
PartyAndCertificate(ByteArrayInputStream(it.identity).use {
|
SecureHash.parse(it.publicKeyHash),
|
||||||
certFactory.generateCertPath(it)
|
PartyAndCertificate(X509CertificateFactory().delegate.generateCertPath(it.identity.inputStream()))
|
||||||
}))
|
)
|
||||||
},
|
},
|
||||||
toPersistentEntity = { key: SecureHash, value: PartyAndCertificate ->
|
toPersistentEntity = { key: SecureHash, value: PartyAndCertificate ->
|
||||||
PersistentIdentity(key.toString(), value.certPath.encoded)
|
PersistentIdentity(key.toString(), value.certPath.encoded)
|
||||||
@ -101,6 +96,10 @@ class PersistentIdentityService(identities: Iterable<PartyAndCertificate> = empt
|
|||||||
init {
|
init {
|
||||||
val caCertificatesWithRoot: Set<X509Certificate> = caCertificates.toSet() + trustRoot
|
val caCertificatesWithRoot: Set<X509Certificate> = caCertificates.toSet() + trustRoot
|
||||||
caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(caCertificatesWithRoot))
|
caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(caCertificatesWithRoot))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Requires a database transaction. */
|
||||||
|
fun loadIdentities(identities: Iterable<PartyAndCertificate> = emptySet(), confidentialIdentities: Iterable<PartyAndCertificate> = emptySet()) {
|
||||||
identities.forEach {
|
identities.forEach {
|
||||||
val key = mapToKey(it)
|
val key = mapToKey(it)
|
||||||
keyToParties.addWithDuplicatesAllowed(key, it, false)
|
keyToParties.addWithDuplicatesAllowed(key, it, false)
|
||||||
@ -135,8 +134,7 @@ class PersistentIdentityService(identities: Iterable<PartyAndCertificate> = empt
|
|||||||
if (firstCertWithThisName != identity.certificate) {
|
if (firstCertWithThisName != identity.certificate) {
|
||||||
val certificates = identity.certPath.certificates
|
val certificates = identity.certPath.certificates
|
||||||
val idx = certificates.lastIndexOf(firstCertWithThisName)
|
val idx = certificates.lastIndexOf(firstCertWithThisName)
|
||||||
val certFactory = CertificateFactory.getInstance("X509")
|
val firstPath = X509CertificateFactory().delegate.generateCertPath(certificates.slice(idx until certificates.size))
|
||||||
val firstPath = certFactory.generateCertPath(certificates.slice(idx..certificates.size - 1))
|
|
||||||
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,14 +6,14 @@ import net.corda.core.internal.cert
|
|||||||
import net.corda.core.internal.toX509CertHolder
|
import net.corda.core.internal.toX509CertHolder
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
import net.corda.node.utilities.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.node.utilities.ContentSignerBuilder
|
import net.corda.nodeapi.internal.crypto.ContentSignerBuilder
|
||||||
import net.corda.node.utilities.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import org.bouncycastle.operator.ContentSigner
|
import org.bouncycastle.operator.ContentSigner
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.Security
|
import java.security.Security
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|
||||||
@ -37,8 +37,7 @@ fun freshCertificate(identityService: IdentityService,
|
|||||||
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
|
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
|
||||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCert.subject,
|
val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCert.subject,
|
||||||
issuerSigner, issuer.name, subjectPublicKey, window)
|
issuerSigner, issuer.name, subjectPublicKey, window)
|
||||||
val certFactory = CertificateFactory.getInstance("X509")
|
val ourCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
|
||||||
val ourCertPath = certFactory.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
|
|
||||||
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
||||||
identityService.verifyAndRegisterIdentity(anonymisedIdentity)
|
identityService.verifyAndRegisterIdentity(anonymisedIdentity)
|
||||||
return anonymisedIdentity
|
return anonymisedIdentity
|
||||||
|
@ -20,10 +20,10 @@ import net.corda.node.services.messaging.NodeLoginModule.Companion.NODE_ROLE
|
|||||||
import net.corda.node.services.messaging.NodeLoginModule.Companion.PEER_ROLE
|
import net.corda.node.services.messaging.NodeLoginModule.Companion.PEER_ROLE
|
||||||
import net.corda.node.services.messaging.NodeLoginModule.Companion.RPC_ROLE
|
import net.corda.node.services.messaging.NodeLoginModule.Companion.RPC_ROLE
|
||||||
import net.corda.node.services.messaging.NodeLoginModule.Companion.VERIFIER_ROLE
|
import net.corda.node.services.messaging.NodeLoginModule.Companion.VERIFIER_ROLE
|
||||||
import net.corda.node.utilities.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_TLS
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS
|
||||||
import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||||
import net.corda.node.utilities.loadKeyStore
|
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||||
import net.corda.nodeapi.*
|
import net.corda.nodeapi.*
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
||||||
|
@ -10,23 +10,23 @@ import net.corda.core.serialization.SerializationDefaults
|
|||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.*
|
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.sequence
|
import net.corda.core.utilities.sequence
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.services.statemachine.StateMachineManagerImpl
|
import net.corda.node.services.statemachine.StateMachineManagerImpl
|
||||||
import net.corda.node.utilities.*
|
import net.corda.node.utilities.*
|
||||||
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent
|
||||||
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent.*
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.ArtemisAddress
|
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.NodeAddress
|
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.ServiceAddress
|
|
||||||
import org.apache.activemq.artemis.api.core.ActiveMQObjectClosedException
|
import org.apache.activemq.artemis.api.core.ActiveMQObjectClosedException
|
||||||
import org.apache.activemq.artemis.api.core.Message.*
|
import org.apache.activemq.artemis.api.core.Message.*
|
||||||
import org.apache.activemq.artemis.api.core.RoutingType
|
import org.apache.activemq.artemis.api.core.RoutingType
|
||||||
import org.apache.activemq.artemis.api.core.SimpleString
|
import org.apache.activemq.artemis.api.core.SimpleString
|
||||||
import org.apache.activemq.artemis.api.core.client.*
|
import org.apache.activemq.artemis.api.core.client.ClientConsumer
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ClientMessage
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -5,14 +5,17 @@ import net.corda.core.messaging.RPCOps
|
|||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.RPCUserService
|
import net.corda.node.services.RPCUserService
|
||||||
import net.corda.node.utilities.*
|
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
|
||||||
import net.corda.nodeapi.config.SSLConfiguration
|
import net.corda.nodeapi.config.SSLConfiguration
|
||||||
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
||||||
|
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||||
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
|
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
|
||||||
|
|
||||||
class RPCMessagingClient(private val config: SSLConfiguration, serverAddress: NetworkHostAndPort) : SingletonSerializeAsToken() {
|
class RPCMessagingClient(private val config: SSLConfiguration, serverAddress: NetworkHostAndPort) : SingletonSerializeAsToken() {
|
||||||
private val artemis = ArtemisMessagingClient(config, serverAddress)
|
private val artemis = ArtemisMessagingClient(config, serverAddress)
|
||||||
private var rpcServer: RPCServer? = null
|
private var rpcServer: RPCServer? = null
|
||||||
|
|
||||||
fun start(rpcOps: RPCOps, userService: RPCUserService) = synchronized(this) {
|
fun start(rpcOps: RPCOps, userService: RPCUserService) = synchronized(this) {
|
||||||
val locator = artemis.start().sessionFactory.serverLocator
|
val locator = artemis.start().sessionFactory.serverLocator
|
||||||
val myCert = loadKeyStore(config.sslKeystore, config.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_TLS)
|
val myCert = loadKeyStore(config.sslKeystore, config.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_TLS)
|
||||||
|
@ -9,13 +9,11 @@ import org.hibernate.type.descriptor.java.AbstractTypeDescriptor
|
|||||||
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan
|
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan
|
||||||
import org.hibernate.type.descriptor.java.MutabilityPlan
|
import org.hibernate.type.descriptor.java.MutabilityPlan
|
||||||
|
|
||||||
class AbstractPartyDescriptor(identitySvc: () -> IdentityService) : AbstractTypeDescriptor<AbstractParty>(AbstractParty::class.java) {
|
class AbstractPartyDescriptor(private val identityService: IdentityService) : AbstractTypeDescriptor<AbstractParty>(AbstractParty::class.java) {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val identityService: IdentityService by lazy(identitySvc)
|
|
||||||
|
|
||||||
override fun fromString(dbData: String?): AbstractParty? {
|
override fun fromString(dbData: String?): AbstractParty? {
|
||||||
return if (dbData != null) {
|
return if (dbData != null) {
|
||||||
val party = identityService.wellKnownPartyFromX500Name(CordaX500Name.parse(dbData))
|
val party = identityService.wellKnownPartyFromX500Name(CordaX500Name.parse(dbData))
|
||||||
|
@ -12,13 +12,11 @@ import javax.persistence.Converter
|
|||||||
* Completely anonymous parties are stored as null (to preserve privacy).
|
* Completely anonymous parties are stored as null (to preserve privacy).
|
||||||
*/
|
*/
|
||||||
@Converter(autoApply = true)
|
@Converter(autoApply = true)
|
||||||
class AbstractPartyToX500NameAsStringConverter(identitySvc: () -> IdentityService) : AttributeConverter<AbstractParty, String> {
|
class AbstractPartyToX500NameAsStringConverter(private val identityService: IdentityService) : AttributeConverter<AbstractParty, String> {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val identityService: IdentityService by lazy(identitySvc)
|
|
||||||
|
|
||||||
override fun convertToDatabaseColumn(party: AbstractParty?): String? {
|
override fun convertToDatabaseColumn(party: AbstractParty?): String? {
|
||||||
if (party != null) {
|
if (party != null) {
|
||||||
val partyName = identityService.wellKnownPartyFromAnonymous(party)?.toString()
|
val partyName = identityService.wellKnownPartyFromAnonymous(party)?.toString()
|
||||||
|
@ -6,8 +6,8 @@ import net.corda.core.schemas.MappedSchema
|
|||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.toHexString
|
import net.corda.core.utilities.toHexString
|
||||||
import net.corda.node.services.api.SchemaService
|
import net.corda.node.services.api.SchemaService
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.utilities.DatabaseTransactionManager
|
import net.corda.node.utilities.DatabaseTransactionManager
|
||||||
import net.corda.node.utilities.parserTransactionIsolationLevel
|
|
||||||
import org.hibernate.SessionFactory
|
import org.hibernate.SessionFactory
|
||||||
import org.hibernate.boot.MetadataSources
|
import org.hibernate.boot.MetadataSources
|
||||||
import org.hibernate.boot.model.naming.Identifier
|
import org.hibernate.boot.model.naming.Identifier
|
||||||
@ -23,10 +23,9 @@ import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor
|
|||||||
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor
|
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor
|
||||||
import org.hibernate.type.descriptor.sql.VarbinaryTypeDescriptor
|
import org.hibernate.type.descriptor.sql.VarbinaryTypeDescriptor
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
class HibernateConfiguration(val schemaService: SchemaService, private val databaseProperties: Properties, private val createIdentityService: () -> IdentityService) {
|
class HibernateConfiguration(val schemaService: SchemaService, private val databaseConfig: DatabaseConfig, private val identityService: IdentityService) {
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = contextLogger()
|
private val logger = contextLogger()
|
||||||
}
|
}
|
||||||
@ -34,14 +33,13 @@ class HibernateConfiguration(val schemaService: SchemaService, private val datab
|
|||||||
// TODO: make this a guava cache or similar to limit ability for this to grow forever.
|
// TODO: make this a guava cache or similar to limit ability for this to grow forever.
|
||||||
private val sessionFactories = ConcurrentHashMap<Set<MappedSchema>, SessionFactory>()
|
private val sessionFactories = ConcurrentHashMap<Set<MappedSchema>, SessionFactory>()
|
||||||
|
|
||||||
private val transactionIsolationLevel = parserTransactionIsolationLevel(databaseProperties.getProperty("transactionIsolationLevel") ?: "")
|
|
||||||
val sessionFactoryForRegisteredSchemas = schemaService.schemaOptions.keys.let {
|
val sessionFactoryForRegisteredSchemas = schemaService.schemaOptions.keys.let {
|
||||||
logger.info("Init HibernateConfiguration for schemas: $it")
|
logger.info("Init HibernateConfiguration for schemas: $it")
|
||||||
// Register the AbstractPartyDescriptor so Hibernate doesn't warn when encountering AbstractParty. Unfortunately
|
// Register the AbstractPartyDescriptor so Hibernate doesn't warn when encountering AbstractParty. Unfortunately
|
||||||
// Hibernate warns about not being able to find a descriptor if we don't provide one, but won't use it by default
|
// Hibernate warns about not being able to find a descriptor if we don't provide one, but won't use it by default
|
||||||
// so we end up providing both descriptor and converter. We should re-examine this in later versions to see if
|
// so we end up providing both descriptor and converter. We should re-examine this in later versions to see if
|
||||||
// either Hibernate can be convinced to stop warning, use the descriptor by default, or something else.
|
// either Hibernate can be convinced to stop warning, use the descriptor by default, or something else.
|
||||||
JavaTypeDescriptorRegistry.INSTANCE.addDescriptor(AbstractPartyDescriptor(createIdentityService))
|
JavaTypeDescriptorRegistry.INSTANCE.addDescriptor(AbstractPartyDescriptor(identityService))
|
||||||
sessionFactoryForSchemas(it)
|
sessionFactoryForSchemas(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,16 +54,16 @@ class HibernateConfiguration(val schemaService: SchemaService, private val datab
|
|||||||
// necessarily remain and would likely be replaced by something like Liquibase. For now it is very convenient though.
|
// necessarily remain and would likely be replaced by something like Liquibase. For now it is very convenient though.
|
||||||
// TODO: replace auto schema generation as it isn't intended for production use, according to Hibernate docs.
|
// TODO: replace auto schema generation as it isn't intended for production use, according to Hibernate docs.
|
||||||
val config = Configuration(metadataSources).setProperty("hibernate.connection.provider_class", NodeDatabaseConnectionProvider::class.java.name)
|
val config = Configuration(metadataSources).setProperty("hibernate.connection.provider_class", NodeDatabaseConnectionProvider::class.java.name)
|
||||||
.setProperty("hibernate.hbm2ddl.auto", if (databaseProperties.getProperty("initDatabase", "true") == "true") "update" else "validate")
|
.setProperty("hibernate.hbm2ddl.auto", if (databaseConfig.initDatabase) "update" else "validate")
|
||||||
.setProperty("hibernate.format_sql", "true")
|
.setProperty("hibernate.format_sql", "true")
|
||||||
.setProperty("hibernate.connection.isolation", transactionIsolationLevel.toString())
|
.setProperty("hibernate.connection.isolation", databaseConfig.transactionIsolationLevel.jdbcValue.toString())
|
||||||
|
|
||||||
schemas.forEach { schema ->
|
schemas.forEach { schema ->
|
||||||
// TODO: require mechanism to set schemaOptions (databaseSchema, tablePrefix) which are not global to session
|
// TODO: require mechanism to set schemaOptions (databaseSchema, tablePrefix) which are not global to session
|
||||||
schema.mappedTypes.forEach { config.addAnnotatedClass(it) }
|
schema.mappedTypes.forEach { config.addAnnotatedClass(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val sessionFactory = buildSessionFactory(config, metadataSources, databaseProperties.getProperty("serverNameTablePrefix", ""))
|
val sessionFactory = buildSessionFactory(config, metadataSources, databaseConfig.serverNameTablePrefix)
|
||||||
logger.info("Created session factory for schemas: $schemas")
|
logger.info("Created session factory for schemas: $schemas")
|
||||||
return sessionFactory
|
return sessionFactory
|
||||||
}
|
}
|
||||||
@ -80,7 +78,7 @@ class HibernateConfiguration(val schemaService: SchemaService, private val datab
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
// register custom converters
|
// register custom converters
|
||||||
applyAttributeConverter(AbstractPartyToX500NameAsStringConverter(createIdentityService))
|
applyAttributeConverter(AbstractPartyToX500NameAsStringConverter(identityService))
|
||||||
// Register a tweaked version of `org.hibernate.type.MaterializedBlobType` that truncates logged messages.
|
// Register a tweaked version of `org.hibernate.type.MaterializedBlobType` that truncates logged messages.
|
||||||
// to avoid OOM when large blobs might get logged.
|
// to avoid OOM when large blobs might get logged.
|
||||||
applyBasicType(CordaMaterializedBlobType, CordaMaterializedBlobType.name)
|
applyBasicType(CordaMaterializedBlobType, CordaMaterializedBlobType.name)
|
||||||
@ -132,11 +130,13 @@ class HibernateConfiguration(val schemaService: SchemaService, private val datab
|
|||||||
private val LOG_SIZE_LIMIT = 1024
|
private val LOG_SIZE_LIMIT = 1024
|
||||||
|
|
||||||
override fun extractLoggableRepresentation(value: ByteArray?): String {
|
override fun extractLoggableRepresentation(value: ByteArray?): String {
|
||||||
return if (value == null) super.extractLoggableRepresentation(value) else {
|
return if (value == null) {
|
||||||
|
super.extractLoggableRepresentation(value)
|
||||||
|
} else {
|
||||||
if (value.size <= LOG_SIZE_LIMIT) {
|
if (value.size <= LOG_SIZE_LIMIT) {
|
||||||
return "[size=${value.size}, value=${value.toHexString()}]"
|
"[size=${value.size}, value=${value.toHexString()}]"
|
||||||
} else {
|
} else {
|
||||||
return "[size=${value.size}, value=${value.copyOfRange(0, LOG_SIZE_LIMIT).toHexString()}...truncated...]"
|
"[size=${value.size}, value=${value.copyOfRange(0, LOG_SIZE_LIMIT).toHexString()}...truncated...]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ class CordaAuthenticationPlugin(val rpcOps:CordaRPCOps, val userService:RPCUserS
|
|||||||
|
|
||||||
if (user != null && user.password == credential) {
|
if (user != null && user.password == credential) {
|
||||||
val actor = Actor(Actor.Id(username), userService.id, nodeLegalName)
|
val actor = Actor(Actor.Id(username), userService.id, nodeLegalName)
|
||||||
return CordaSSHAuthInfo(true, RPCOpsWithContext(rpcOps, InvocationContext.rpc(actor), RpcPermissions(user.permissions)))
|
return CordaSSHAuthInfo(true, makeRPCOpsWithContext(rpcOps, InvocationContext.rpc(actor), RpcPermissions(user.permissions)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return AuthInfo.UNSUCCESSFUL;
|
return AuthInfo.UNSUCCESSFUL;
|
||||||
|
@ -101,10 +101,10 @@ object InteractiveShell {
|
|||||||
this.nodeLegalName = configuration.myLegalName
|
this.nodeLegalName = configuration.myLegalName
|
||||||
this.database = database
|
this.database = database
|
||||||
val dir = configuration.baseDirectory
|
val dir = configuration.baseDirectory
|
||||||
val runSshDeamon = configuration.sshd != null
|
val runSshDaemon = configuration.sshd != null
|
||||||
|
|
||||||
val config = Properties()
|
val config = Properties()
|
||||||
if (runSshDeamon) {
|
if (runSshDaemon) {
|
||||||
val sshKeysDir = dir / "sshkey"
|
val sshKeysDir = dir / "sshkey"
|
||||||
sshKeysDir.toFile().mkdirs()
|
sshKeysDir.toFile().mkdirs()
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ object InteractiveShell {
|
|||||||
ExternalResolver.INSTANCE.addCommand("start", "An alias for 'flow start'", StartShellCommand::class.java)
|
ExternalResolver.INSTANCE.addCommand("start", "An alias for 'flow start'", StartShellCommand::class.java)
|
||||||
shell = ShellLifecycle(dir).start(config)
|
shell = ShellLifecycle(dir).start(config)
|
||||||
|
|
||||||
if (runSshDeamon) {
|
if (runSshDaemon) {
|
||||||
Node.printBasicNodeInfo("SSH server listening on port", configuration.sshd!!.port.toString())
|
Node.printBasicNodeInfo("SSH server listening on port", configuration.sshd!!.port.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,7 +182,7 @@ object InteractiveShell {
|
|||||||
context.refresh()
|
context.refresh()
|
||||||
this.config = config
|
this.config = config
|
||||||
start(context)
|
start(context)
|
||||||
return context.getPlugin(ShellFactory::class.java).create(null, CordaSSHAuthInfo(false, RPCOpsWithContext(rpcOps, net.corda.core.context.InvocationContext.shell(), RpcPermissions.ALL), StdoutANSIProgressRenderer))
|
return context.getPlugin(ShellFactory::class.java).create(null, CordaSSHAuthInfo(false, makeRPCOpsWithContext(rpcOps, net.corda.core.context.InvocationContext.shell(), RpcPermissions.ALL), StdoutANSIProgressRenderer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +236,7 @@ object InteractiveShell {
|
|||||||
try {
|
try {
|
||||||
// Show the progress tracker on the console until the flow completes or is interrupted with a
|
// Show the progress tracker on the console until the flow completes or is interrupted with a
|
||||||
// Ctrl-C keypress.
|
// Ctrl-C keypress.
|
||||||
val stateObservable = runFlowFromString({ clazz,args -> rpcOps.startTrackedFlowDynamic (clazz, *args) }, inputData, clazz)
|
val stateObservable = runFlowFromString({ clazz, args -> rpcOps.startTrackedFlowDynamic(clazz, *args) }, inputData, clazz)
|
||||||
|
|
||||||
val latch = CountDownLatch(1)
|
val latch = CountDownLatch(1)
|
||||||
ansiProgressRenderer.render(stateObservable, { latch.countDown() })
|
ansiProgressRenderer.render(stateObservable, { latch.countDown() })
|
||||||
@ -247,7 +247,6 @@ object InteractiveShell {
|
|||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
// TODO: When the flow framework allows us to kill flows mid-flight, do so here.
|
// TODO: When the flow framework allows us to kill flows mid-flight, do so here.
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e: NoApplicableConstructor) {
|
} catch (e: NoApplicableConstructor) {
|
||||||
output.println("No matching constructor found:", Color.red)
|
output.println("No matching constructor found:", Color.red)
|
||||||
e.errors.forEach { output.println("- $it", Color.red) }
|
e.errors.forEach { output.println("- $it", Color.red) }
|
||||||
@ -326,7 +325,9 @@ object InteractiveShell {
|
|||||||
val (stateMachines, stateMachineUpdates) = proxy.stateMachinesFeed()
|
val (stateMachines, stateMachineUpdates) = proxy.stateMachinesFeed()
|
||||||
val currentStateMachines = stateMachines.map { StateMachineUpdate.Added(it) }
|
val currentStateMachines = stateMachines.map { StateMachineUpdate.Added(it) }
|
||||||
val subscriber = FlowWatchPrintingSubscriber(out)
|
val subscriber = FlowWatchPrintingSubscriber(out)
|
||||||
stateMachineUpdates.startWith(currentStateMachines).subscribe(subscriber)
|
database.transaction {
|
||||||
|
stateMachineUpdates.startWith(currentStateMachines).subscribe(subscriber)
|
||||||
|
}
|
||||||
var result: Any? = subscriber.future
|
var result: Any? = subscriber.future
|
||||||
if (result is Future<*>) {
|
if (result is Future<*>) {
|
||||||
if (!result.isDone) {
|
if (!result.isDone) {
|
||||||
@ -348,7 +349,7 @@ object InteractiveShell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun runRPCFromString(input: List<String>, out: RenderPrintWriter, context: InvocationContext<out Any>): Any? {
|
fun runRPCFromString(input: List<String>, out: RenderPrintWriter, context: InvocationContext<out Any>, cordaRPCOps: CordaRPCOps): Any? {
|
||||||
val parser = StringToMethodCallParser(CordaRPCOps::class.java, context.attributes["mapper"] as ObjectMapper)
|
val parser = StringToMethodCallParser(CordaRPCOps::class.java, context.attributes["mapper"] as ObjectMapper)
|
||||||
|
|
||||||
val cmd = input.joinToString(" ").trim { it <= ' ' }
|
val cmd = input.joinToString(" ").trim { it <= ' ' }
|
||||||
@ -363,7 +364,7 @@ object InteractiveShell {
|
|||||||
var result: Any? = null
|
var result: Any? = null
|
||||||
try {
|
try {
|
||||||
InputStreamSerializer.invokeContext = context
|
InputStreamSerializer.invokeContext = context
|
||||||
val call = database.transaction { parser.parse(context.attributes["ops"] as CordaRPCOps, cmd) }
|
val call = database.transaction { parser.parse(cordaRPCOps, cmd) }
|
||||||
result = call.call()
|
result = call.call()
|
||||||
if (result != null && result !is kotlin.Unit && result !is Void) {
|
if (result != null && result !is kotlin.Unit && result !is Void) {
|
||||||
result = printAndFollowRPCResponse(result, out)
|
result = printAndFollowRPCResponse(result, out)
|
||||||
|
@ -1,205 +1,45 @@
|
|||||||
package net.corda.node.shell
|
package net.corda.node.shell
|
||||||
|
|
||||||
import net.corda.core.concurrent.CordaFuture
|
|
||||||
import net.corda.core.context.InvocationContext
|
import net.corda.core.context.InvocationContext
|
||||||
import net.corda.core.contracts.ContractState
|
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.flows.FlowLogic
|
|
||||||
import net.corda.core.identity.AbstractParty
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.messaging.*
|
import net.corda.core.messaging.*
|
||||||
import net.corda.core.node.NodeInfo
|
|
||||||
import net.corda.core.node.services.AttachmentId
|
|
||||||
import net.corda.core.node.services.NetworkMapCache
|
|
||||||
import net.corda.core.node.services.Vault
|
|
||||||
import net.corda.core.node.services.vault.*
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT
|
import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT
|
||||||
import net.corda.node.services.messaging.RpcAuthContext
|
import net.corda.node.services.messaging.RpcAuthContext
|
||||||
import net.corda.node.services.messaging.RpcPermissions
|
import net.corda.node.services.messaging.RpcPermissions
|
||||||
import java.io.InputStream
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.security.PublicKey
|
import java.lang.reflect.Proxy
|
||||||
import java.time.Instant
|
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.Future
|
import java.util.concurrent.Future
|
||||||
|
|
||||||
class RPCOpsWithContext(val cordaRPCOps: CordaRPCOps, val invocationContext:InvocationContext, val rpcPermissions: RpcPermissions) : CordaRPCOps {
|
fun makeRPCOpsWithContext(cordaRPCOps: CordaRPCOps, invocationContext:InvocationContext, rpcPermissions: RpcPermissions) : CordaRPCOps {
|
||||||
|
return Proxy.newProxyInstance(CordaRPCOps::class.java.classLoader, arrayOf(CordaRPCOps::class.java), { proxy, method, args ->
|
||||||
|
RPCContextRunner(invocationContext, rpcPermissions) {
|
||||||
|
try {
|
||||||
|
method.invoke(cordaRPCOps, *(args ?: arrayOf()))
|
||||||
|
} catch (e: InvocationTargetException) {
|
||||||
|
// Unpack exception.
|
||||||
|
throw e.targetException
|
||||||
|
}
|
||||||
|
}.get().getOrThrow()
|
||||||
|
}) as CordaRPCOps
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RPCContextRunner<T>(val invocationContext:InvocationContext, val rpcPermissions: RpcPermissions, val block:() -> T) : Thread() {
|
||||||
class RPCContextRunner<T>(val invocationContext:InvocationContext, val permissions:RpcPermissions, val block:() -> T) : Thread() {
|
private var result: CompletableFuture<T> = CompletableFuture()
|
||||||
private var result: CompletableFuture<T> = CompletableFuture()
|
override fun run() {
|
||||||
override fun run() {
|
CURRENT_RPC_CONTEXT.set(RpcAuthContext(invocationContext, rpcPermissions))
|
||||||
CURRENT_RPC_CONTEXT.set(RpcAuthContext(invocationContext, permissions))
|
try {
|
||||||
try {
|
result.complete(block())
|
||||||
result.complete(block())
|
} catch (e:Throwable) {
|
||||||
} catch (e:Throwable) {
|
result.completeExceptionally(e)
|
||||||
result.completeExceptionally(e)
|
} finally {
|
||||||
}
|
|
||||||
CURRENT_RPC_CONTEXT.remove()
|
CURRENT_RPC_CONTEXT.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun get(): Future<T> {
|
|
||||||
start()
|
|
||||||
join()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun uploadAttachmentWithMetadata(jar: InputStream, uploader: String, filename: String): SecureHash {
|
fun get(): Future<T> {
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.uploadAttachmentWithMetadata(jar, uploader, filename) }.get().getOrThrow()
|
start()
|
||||||
}
|
join()
|
||||||
|
return result
|
||||||
override fun queryAttachments(query: AttachmentQueryCriteria, sorting: AttachmentSort?): List<AttachmentId> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.queryAttachments(query, sorting) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : ContractState> vaultTrackByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.vaultTrackByWithSorting(contractStateType, criteria, sorting) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : ContractState> vaultTrackByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.vaultTrackByWithPagingSpec(contractStateType, criteria, paging) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : ContractState> vaultTrackByCriteria(contractStateType: Class<out T>, criteria: QueryCriteria): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.vaultTrackByCriteria(contractStateType, criteria) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : ContractState> vaultTrack(contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.vaultTrack(contractStateType) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : ContractState> vaultQueryByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.vaultQueryByWithSorting(contractStateType, criteria, sorting) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : ContractState> vaultQueryByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): Vault.Page<T> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.vaultQueryByWithPagingSpec(contractStateType, criteria, paging) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : ContractState> vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class<out T>): Vault.Page<T> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.vaultQueryByCriteria(criteria, contractStateType) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : ContractState> vaultQuery(contractStateType: Class<out T>): Vault.Page<T> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.vaultQuery(contractStateType) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun stateMachinesSnapshot(): List<StateMachineInfo> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions, cordaRPCOps::stateMachinesSnapshot).get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun stateMachinesFeed(): DataFeed<List<StateMachineInfo>, StateMachineUpdate> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions, cordaRPCOps::stateMachinesFeed).get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : ContractState> vaultQueryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class<out T>): Vault.Page<T> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.vaultQueryBy(criteria, paging, sorting, contractStateType) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : ContractState> vaultTrackBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.vaultTrackBy(criteria, paging, sorting, contractStateType) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun internalVerifiedTransactionsSnapshot(): List<SignedTransaction> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.internalVerifiedTransactionsSnapshot() }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun internalVerifiedTransactionsFeed(): DataFeed<List<SignedTransaction>, SignedTransaction> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.internalVerifiedTransactionsFeed() }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun stateMachineRecordedTransactionMappingSnapshot(): List<StateMachineTransactionMapping> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.stateMachineRecordedTransactionMappingSnapshot() }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun stateMachineRecordedTransactionMappingFeed(): DataFeed<List<StateMachineTransactionMapping>, StateMachineTransactionMapping> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.stateMachineRecordedTransactionMappingFeed() }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun networkMapSnapshot(): List<NodeInfo> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.networkMapSnapshot() }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun networkMapFeed(): DataFeed<List<NodeInfo>, NetworkMapCache.MapChange> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.networkMapFeed() }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T> startFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowHandle<T> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.startFlowDynamic(logicType, *args) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T> startTrackedFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowProgressHandle<T> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.startTrackedFlowDynamic(logicType, *args) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun nodeInfo(): NodeInfo {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.nodeInfo() }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun notaryIdentities(): List<Party> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.notaryIdentities() }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addVaultTransactionNote(txnId: SecureHash, txnNote: String) {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.addVaultTransactionNote(txnId, txnNote) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getVaultTransactionNotes(txnId: SecureHash): Iterable<String> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.getVaultTransactionNotes(txnId) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun attachmentExists(id: SecureHash): Boolean {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.attachmentExists(id) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openAttachment(id: SecureHash): InputStream {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.openAttachment(id) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun uploadAttachment(jar: InputStream): SecureHash {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.uploadAttachment(jar) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun currentNodeTime(): Instant {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.currentNodeTime() }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun waitUntilNetworkReady(): CordaFuture<Void?> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.waitUntilNetworkReady() }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.wellKnownPartyFromAnonymous(party) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun partyFromKey(key: PublicKey): Party? {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.partyFromKey(key) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun wellKnownPartyFromX500Name(x500Name: CordaX500Name): Party? {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.wellKnownPartyFromX500Name(x500Name) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun notaryPartyFromX500Name(x500Name: CordaX500Name): Party? {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.notaryPartyFromX500Name(x500Name) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.partiesFromName(query, exactMatch) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun registeredFlows(): List<String> {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.registeredFlows() }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.nodeInfoFromParty(party) }.get().getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun clearNetworkMapCache() {
|
|
||||||
return RPCContextRunner(invocationContext, rpcPermissions) { cordaRPCOps.clearNetworkMapCache() }.get().getOrThrow()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -169,7 +169,7 @@ abstract class ANSIProgressRenderer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CRaSHNSIProgressRenderer(val renderPrintWriter:RenderPrintWriter) : ANSIProgressRenderer() {
|
class CRaSHANSIProgressRenderer(val renderPrintWriter:RenderPrintWriter) : ANSIProgressRenderer() {
|
||||||
|
|
||||||
override fun printLine(line: String) {
|
override fun printLine(line: String) {
|
||||||
renderPrintWriter.println(line)
|
renderPrintWriter.println(line)
|
||||||
@ -181,7 +181,7 @@ class CRaSHNSIProgressRenderer(val renderPrintWriter:RenderPrintWriter) : ANSIPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setup() {
|
override fun setup() {
|
||||||
//we assume SSH always use ansi
|
// We assume SSH always use ANSI.
|
||||||
usingANSI = true
|
usingANSI = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import com.zaxxer.hikari.HikariConfig
|
|||||||
import com.zaxxer.hikari.HikariDataSource
|
import com.zaxxer.hikari.HikariDataSource
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.node.services.api.SchemaService
|
import net.corda.node.services.api.SchemaService
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.services.persistence.HibernateConfiguration
|
import net.corda.node.services.persistence.HibernateConfiguration
|
||||||
import net.corda.node.services.schema.NodeSchemaService
|
import net.corda.node.services.schema.NodeSchemaService
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -21,20 +22,23 @@ import java.util.concurrent.CopyOnWriteArrayList
|
|||||||
const val NODE_DATABASE_PREFIX = "node_"
|
const val NODE_DATABASE_PREFIX = "node_"
|
||||||
|
|
||||||
//HikariDataSource implements Closeable which allows CordaPersistence to be Closeable
|
//HikariDataSource implements Closeable which allows CordaPersistence to be Closeable
|
||||||
class CordaPersistence(var dataSource: HikariDataSource, private val schemaService: SchemaService,
|
class CordaPersistence(
|
||||||
private val createIdentityService: () -> IdentityService, databaseProperties: Properties) : Closeable {
|
val dataSource: HikariDataSource,
|
||||||
var transactionIsolationLevel = parserTransactionIsolationLevel(databaseProperties.getProperty("transactionIsolationLevel"))
|
private val schemaService: SchemaService,
|
||||||
|
private val identityService: IdentityService,
|
||||||
|
databaseConfig: DatabaseConfig
|
||||||
|
) : Closeable {
|
||||||
|
val transactionIsolationLevel = databaseConfig.transactionIsolationLevel.jdbcValue
|
||||||
val hibernateConfig: HibernateConfiguration by lazy {
|
val hibernateConfig: HibernateConfiguration by lazy {
|
||||||
transaction {
|
transaction {
|
||||||
HibernateConfiguration(schemaService, databaseProperties, createIdentityService)
|
HibernateConfiguration(schemaService, databaseConfig, identityService)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val entityManagerFactory get() = hibernateConfig.sessionFactoryForRegisteredSchemas
|
val entityManagerFactory get() = hibernateConfig.sessionFactoryForRegisteredSchemas
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun connect(dataSource: HikariDataSource, schemaService: SchemaService, createIdentityService: () -> IdentityService, databaseProperties: Properties): CordaPersistence {
|
fun connect(dataSource: HikariDataSource, schemaService: SchemaService, identityService: IdentityService, databaseConfig: DatabaseConfig): CordaPersistence {
|
||||||
return CordaPersistence(dataSource, schemaService, createIdentityService, databaseProperties).apply {
|
return CordaPersistence(dataSource, schemaService, identityService, databaseConfig).apply {
|
||||||
DatabaseTransactionManager(this)
|
DatabaseTransactionManager(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,9 +57,7 @@ class CordaPersistence(var dataSource: HikariDataSource, private val schemaServi
|
|||||||
/**
|
/**
|
||||||
* Creates an instance of [DatabaseTransaction], with the transaction isolation level specified at the creation time.
|
* Creates an instance of [DatabaseTransaction], with the transaction isolation level specified at the creation time.
|
||||||
*/
|
*/
|
||||||
fun createTransaction(): DatabaseTransaction {
|
fun createTransaction(): DatabaseTransaction = createTransaction(transactionIsolationLevel)
|
||||||
return createTransaction(transactionIsolationLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createSession(): Connection {
|
fun createSession(): Connection {
|
||||||
// We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases.
|
// We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases.
|
||||||
@ -78,9 +80,7 @@ class CordaPersistence(var dataSource: HikariDataSource, private val schemaServi
|
|||||||
* Executes given statement in the scope of transaction with the transaction level specified at the creation time.
|
* Executes given statement in the scope of transaction with the transaction level specified at the creation time.
|
||||||
* @param statement to be executed in the scope of this transaction.
|
* @param statement to be executed in the scope of this transaction.
|
||||||
*/
|
*/
|
||||||
fun <T> transaction(statement: DatabaseTransaction.() -> T): T {
|
fun <T> transaction(statement: DatabaseTransaction.() -> T): T = transaction(transactionIsolationLevel, statement)
|
||||||
return transaction(transactionIsolationLevel, statement)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> transaction(transactionIsolation: Int, repetitionAttempts: Int, statement: DatabaseTransaction.() -> T): T {
|
private fun <T> transaction(transactionIsolation: Int, repetitionAttempts: Int, statement: DatabaseTransaction.() -> T): T {
|
||||||
val outer = DatabaseTransactionManager.currentOrNull()
|
val outer = DatabaseTransactionManager.currentOrNull()
|
||||||
@ -120,11 +120,10 @@ class CordaPersistence(var dataSource: HikariDataSource, private val schemaServi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun configureDatabase(dataSourceProperties: Properties, databaseProperties: Properties?, createIdentityService: () -> IdentityService, schemaService: SchemaService = NodeSchemaService(null)): CordaPersistence {
|
fun configureDatabase(dataSourceProperties: Properties, databaseConfig: DatabaseConfig, identityService: IdentityService, schemaService: SchemaService = NodeSchemaService(null)): CordaPersistence {
|
||||||
val config = HikariConfig(dataSourceProperties)
|
val config = HikariConfig(dataSourceProperties)
|
||||||
val dataSource = HikariDataSource(config)
|
val dataSource = HikariDataSource(config)
|
||||||
val persistence = CordaPersistence.connect(dataSource, schemaService, createIdentityService, databaseProperties ?: Properties())
|
val persistence = CordaPersistence.connect(dataSource, schemaService, identityService, databaseConfig)
|
||||||
|
|
||||||
// Check not in read-only mode.
|
// Check not in read-only mode.
|
||||||
persistence.transaction {
|
persistence.transaction {
|
||||||
persistence.dataSource.connection.use {
|
persistence.dataSource.connection.use {
|
||||||
@ -217,15 +216,3 @@ fun <T : Any> rx.Observable<T>.wrapWithDatabaseTransaction(db: CordaPersistence?
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parserTransactionIsolationLevel(property: String?): Int =
|
|
||||||
when (property) {
|
|
||||||
"none" -> Connection.TRANSACTION_NONE
|
|
||||||
"readUncommitted" -> Connection.TRANSACTION_READ_UNCOMMITTED
|
|
||||||
"readCommitted" -> Connection.TRANSACTION_READ_COMMITTED
|
|
||||||
"repeatableRead" -> Connection.TRANSACTION_REPEATABLE_READ
|
|
||||||
"serializable" -> Connection.TRANSACTION_SERIALIZABLE
|
|
||||||
else -> {
|
|
||||||
Connection.TRANSACTION_REPEATABLE_READ
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -8,6 +8,7 @@ import net.corda.core.internal.cert
|
|||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ package net.corda.node.utilities.registration
|
|||||||
|
|
||||||
import com.google.common.net.MediaType
|
import com.google.common.net.MediaType
|
||||||
import net.corda.core.internal.openHttpConnection
|
import net.corda.core.internal.openHttpConnection
|
||||||
import net.corda.node.utilities.CertificateStream
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -32,9 +32,9 @@ class HTTPNetworkRegistrationService(compatibilityZoneURL: URL) : NetworkRegistr
|
|||||||
return when (conn.responseCode) {
|
return when (conn.responseCode) {
|
||||||
HTTP_OK -> ZipInputStream(conn.inputStream).use {
|
HTTP_OK -> ZipInputStream(conn.inputStream).use {
|
||||||
val certificates = ArrayList<Certificate>()
|
val certificates = ArrayList<Certificate>()
|
||||||
val stream = CertificateStream(it)
|
val factory = X509CertificateFactory()
|
||||||
while (it.nextEntry != null) {
|
while (it.nextEntry != null) {
|
||||||
certificates.add(stream.nextCertificate())
|
certificates += factory.generateCertificate(it)
|
||||||
}
|
}
|
||||||
certificates.toTypedArray()
|
certificates.toTypedArray()
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.utilities.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA
|
||||||
import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_TLS
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS
|
||||||
import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||||
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
|
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
|
||||||
import org.bouncycastle.util.io.pem.PemObject
|
import org.bouncycastle.util.io.pem.PemObject
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
|
@ -10,7 +10,7 @@ dataSourceProperties = {
|
|||||||
"dataSource.password" = ""
|
"dataSource.password" = ""
|
||||||
}
|
}
|
||||||
database = {
|
database = {
|
||||||
transactionIsolationLevel = "repeatableRead"
|
transactionIsolationLevel = "REPEATABLE_READ"
|
||||||
initDatabase = true
|
initDatabase = true
|
||||||
}
|
}
|
||||||
devMode = true
|
devMode = true
|
||||||
|
@ -69,7 +69,7 @@ public class VaultQueryJavaTests {
|
|||||||
keys.add(getDUMMY_NOTARY_KEY());
|
keys.add(getDUMMY_NOTARY_KEY());
|
||||||
IdentityService identitySvc = makeTestIdentityService();
|
IdentityService identitySvc = makeTestIdentityService();
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Pair<CordaPersistence, MockServices> databaseAndServices = makeTestDatabaseAndMockServices(keys, () -> identitySvc, cordappPackages);
|
Pair<CordaPersistence, MockServices> databaseAndServices = makeTestDatabaseAndMockServices(keys, identitySvc, cordappPackages);
|
||||||
issuerServices = new MockServices(cordappPackages, getDUMMY_CASH_ISSUER_NAME(), getDUMMY_CASH_ISSUER_KEY(), getBOC_KEY());
|
issuerServices = new MockServices(cordappPackages, getDUMMY_CASH_ISSUER_NAME(), getDUMMY_CASH_ISSUER_KEY(), getBOC_KEY());
|
||||||
database = databaseAndServices.getFirst();
|
database = databaseAndServices.getFirst();
|
||||||
services = databaseAndServices.getSecond();
|
services = databaseAndServices.getSecond();
|
||||||
|
@ -2,18 +2,15 @@ package net.corda.node
|
|||||||
|
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||||
import net.corda.client.jackson.JacksonSupport
|
import net.corda.client.jackson.JacksonSupport
|
||||||
import net.corda.core.concurrent.CordaFuture
|
|
||||||
import net.corda.core.contracts.Amount
|
import net.corda.core.contracts.Amount
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.StateMachineRunId
|
import net.corda.core.flows.StateMachineRunId
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.FlowStateMachine
|
|
||||||
import net.corda.core.internal.concurrent.openFuture
|
import net.corda.core.internal.concurrent.openFuture
|
||||||
import net.corda.core.internal.objectOrNewInstance
|
|
||||||
import net.corda.core.messaging.FlowProgressHandle
|
|
||||||
import net.corda.core.messaging.FlowProgressHandleImpl
|
import net.corda.core.messaging.FlowProgressHandleImpl
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.services.identity.InMemoryIdentityService
|
import net.corda.node.services.identity.InMemoryIdentityService
|
||||||
import net.corda.node.shell.InteractiveShell
|
import net.corda.node.shell.InteractiveShell
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
@ -21,7 +18,6 @@ import net.corda.testing.DEV_TRUST_ROOT
|
|||||||
import net.corda.testing.MEGA_CORP
|
import net.corda.testing.MEGA_CORP
|
||||||
import net.corda.testing.MEGA_CORP_IDENTITY
|
import net.corda.testing.MEGA_CORP_IDENTITY
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
|
||||||
import net.corda.testing.rigorousMock
|
import net.corda.testing.rigorousMock
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -33,7 +29,7 @@ import kotlin.test.assertEquals
|
|||||||
class InteractiveShellTest {
|
class InteractiveShellTest {
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
InteractiveShell.database = configureDatabase(MockServices.makeTestDataSourceProperties(), MockServices.makeTestDatabaseProperties(), ::makeTestIdentityService)
|
InteractiveShell.database = configureDatabase(MockServices.makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock())
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -96,6 +92,4 @@ class InteractiveShellTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun party() = check("party: \"${MEGA_CORP.name}\"", MEGA_CORP.name.toString())
|
fun party() = check("party: \"${MEGA_CORP.name}\"", MEGA_CORP.name.toString())
|
||||||
|
|
||||||
class DummyFSM(override val logic: FlowA) : FlowStateMachine<Any?> by rigorousMock()
|
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,8 @@ package net.corda.node.services.config
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.net.URL
|
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
@ -40,7 +38,7 @@ class NodeConfigurationImplTest {
|
|||||||
keyStorePassword = "cordacadevpass",
|
keyStorePassword = "cordacadevpass",
|
||||||
trustStorePassword = "trustpass",
|
trustStorePassword = "trustpass",
|
||||||
dataSourceProperties = makeTestDataSourceProperties(ALICE.name.organisation),
|
dataSourceProperties = makeTestDataSourceProperties(ALICE.name.organisation),
|
||||||
database = makeTestDatabaseProperties(),
|
database = DatabaseConfig(),
|
||||||
rpcUsers = emptyList(),
|
rpcUsers = emptyList(),
|
||||||
verifierType = VerifierType.InMemory,
|
verifierType = VerifierType.InMemory,
|
||||||
useHTTPS = false,
|
useHTTPS = false,
|
||||||
|
@ -23,6 +23,7 @@ import net.corda.node.internal.cordapp.CordappLoader
|
|||||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||||
import net.corda.node.services.api.MonitoringService
|
import net.corda.node.services.api.MonitoringService
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.services.identity.InMemoryIdentityService
|
import net.corda.node.services.identity.InMemoryIdentityService
|
||||||
import net.corda.node.services.network.NetworkMapCacheImpl
|
import net.corda.node.services.network.NetworkMapCacheImpl
|
||||||
import net.corda.node.services.persistence.DBCheckpointStorage
|
import net.corda.node.services.persistence.DBCheckpointStorage
|
||||||
@ -37,10 +38,11 @@ import net.corda.testing.*
|
|||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.node.*
|
import net.corda.testing.node.*
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.*
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
@ -91,8 +93,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
|||||||
smmHasRemovedAllFlows = CountDownLatch(1)
|
smmHasRemovedAllFlows = CountDownLatch(1)
|
||||||
calls = 0
|
calls = 0
|
||||||
val dataSourceProps = makeTestDataSourceProperties()
|
val dataSourceProps = makeTestDataSourceProperties()
|
||||||
val databaseProperties = makeTestDatabaseProperties()
|
database = configureDatabase(dataSourceProps, DatabaseConfig(), rigorousMock())
|
||||||
database = configureDatabase(dataSourceProps, databaseProperties, ::makeTestIdentityService)
|
|
||||||
val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
|
val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
|
||||||
kms = MockKeyManagementService(identityService, ALICE_KEY)
|
kms = MockKeyManagementService(identityService, ALICE_KEY)
|
||||||
val configuration = testNodeConfiguration(Paths.get("."), CordaX500Name("Alice", "London", "GB"))
|
val configuration = testNodeConfiguration(Paths.get("."), CordaX500Name("Alice", "London", "GB"))
|
||||||
|
@ -6,16 +6,15 @@ import net.corda.core.identity.AnonymousParty
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
|
import net.corda.core.internal.cert
|
||||||
import net.corda.core.internal.toX509CertHolder
|
import net.corda.core.internal.toX509CertHolder
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.core.internal.cert
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.node.services.identity.InMemoryIdentityService
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.node.utilities.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.node.utilities.CertificateType
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.node.utilities.X509Utilities
|
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
@ -156,12 +155,11 @@ class InMemoryIdentityServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createParty(x500Name: CordaX500Name, ca: CertificateAndKeyPair): Pair<PartyAndCertificate, PartyAndCertificate> {
|
private fun createParty(x500Name: CordaX500Name, ca: CertificateAndKeyPair): Pair<PartyAndCertificate, PartyAndCertificate> {
|
||||||
val certFactory = CertificateFactory.getInstance("X509")
|
|
||||||
val issuerKeyPair = generateKeyPair()
|
val issuerKeyPair = generateKeyPair()
|
||||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca)
|
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca)
|
||||||
val txKey = Crypto.generateKeyPair()
|
val txKey = Crypto.generateKeyPair()
|
||||||
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
||||||
val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
val txCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
||||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,22 +6,20 @@ import net.corda.core.identity.AnonymousParty
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
|
import net.corda.core.internal.cert
|
||||||
import net.corda.core.internal.toX509CertHolder
|
import net.corda.core.internal.toX509CertHolder
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.node.services.identity.PersistentIdentityService
|
|
||||||
import net.corda.node.utilities.CertificateAndKeyPair
|
|
||||||
import net.corda.node.utilities.CertificateType
|
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
import net.corda.node.utilities.X509Utilities
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import javax.security.auth.x500.X500Principal
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
@ -30,14 +28,13 @@ import kotlin.test.assertNull
|
|||||||
* Tests for the in memory identity service.
|
* Tests for the in memory identity service.
|
||||||
*/
|
*/
|
||||||
class PersistentIdentityServiceTests {
|
class PersistentIdentityServiceTests {
|
||||||
|
private lateinit var database: CordaPersistence
|
||||||
lateinit var database: CordaPersistence
|
private lateinit var services: MockServices
|
||||||
lateinit var services: MockServices
|
private lateinit var identityService: IdentityService
|
||||||
lateinit var identityService: IdentityService
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
val databaseAndServices = MockServices.makeTestDatabaseAndMockServices(keys = emptyList(), createIdentityService = { PersistentIdentityService(trustRoot = DEV_TRUST_ROOT) })
|
val databaseAndServices = MockServices.makeTestDatabaseAndMockServices(keys = emptyList(), identityService = PersistentIdentityService(DEV_TRUST_ROOT))
|
||||||
database = databaseAndServices.first
|
database = databaseAndServices.first
|
||||||
services = databaseAndServices.second
|
services = databaseAndServices.second
|
||||||
identityService = services.identityService
|
identityService = services.identityService
|
||||||
@ -236,7 +233,7 @@ class PersistentIdentityServiceTests {
|
|||||||
|
|
||||||
// Create new identity service mounted onto same DB
|
// Create new identity service mounted onto same DB
|
||||||
val newPersistentIdentityService = database.transaction {
|
val newPersistentIdentityService = database.transaction {
|
||||||
PersistentIdentityService(trustRoot = DEV_TRUST_ROOT)
|
PersistentIdentityService(DEV_TRUST_ROOT)
|
||||||
}
|
}
|
||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
@ -256,12 +253,11 @@ class PersistentIdentityServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createParty(x500Name: CordaX500Name, ca: CertificateAndKeyPair): Pair<PartyAndCertificate, PartyAndCertificate> {
|
private fun createParty(x500Name: CordaX500Name, ca: CertificateAndKeyPair): Pair<PartyAndCertificate, PartyAndCertificate> {
|
||||||
val certFactory = CertificateFactory.getInstance("X509")
|
|
||||||
val issuerKeyPair = generateKeyPair()
|
val issuerKeyPair = generateKeyPair()
|
||||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca)
|
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca)
|
||||||
val txKey = Crypto.generateKeyPair()
|
val txKey = Crypto.generateKeyPair()
|
||||||
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
||||||
val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
val txCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
||||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.core.internal.concurrent.openFuture
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.RPCUserService
|
import net.corda.node.services.RPCUserService
|
||||||
import net.corda.node.services.RPCUserServiceImpl
|
import net.corda.node.services.RPCUserServiceImpl
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.services.config.configureWithDevSSLCertificate
|
import net.corda.node.services.config.configureWithDevSSLCertificate
|
||||||
import net.corda.node.services.network.NetworkMapCacheImpl
|
import net.corda.node.services.network.NetworkMapCacheImpl
|
||||||
@ -18,8 +19,6 @@ import net.corda.node.utilities.configureDatabase
|
|||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
@ -68,7 +67,7 @@ class ArtemisMessagingTests {
|
|||||||
baseDirectory = baseDirectory,
|
baseDirectory = baseDirectory,
|
||||||
myLegalName = ALICE.name)
|
myLegalName = ALICE.name)
|
||||||
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
||||||
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
|
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock())
|
||||||
networkMapRegistrationFuture = doneFuture(Unit)
|
networkMapRegistrationFuture = doneFuture(Unit)
|
||||||
networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, emptyList()), rigorousMock())
|
networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, emptyList()), rigorousMock())
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ import net.corda.core.serialization.serialize
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.services.network.TestNodeInfoFactory.createNodeInfo
|
import net.corda.node.services.network.TestNodeInfoFactory.createNodeInfo
|
||||||
import net.corda.node.utilities.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.node.utilities.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
|
@ -8,15 +8,14 @@ import net.corda.core.identity.PartyAndCertificate
|
|||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.utilities.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.node.utilities.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
object TestNodeInfoFactory {
|
object TestNodeInfoFactory {
|
||||||
@ -40,11 +39,11 @@ object TestNodeInfoFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun buildCertPath(vararg certificates: Certificate): CertPath {
|
private fun buildCertPath(vararg certificates: Certificate): CertPath {
|
||||||
return CertificateFactory.getInstance("X509").generateCertPath(certificates.asList())
|
return X509CertificateFactory().delegate.generateCertPath(certificates.asList())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun X509CertificateHolder.toX509Certificate(): X509Certificate {
|
private fun X509CertificateHolder.toX509Certificate(): X509Certificate {
|
||||||
return CertificateFactory.getInstance("X509").generateCertificate(ByteArrayInputStream(encoded)) as X509Certificate
|
return X509CertificateFactory().generateCertificate(encoded.inputStream())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -4,14 +4,14 @@ import com.google.common.primitives.Ints
|
|||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import net.corda.node.services.api.Checkpoint
|
import net.corda.node.services.api.Checkpoint
|
||||||
import net.corda.node.services.api.CheckpointStorage
|
import net.corda.node.services.api.CheckpointStorage
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
import net.corda.testing.LogHelper
|
import net.corda.testing.LogHelper
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
|
import net.corda.testing.rigorousMock
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -31,13 +31,14 @@ class DBCheckpointStorageTests {
|
|||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
lateinit var checkpointStorage: DBCheckpointStorage
|
|
||||||
lateinit var database: CordaPersistence
|
private lateinit var checkpointStorage: DBCheckpointStorage
|
||||||
|
private lateinit var database: CordaPersistence
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
||||||
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
|
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock())
|
||||||
newCheckpointStorage()
|
newCheckpointStorage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@ import net.corda.core.crypto.SecureHash
|
|||||||
import net.corda.core.crypto.SignatureMetadata
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
import net.corda.core.crypto.TransactionSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.node.StatesToRecord
|
import net.corda.core.node.StatesToRecord
|
||||||
import net.corda.core.node.services.VaultService
|
|
||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.node.services.api.VaultServiceInternal
|
import net.corda.node.services.api.VaultServiceInternal
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.services.schema.HibernateObserver
|
import net.corda.node.services.schema.HibernateObserver
|
||||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
import net.corda.node.services.vault.NodeVaultService
|
import net.corda.node.services.vault.NodeVaultService
|
||||||
@ -19,8 +19,6 @@ import net.corda.node.utilities.configureDatabase
|
|||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -33,16 +31,16 @@ class DBTransactionStorageTests {
|
|||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
lateinit var database: CordaPersistence
|
|
||||||
lateinit var transactionStorage: DBTransactionStorage
|
private lateinit var database: CordaPersistence
|
||||||
lateinit var services: MockServices
|
private lateinit var transactionStorage: DBTransactionStorage
|
||||||
val vault: VaultService get() = services.vaultService
|
private lateinit var services: MockServices
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
||||||
val dataSourceProps = makeTestDataSourceProperties()
|
val dataSourceProps = makeTestDataSourceProperties()
|
||||||
database = configureDatabase(dataSourceProps, makeTestDatabaseProperties(), ::makeTestIdentityService)
|
database = configureDatabase(dataSourceProps, DatabaseConfig(), rigorousMock())
|
||||||
database.transaction {
|
database.transaction {
|
||||||
services = object : MockServices(BOB_KEY) {
|
services = object : MockServices(BOB_KEY) {
|
||||||
override val vaultService: VaultServiceInternal
|
override val vaultService: VaultServiceInternal
|
||||||
|
@ -25,6 +25,7 @@ import net.corda.finance.schemas.CashSchemaV1
|
|||||||
import net.corda.finance.schemas.SampleCashSchemaV2
|
import net.corda.finance.schemas.SampleCashSchemaV2
|
||||||
import net.corda.finance.schemas.SampleCashSchemaV3
|
import net.corda.finance.schemas.SampleCashSchemaV3
|
||||||
import net.corda.finance.utils.sumCash
|
import net.corda.finance.utils.sumCash
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.services.schema.HibernateObserver
|
import net.corda.node.services.schema.HibernateObserver
|
||||||
import net.corda.node.services.vault.VaultSchemaV1
|
import net.corda.node.services.vault.VaultSchemaV1
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
@ -36,7 +37,6 @@ import net.corda.testing.contracts.fillWithSomeTestDeals
|
|||||||
import net.corda.testing.contracts.fillWithSomeTestLinearStates
|
import net.corda.testing.contracts.fillWithSomeTestLinearStates
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
||||||
import net.corda.testing.schemas.DummyLinearStateSchemaV1
|
import net.corda.testing.schemas.DummyLinearStateSchemaV1
|
||||||
import net.corda.testing.schemas.DummyLinearStateSchemaV2
|
import net.corda.testing.schemas.DummyLinearStateSchemaV2
|
||||||
@ -84,8 +84,7 @@ class HibernateConfigurationTest {
|
|||||||
issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY)
|
issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY)
|
||||||
notaryServices = MockServices(cordappPackages, DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
notaryServices = MockServices(cordappPackages, DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
||||||
val dataSourceProps = makeTestDataSourceProperties()
|
val dataSourceProps = makeTestDataSourceProperties()
|
||||||
val defaultDatabaseProperties = makeTestDatabaseProperties()
|
database = configureDatabase(dataSourceProps, DatabaseConfig(), makeTestIdentityService())
|
||||||
database = configureDatabase(dataSourceProps, defaultDatabaseProperties, ::makeTestIdentityService)
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
hibernateConfig = database.hibernateConfig
|
hibernateConfig = database.hibernateConfig
|
||||||
// `consumeCash` expects we can self-notarise transactions
|
// `consumeCash` expects we can self-notarise transactions
|
||||||
|
@ -13,13 +13,13 @@ import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
|||||||
import net.corda.core.node.services.vault.AttachmentSort
|
import net.corda.core.node.services.vault.AttachmentSort
|
||||||
import net.corda.core.node.services.vault.Builder
|
import net.corda.core.node.services.vault.Builder
|
||||||
import net.corda.core.node.services.vault.Sort
|
import net.corda.core.node.services.vault.Sort
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
import net.corda.testing.LogHelper
|
import net.corda.testing.LogHelper
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
import net.corda.testing.rigorousMock
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
@ -36,15 +36,15 @@ import kotlin.test.assertNull
|
|||||||
|
|
||||||
class NodeAttachmentStorageTest {
|
class NodeAttachmentStorageTest {
|
||||||
// Use an in memory file system for testing attachment storage.
|
// Use an in memory file system for testing attachment storage.
|
||||||
lateinit var fs: FileSystem
|
private lateinit var fs: FileSystem
|
||||||
lateinit var database: CordaPersistence
|
private lateinit var database: CordaPersistence
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
||||||
|
|
||||||
val dataSourceProperties = makeTestDataSourceProperties()
|
val dataSourceProperties = makeTestDataSourceProperties()
|
||||||
database = configureDatabase(dataSourceProperties, makeTestDatabaseProperties(), ::makeTestIdentityService)
|
database = configureDatabase(dataSourceProperties, DatabaseConfig(), rigorousMock())
|
||||||
fs = Jimfs.newFileSystem(Configuration.unix())
|
fs = Jimfs.newFileSystem(Configuration.unix())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,9 +82,9 @@ class NodeAttachmentStorageTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `metadata can be used to search`() {
|
fun `metadata can be used to search`() {
|
||||||
val (jarA,hashA) = makeTestJar()
|
val (jarA, _) = makeTestJar()
|
||||||
val (jarB,hashB) = makeTestJar(listOf(Pair("file","content")))
|
val (jarB, hashB) = makeTestJar(listOf(Pair("file","content")))
|
||||||
val (jarC,hashC) = makeTestJar(listOf(Pair("magic_file","magic_content_puff")))
|
val (jarC, hashC) = makeTestJar(listOf(Pair("magic_file","magic_content_puff")))
|
||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val storage = NodeAttachmentService(MetricRegistry())
|
val storage = NodeAttachmentService(MetricRegistry())
|
||||||
|
@ -11,23 +11,21 @@ import net.corda.core.schemas.MappedSchema
|
|||||||
import net.corda.core.schemas.PersistentState
|
import net.corda.core.schemas.PersistentState
|
||||||
import net.corda.core.schemas.QueryableState
|
import net.corda.core.schemas.QueryableState
|
||||||
import net.corda.node.services.api.SchemaService
|
import net.corda.node.services.api.SchemaService
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.utilities.DatabaseTransactionManager
|
import net.corda.node.utilities.DatabaseTransactionManager
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
import net.corda.testing.LogHelper
|
import net.corda.testing.LogHelper
|
||||||
import net.corda.testing.MEGA_CORP
|
import net.corda.testing.MEGA_CORP
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
import net.corda.testing.rigorousMock
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
class HibernateObserverTests {
|
class HibernateObserverTests {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
LogHelper.setLevel(HibernateObserver::class)
|
LogHelper.setLevel(HibernateObserver::class)
|
||||||
@ -51,9 +49,8 @@ class HibernateObserverTests {
|
|||||||
get() = throw UnsupportedOperationException()
|
get() = throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method does not use back quotes for a nice name since it seems to kill the kotlin compiler.
|
|
||||||
@Test
|
@Test
|
||||||
fun testChildObjectsArePersisted() {
|
fun `test child objects are persisted`() {
|
||||||
val testSchema = TestSchema
|
val testSchema = TestSchema
|
||||||
val rawUpdatesPublisher = PublishSubject.create<Vault.Update<ContractState>>()
|
val rawUpdatesPublisher = PublishSubject.create<Vault.Update<ContractState>>()
|
||||||
val schemaService = object : SchemaService {
|
val schemaService = object : SchemaService {
|
||||||
@ -68,7 +65,7 @@ class HibernateObserverTests {
|
|||||||
return parent
|
return parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService, schemaService)
|
val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock(), schemaService)
|
||||||
HibernateObserver.install(rawUpdatesPublisher, database.hibernateConfig)
|
HibernateObserver.install(rawUpdatesPublisher, database.hibernateConfig)
|
||||||
database.transaction {
|
database.transaction {
|
||||||
rawUpdatesPublisher.onNext(Vault.Update(emptySet(), setOf(StateAndRef(TransactionState(TestState(), DummyContract.PROGRAM_ID, MEGA_CORP), StateRef(SecureHash.sha256("dummy"), 0)))))
|
rawUpdatesPublisher.onNext(Vault.Update(emptySet(), setOf(StateAndRef(TransactionState(TestState(), DummyContract.PROGRAM_ID, MEGA_CORP), StateRef(SecureHash.sha256("dummy"), 0)))))
|
||||||
|
@ -10,16 +10,18 @@ import net.corda.core.internal.concurrent.asCordaFuture
|
|||||||
import net.corda.core.internal.concurrent.transpose
|
import net.corda.core.internal.concurrent.transpose
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
import net.corda.node.utilities.DatabaseTransaction
|
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
import net.corda.testing.LogHelper
|
import net.corda.testing.LogHelper
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.freeLocalHostAndPort
|
import net.corda.testing.freeLocalHostAndPort
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
import net.corda.testing.rigorousMock
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
import org.junit.After
|
||||||
import org.junit.*
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
@ -30,8 +32,8 @@ class DistributedImmutableMapTests {
|
|||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule(true)
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
lateinit var cluster: List<Member>
|
|
||||||
lateinit var transaction: DatabaseTransaction
|
private lateinit var cluster: List<Member>
|
||||||
private val databases: MutableList<CordaPersistence> = mutableListOf()
|
private val databases: MutableList<CordaPersistence> = mutableListOf()
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@ -86,7 +88,7 @@ class DistributedImmutableMapTests {
|
|||||||
private fun createReplica(myAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): CompletableFuture<Member> {
|
private fun createReplica(myAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): CompletableFuture<Member> {
|
||||||
val storage = Storage.builder().withStorageLevel(StorageLevel.MEMORY).build()
|
val storage = Storage.builder().withStorageLevel(StorageLevel.MEMORY).build()
|
||||||
val address = Address(myAddress.host, myAddress.port)
|
val address = Address(myAddress.host, myAddress.port)
|
||||||
val database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties("serverNameTablePrefix", "PORT_${myAddress.port}_"), ::makeTestIdentityService)
|
val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(serverNameTablePrefix = "PORT_${myAddress.port}_"), rigorousMock())
|
||||||
databases.add(database)
|
databases.add(database)
|
||||||
val stateMachineFactory = { DistributedImmutableMap(database, RaftUniquenessProvider.Companion::createMap) }
|
val stateMachineFactory = { DistributedImmutableMap(database, RaftUniquenessProvider.Companion::createMap) }
|
||||||
|
|
||||||
|
@ -2,12 +2,11 @@ package net.corda.node.services.transactions
|
|||||||
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.node.services.UniquenessException
|
import net.corda.core.node.services.UniquenessException
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
@ -19,15 +18,16 @@ class PersistentUniquenessProviderTests {
|
|||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
val identity = MEGA_CORP
|
|
||||||
val txID = SecureHash.randomSHA256()
|
|
||||||
|
|
||||||
lateinit var database: CordaPersistence
|
private val identity = MEGA_CORP
|
||||||
|
private val txID = SecureHash.randomSHA256()
|
||||||
|
|
||||||
|
private lateinit var database: CordaPersistence
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
||||||
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
|
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock())
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -27,15 +27,14 @@ import net.corda.finance.schemas.CashSchemaV1
|
|||||||
import net.corda.finance.schemas.CashSchemaV1.PersistentCashState
|
import net.corda.finance.schemas.CashSchemaV1.PersistentCashState
|
||||||
import net.corda.finance.schemas.CommercialPaperSchemaV1
|
import net.corda.finance.schemas.CommercialPaperSchemaV1
|
||||||
import net.corda.finance.schemas.SampleCashSchemaV3
|
import net.corda.finance.schemas.SampleCashSchemaV3
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.contracts.*
|
import net.corda.testing.contracts.*
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
|
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
|
||||||
import net.corda.testing.schemas.DummyLinearStateSchemaV1
|
import net.corda.testing.schemas.DummyLinearStateSchemaV1
|
||||||
import org.assertj.core.api.Assertions
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
@ -53,9 +52,16 @@ class VaultQueryTests {
|
|||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
private val cordappPackages = setOf(
|
|
||||||
"net.corda.testing.contracts", "net.corda.finance.contracts",
|
@Rule
|
||||||
CashSchemaV1::class.packageName, CommercialPaperSchemaV1::class.packageName, DummyLinearStateSchemaV1::class.packageName).toMutableList()
|
@JvmField
|
||||||
|
val expectedEx: ExpectedException = ExpectedException.none()
|
||||||
|
|
||||||
|
private val cordappPackages = mutableListOf(
|
||||||
|
"net.corda.testing.contracts",
|
||||||
|
"net.corda.finance.contracts",
|
||||||
|
CashSchemaV1::class.packageName,
|
||||||
|
DummyLinearStateSchemaV1::class.packageName)
|
||||||
private lateinit var services: MockServices
|
private lateinit var services: MockServices
|
||||||
private lateinit var notaryServices: MockServices
|
private lateinit var notaryServices: MockServices
|
||||||
private val vaultService: VaultService get() = services.vaultService
|
private val vaultService: VaultService get() = services.vaultService
|
||||||
@ -93,7 +99,7 @@ class VaultQueryTests {
|
|||||||
@Ignore
|
@Ignore
|
||||||
@Test
|
@Test
|
||||||
fun createPersistentTestDb() {
|
fun createPersistentTestDb() {
|
||||||
val database = configureDatabase(makePersistentDataSourceProperties(), makeTestDatabaseProperties(), { identitySvc })
|
val database = configureDatabase(makePersistentDataSourceProperties(), DatabaseConfig(), identitySvc)
|
||||||
setUpDb(database, 5000)
|
setUpDb(database, 5000)
|
||||||
|
|
||||||
database.close()
|
database.close()
|
||||||
@ -129,9 +135,6 @@ class VaultQueryTests {
|
|||||||
return props
|
return props
|
||||||
}
|
}
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val expectedEx = ExpectedException.none()!!
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query API tests
|
* Query API tests
|
||||||
*/
|
*/
|
||||||
@ -1597,8 +1600,8 @@ class VaultQueryTests {
|
|||||||
|
|
||||||
val result = vaultService.queryBy<CommercialPaper.State>(criteria1)
|
val result = vaultService.queryBy<CommercialPaper.State>(criteria1)
|
||||||
|
|
||||||
Assertions.assertThat(result.states).hasSize(1)
|
assertThat(result.states).hasSize(1)
|
||||||
Assertions.assertThat(result.statesMetadata).hasSize(1)
|
assertThat(result.statesMetadata).hasSize(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1642,8 +1645,8 @@ class VaultQueryTests {
|
|||||||
|
|
||||||
vaultService.queryBy<CommercialPaper.State>(criteria1.and(criteria3).and(criteria2))
|
vaultService.queryBy<CommercialPaper.State>(criteria1.and(criteria3).and(criteria2))
|
||||||
}
|
}
|
||||||
Assertions.assertThat(result.states).hasSize(1)
|
assertThat(result.states).hasSize(1)
|
||||||
Assertions.assertThat(result.statesMetadata).hasSize(1)
|
assertThat(result.statesMetadata).hasSize(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@ package net.corda.node.utilities
|
|||||||
import com.google.common.util.concurrent.SettableFuture
|
import com.google.common.util.concurrent.SettableFuture
|
||||||
import net.corda.core.internal.bufferUntilSubscribed
|
import net.corda.core.internal.bufferUntilSubscribed
|
||||||
import net.corda.core.internal.tee
|
import net.corda.core.internal.tee
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
import net.corda.testing.rigorousMock
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -18,10 +18,10 @@ class ObservablesTests {
|
|||||||
|
|
||||||
private fun isInDatabaseTransaction(): Boolean = (DatabaseTransactionManager.currentOrNull() != null)
|
private fun isInDatabaseTransaction(): Boolean = (DatabaseTransactionManager.currentOrNull() != null)
|
||||||
|
|
||||||
val toBeClosed = mutableListOf<Closeable>()
|
private val toBeClosed = mutableListOf<Closeable>()
|
||||||
|
|
||||||
fun createDatabase(): CordaPersistence {
|
private fun createDatabase(): CordaPersistence {
|
||||||
val database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
|
val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock())
|
||||||
toBeClosed += database
|
toBeClosed += database
|
||||||
return database
|
return database
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,395 @@
|
|||||||
|
package net.corda.node.utilities
|
||||||
|
|
||||||
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.crypto.SignatureScheme
|
||||||
|
import net.corda.core.crypto.newSecureRandom
|
||||||
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.internal.*
|
||||||
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.rules.TemporaryFolder
|
||||||
|
import java.io.DataInputStream
|
||||||
|
import java.io.DataOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.InetAddress
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.net.ServerSocket
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.security.KeyStore
|
||||||
|
import javax.net.ssl.*
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Various tests for mixed-scheme mutual TLS authentication, such as:
|
||||||
|
* Both TLS keys and CAs are using EC NIST P-256.
|
||||||
|
* Both TLS keys and CAs are using RSA.
|
||||||
|
* Server EC NIST P-256 - Client RSA.
|
||||||
|
* Server RSA - Client EC NIST P-256.
|
||||||
|
* Mixed CA and TLS keys.
|
||||||
|
*
|
||||||
|
* TLS/SSL protocols support a large number of cipher suites.
|
||||||
|
* A cipher suite is a collection of symmetric and asymmetric encryption algorithms used by hosts to establish
|
||||||
|
* a secure communication. Supported cipher suites can be classified based on encryption algorithm strength,
|
||||||
|
* key length, key exchange and authentication mechanisms. Some cipher suites offer better level of security than others.
|
||||||
|
*
|
||||||
|
* Each TLS cipher suite has a unique name that is used to identify it and to describe the algorithmic contents of it.
|
||||||
|
* Each segment in a cipher suite name stands for a different algorithm or protocol.
|
||||||
|
* An example of a cipher suite name: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||||
|
* The meaning of this name is:
|
||||||
|
* TLS defines the protocol that this cipher suite is for; it will usually be TLS.
|
||||||
|
* ECDHE indicates the key exchange algorithm being used.
|
||||||
|
* ECDSA indicates the authentication algorithm (signing the DH keys).
|
||||||
|
* AES_128_GCM indicates the block cipher being used to encrypt the message stream.
|
||||||
|
* SHA256 indicates the message authentication algorithm which is used to authenticate a message.
|
||||||
|
*/
|
||||||
|
class TLSAuthenticationTests {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val tempFolder: TemporaryFolder = TemporaryFolder()
|
||||||
|
|
||||||
|
// Root CA.
|
||||||
|
private val ROOT_X500 = CordaX500Name(commonName = "Root_CA_1", organisation = "R3CEV", locality = "London", country = "GB")
|
||||||
|
// Intermediate CA.
|
||||||
|
private val INTERMEDIATE_X500 = CordaX500Name(commonName = "Intermediate_CA_1", organisation = "R3CEV", locality = "London", country = "GB")
|
||||||
|
// TLS server (client1).
|
||||||
|
private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB")
|
||||||
|
// TLS client (client2).
|
||||||
|
private val CLIENT_2_X500 = CordaX500Name(commonName = "Client_2", organisation = "R3CEV", locality = "London", country = "GB")
|
||||||
|
// Password for keys and keystores.
|
||||||
|
private val PASSWORD = "dummypassword"
|
||||||
|
// Default supported TLS schemes for Corda nodes.
|
||||||
|
private val CORDA_TLS_CIPHER_SUITES = arrayOf(
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `All EC R1`() {
|
||||||
|
val (serverSocketFactory, clientSocketFactory) = buildTLSFactories(
|
||||||
|
rootCAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
intermediateCAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client1CAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client1TLSScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client2CAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client2TLSScheme = Crypto.ECDSA_SECP256R1_SHA256
|
||||||
|
)
|
||||||
|
|
||||||
|
val (serverSocket, clientSocket) =
|
||||||
|
buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0)
|
||||||
|
|
||||||
|
testConnect(serverSocket, clientSocket, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `All RSA`() {
|
||||||
|
val (serverSocketFactory, clientSocketFactory) = buildTLSFactories(
|
||||||
|
rootCAScheme = Crypto.RSA_SHA256,
|
||||||
|
intermediateCAScheme = Crypto.RSA_SHA256,
|
||||||
|
client1CAScheme = Crypto.RSA_SHA256,
|
||||||
|
client1TLSScheme = Crypto.RSA_SHA256,
|
||||||
|
client2CAScheme = Crypto.RSA_SHA256,
|
||||||
|
client2TLSScheme = Crypto.RSA_SHA256
|
||||||
|
)
|
||||||
|
|
||||||
|
val (serverSocket, clientSocket) =
|
||||||
|
buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0)
|
||||||
|
|
||||||
|
testConnect(serverSocket, clientSocket, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server's public key type is the one selected if users use different key types (e.g RSA and EC R1).
|
||||||
|
@Test
|
||||||
|
fun `Server RSA - Client EC R1 - CAs all EC R1`() {
|
||||||
|
val (serverSocketFactory, clientSocketFactory) = buildTLSFactories(
|
||||||
|
rootCAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
intermediateCAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client1CAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client1TLSScheme = Crypto.RSA_SHA256,
|
||||||
|
client2CAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client2TLSScheme = Crypto.ECDSA_SECP256R1_SHA256
|
||||||
|
)
|
||||||
|
|
||||||
|
val (serverSocket, clientSocket) =
|
||||||
|
buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0)
|
||||||
|
testConnect(serverSocket, clientSocket, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256") // Server's key type is selected.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Server EC R1 - Client RSA - CAs all EC R1`() {
|
||||||
|
val (serverSocketFactory, clientSocketFactory) = buildTLSFactories(
|
||||||
|
rootCAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
intermediateCAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client1CAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client1TLSScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client2CAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client2TLSScheme = Crypto.RSA_SHA256
|
||||||
|
)
|
||||||
|
|
||||||
|
val (serverSocket, clientSocket) = buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0)
|
||||||
|
testConnect(serverSocket, clientSocket, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256") // Server's key type is selected.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Server EC R1 - Client EC R1 - CAs all RSA`() {
|
||||||
|
val (serverSocketFactory, clientSocketFactory) = buildTLSFactories(
|
||||||
|
rootCAScheme = Crypto.RSA_SHA256,
|
||||||
|
intermediateCAScheme = Crypto.RSA_SHA256,
|
||||||
|
client1CAScheme = Crypto.RSA_SHA256,
|
||||||
|
client1TLSScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client2CAScheme = Crypto.RSA_SHA256,
|
||||||
|
client2TLSScheme = Crypto.ECDSA_SECP256R1_SHA256
|
||||||
|
)
|
||||||
|
|
||||||
|
val (serverSocket, clientSocket) = buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0)
|
||||||
|
testConnect(serverSocket, clientSocket, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Server EC R1 - Client RSA - Mixed CAs`() {
|
||||||
|
val (serverSocketFactory, clientSocketFactory) = buildTLSFactories(
|
||||||
|
rootCAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
intermediateCAScheme = Crypto.RSA_SHA256,
|
||||||
|
client1CAScheme = Crypto.RSA_SHA256,
|
||||||
|
client1TLSScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client2CAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client2TLSScheme = Crypto.RSA_SHA256
|
||||||
|
)
|
||||||
|
|
||||||
|
val (serverSocket, clientSocket) = buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0)
|
||||||
|
testConnect(serverSocket, clientSocket, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `All RSA - avoid ECC for DH`() {
|
||||||
|
val (serverSocketFactory, clientSocketFactory) = buildTLSFactories(
|
||||||
|
rootCAScheme = Crypto.RSA_SHA256,
|
||||||
|
intermediateCAScheme = Crypto.RSA_SHA256,
|
||||||
|
client1CAScheme = Crypto.RSA_SHA256,
|
||||||
|
client1TLSScheme = Crypto.RSA_SHA256,
|
||||||
|
client2CAScheme = Crypto.RSA_SHA256,
|
||||||
|
client2TLSScheme = Crypto.RSA_SHA256
|
||||||
|
)
|
||||||
|
|
||||||
|
val (serverSocket, clientSocket) = buildTLSSockets(
|
||||||
|
serverSocketFactory,
|
||||||
|
clientSocketFactory,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
CORDA_TLS_CIPHER_SUITES,
|
||||||
|
arrayOf("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")) // Second client accepts DHE only.
|
||||||
|
testConnect(serverSocket, clientSocket, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")
|
||||||
|
}
|
||||||
|
|
||||||
|
// According to RFC 5246 (TLS 1.2), section 7.4.1.2 ClientHello cipher_suites:
|
||||||
|
// This is a list of the cryptographic options supported by the client, with the client's first preference first.
|
||||||
|
//
|
||||||
|
// However, the server is still free to ignore this order and pick what it thinks is best,
|
||||||
|
// see https://security.stackexchange.com/questions/121608 for more information.
|
||||||
|
@Test
|
||||||
|
fun `TLS cipher suite order matters - client wins`() {
|
||||||
|
val (serverSocketFactory, clientSocketFactory) = buildTLSFactories(
|
||||||
|
rootCAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
intermediateCAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client1CAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client1TLSScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client2CAScheme = Crypto.ECDSA_SECP256R1_SHA256,
|
||||||
|
client2TLSScheme = Crypto.ECDSA_SECP256R1_SHA256
|
||||||
|
)
|
||||||
|
|
||||||
|
val (serverSocket, clientSocket) = buildTLSSockets(
|
||||||
|
serverSocketFactory,
|
||||||
|
clientSocketFactory,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
arrayOf("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"), // GCM then CBC.
|
||||||
|
arrayOf("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")) // CBC then GCM.
|
||||||
|
testConnect(serverSocket, clientSocket, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256") // Client order wins.
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tempFile(name: String): Path = tempFolder.root.toPath() / name
|
||||||
|
|
||||||
|
private fun buildTLSFactories(
|
||||||
|
rootCAScheme: SignatureScheme,
|
||||||
|
intermediateCAScheme: SignatureScheme,
|
||||||
|
client1CAScheme: SignatureScheme,
|
||||||
|
client1TLSScheme: SignatureScheme,
|
||||||
|
client2CAScheme: SignatureScheme,
|
||||||
|
client2TLSScheme: SignatureScheme
|
||||||
|
): Pair<SSLServerSocketFactory, SSLSocketFactory> {
|
||||||
|
|
||||||
|
val trustStorePath = tempFile("cordaTrustStore.jks")
|
||||||
|
val client1TLSKeyStorePath = tempFile("client1sslkeystore.jks")
|
||||||
|
val client2TLSKeyStorePath = tempFile("client2sslkeystore.jks")
|
||||||
|
|
||||||
|
// ROOT CA key and cert.
|
||||||
|
val rootCAKeyPair = Crypto.generateKeyPair(rootCAScheme)
|
||||||
|
val rootCACert = X509Utilities.createSelfSignedCACertificate(ROOT_X500, rootCAKeyPair)
|
||||||
|
|
||||||
|
// Intermediate CA key and cert.
|
||||||
|
val intermediateCAKeyPair = Crypto.generateKeyPair(intermediateCAScheme)
|
||||||
|
val intermediateCACert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.INTERMEDIATE_CA,
|
||||||
|
rootCACert,
|
||||||
|
rootCAKeyPair,
|
||||||
|
INTERMEDIATE_X500,
|
||||||
|
intermediateCAKeyPair.public
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client 1 keys, certs and SSLKeyStore.
|
||||||
|
val client1CAKeyPair = Crypto.generateKeyPair(client1CAScheme)
|
||||||
|
val client1CACert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.CLIENT_CA,
|
||||||
|
intermediateCACert,
|
||||||
|
intermediateCAKeyPair,
|
||||||
|
CLIENT_1_X500,
|
||||||
|
client1CAKeyPair.public
|
||||||
|
)
|
||||||
|
|
||||||
|
val client1TLSKeyPair = Crypto.generateKeyPair(client1TLSScheme)
|
||||||
|
val client1TLSCert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.TLS,
|
||||||
|
client1CACert,
|
||||||
|
client1CAKeyPair,
|
||||||
|
CLIENT_1_X500,
|
||||||
|
client1TLSKeyPair.public
|
||||||
|
)
|
||||||
|
|
||||||
|
val client1TLSKeyStore = loadOrCreateKeyStore(client1TLSKeyStorePath, PASSWORD)
|
||||||
|
client1TLSKeyStore.addOrReplaceKey(
|
||||||
|
X509Utilities.CORDA_CLIENT_TLS,
|
||||||
|
client1TLSKeyPair.private,
|
||||||
|
PASSWORD.toCharArray(),
|
||||||
|
arrayOf(client1TLSCert, client1CACert, intermediateCACert, rootCACert))
|
||||||
|
// client1TLSKeyStore.save(client1TLSKeyStorePath, PASSWORD)
|
||||||
|
|
||||||
|
// Client 2 keys, certs and SSLKeyStore.
|
||||||
|
val client2CAKeyPair = Crypto.generateKeyPair(client2CAScheme)
|
||||||
|
val client2CACert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.CLIENT_CA,
|
||||||
|
intermediateCACert,
|
||||||
|
intermediateCAKeyPair,
|
||||||
|
CLIENT_2_X500,
|
||||||
|
client2CAKeyPair.public
|
||||||
|
)
|
||||||
|
|
||||||
|
val client2TLSKeyPair = Crypto.generateKeyPair(client2TLSScheme)
|
||||||
|
val client2TLSCert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.TLS,
|
||||||
|
client2CACert,
|
||||||
|
client2CAKeyPair,
|
||||||
|
CLIENT_2_X500,
|
||||||
|
client2TLSKeyPair.public
|
||||||
|
)
|
||||||
|
|
||||||
|
val client2TLSKeyStore = loadOrCreateKeyStore(client2TLSKeyStorePath, PASSWORD)
|
||||||
|
client2TLSKeyStore.addOrReplaceKey(
|
||||||
|
X509Utilities.CORDA_CLIENT_TLS,
|
||||||
|
client2TLSKeyPair.private,
|
||||||
|
PASSWORD.toCharArray(),
|
||||||
|
arrayOf(client2TLSCert, client2CACert, intermediateCACert, rootCACert))
|
||||||
|
// client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD)
|
||||||
|
|
||||||
|
val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD)
|
||||||
|
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert.cert)
|
||||||
|
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert.cert)
|
||||||
|
// trustStore.save(trustStorePath, PASSWORD)
|
||||||
|
|
||||||
|
val client1SSLContext = sslContext(client1TLSKeyStore, PASSWORD, trustStore)
|
||||||
|
val client2SSLContext = sslContext(client2TLSKeyStore, PASSWORD, trustStore)
|
||||||
|
|
||||||
|
val serverSocketFactory = client1SSLContext.serverSocketFactory
|
||||||
|
val clientSocketFactory = client2SSLContext.socketFactory
|
||||||
|
|
||||||
|
return Pair(serverSocketFactory, clientSocketFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildTLSSockets(
|
||||||
|
serverSocketFactory: SSLServerSocketFactory,
|
||||||
|
clientSocketFactory: SSLSocketFactory,
|
||||||
|
serverPort: Int = 0, // Use 0 to get first free socket.
|
||||||
|
clientPort: Int = 0, // Use 0 to get first free socket.
|
||||||
|
cipherSuitesServer: Array<String> = CORDA_TLS_CIPHER_SUITES,
|
||||||
|
cipherSuitesClient: Array<String> = CORDA_TLS_CIPHER_SUITES
|
||||||
|
): Pair<SSLServerSocket, SSLSocket> {
|
||||||
|
val serverSocket = serverSocketFactory.createServerSocket(serverPort) as SSLServerSocket // use 0 to get first free socket.
|
||||||
|
val serverParams = SSLParameters(cipherSuitesServer, arrayOf("TLSv1.2"))
|
||||||
|
serverParams.needClientAuth = true // Note that needClientAuth is requiring client authentication Vs wantClientAuth, in which client authentication is optional).
|
||||||
|
serverParams.endpointIdentificationAlgorithm = null // Reconfirm default no server name indication, use our own validator.
|
||||||
|
serverSocket.sslParameters = serverParams
|
||||||
|
serverSocket.useClientMode = false
|
||||||
|
|
||||||
|
val clientSocket = clientSocketFactory.createSocket() as SSLSocket
|
||||||
|
val clientParams = SSLParameters(cipherSuitesClient, arrayOf("TLSv1.2"))
|
||||||
|
clientParams.endpointIdentificationAlgorithm = null // Reconfirm default no server name indication, use our own validator.
|
||||||
|
clientSocket.sslParameters = clientParams
|
||||||
|
clientSocket.useClientMode = true
|
||||||
|
// We need to specify this explicitly because by default the client binds to 'localhost' and we want it to bind
|
||||||
|
// to whatever <hostname> resolves to(as that's what the server binds to). In particular on Debian <hostname>
|
||||||
|
// resolves to 127.0.1.1 instead of the external address of the interface, so the TLS handshake fails.
|
||||||
|
clientSocket.bind(InetSocketAddress(InetAddress.getLocalHost(), clientPort))
|
||||||
|
return Pair(serverSocket, clientSocket)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun testConnect(serverSocket: ServerSocket, clientSocket: SSLSocket, expectedCipherSuite: String) {
|
||||||
|
val lock = Object()
|
||||||
|
var done = false
|
||||||
|
var serverError = false
|
||||||
|
|
||||||
|
val serverThread = thread {
|
||||||
|
try {
|
||||||
|
val sslServerSocket = serverSocket.accept()
|
||||||
|
assertTrue(sslServerSocket.isConnected)
|
||||||
|
val serverInput = DataInputStream(sslServerSocket.inputStream)
|
||||||
|
val receivedString = serverInput.readUTF()
|
||||||
|
assertEquals("Hello World", receivedString)
|
||||||
|
synchronized(lock) {
|
||||||
|
done = true
|
||||||
|
lock.notifyAll()
|
||||||
|
}
|
||||||
|
sslServerSocket.close()
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
serverError = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientSocket.connect(InetSocketAddress(InetAddress.getLocalHost(), serverSocket.localPort))
|
||||||
|
assertTrue(clientSocket.isConnected)
|
||||||
|
assertEquals(expectedCipherSuite, clientSocket.session.cipherSuite)
|
||||||
|
|
||||||
|
// Timeout after 30 secs.
|
||||||
|
val output = DataOutputStream(clientSocket.outputStream)
|
||||||
|
output.writeUTF("Hello World")
|
||||||
|
var timeout = 0
|
||||||
|
synchronized(lock) {
|
||||||
|
while (!done) {
|
||||||
|
timeout++
|
||||||
|
if (timeout > 30) throw IOException("Timed out waiting for server to complete")
|
||||||
|
lock.wait(1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientSocket.close()
|
||||||
|
serverThread.join(1000)
|
||||||
|
assertFalse { serverError }
|
||||||
|
serverSocket.close()
|
||||||
|
assertTrue(done)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate an SSLContext from a KeyStore and a TrustStore.
|
||||||
|
private fun sslContext(sslKeyStore: KeyStore, sslKeyStorePassword: String, sslTrustStore: KeyStore) : SSLContext {
|
||||||
|
val context = SSLContext.getInstance("TLS")
|
||||||
|
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
|
||||||
|
// Requires the KeyStore password as well.
|
||||||
|
keyManagerFactory.init(sslKeyStore, sslKeyStorePassword.toCharArray())
|
||||||
|
val keyManagers = keyManagerFactory.keyManagers
|
||||||
|
val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||||
|
// Password is not required for TrustStore.
|
||||||
|
trustMgrFactory.init(sslTrustStore)
|
||||||
|
val trustManagers = trustMgrFactory.trustManagers
|
||||||
|
return context.apply { init(keyManagers, trustManagers, newSecureRandom()) }
|
||||||
|
}
|
||||||
|
}
|
@ -13,10 +13,11 @@ import net.corda.core.serialization.deserialize
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.node.serialization.KryoServerSerializationScheme
|
import net.corda.node.serialization.KryoServerSerializationScheme
|
||||||
import net.corda.node.services.config.createKeystoreForCordaNode
|
import net.corda.node.services.config.createKeystoreForCordaNode
|
||||||
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.nodeapi.internal.serialization.AllWhitelist
|
import net.corda.nodeapi.internal.serialization.AllWhitelist
|
||||||
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
|
||||||
import net.corda.nodeapi.internal.serialization.SerializationContextImpl
|
import net.corda.nodeapi.internal.serialization.SerializationContextImpl
|
||||||
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
|
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
import net.corda.testing.BOB_PUBKEY
|
import net.corda.testing.BOB_PUBKEY
|
||||||
@ -41,7 +42,6 @@ import java.security.PrivateKey
|
|||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
@ -49,7 +49,6 @@ import javax.net.ssl.*
|
|||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
|
|
||||||
class X509UtilitiesTest {
|
class X509UtilitiesTest {
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
@ -360,10 +359,16 @@ class X509UtilitiesTest {
|
|||||||
trustStorePassword: String
|
trustStorePassword: String
|
||||||
): KeyStore {
|
): KeyStore {
|
||||||
val rootCAKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val rootCAKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", organisation = "R3CEV", locality = "London", country = "GB"), rootCAKey)
|
val baseName = CordaX500Name(organisation = "R3CEV", locality = "London", country = "GB")
|
||||||
|
val rootCACert = X509Utilities.createSelfSignedCACertificate(baseName.copy(commonName = "Corda Node Root CA"), rootCAKey)
|
||||||
|
|
||||||
val intermediateCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val intermediateCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", organisation = "R3CEV", locality = "London", country = "GB"), intermediateCAKeyPair.public)
|
val intermediateCACert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.INTERMEDIATE_CA,
|
||||||
|
rootCACert,
|
||||||
|
rootCAKey,
|
||||||
|
baseName.copy(commonName = "Corda Node Intermediate CA"),
|
||||||
|
intermediateCAKeyPair.public)
|
||||||
|
|
||||||
val keyPass = keyPassword.toCharArray()
|
val keyPass = keyPassword.toCharArray()
|
||||||
val keyStore = loadOrCreateKeyStore(keyStoreFilePath, storePassword)
|
val keyStore = loadOrCreateKeyStore(keyStoreFilePath, storePassword)
|
||||||
@ -426,11 +431,10 @@ class X509UtilitiesTest {
|
|||||||
emptyMap(),
|
emptyMap(),
|
||||||
true,
|
true,
|
||||||
SerializationContext.UseCase.P2P)
|
SerializationContext.UseCase.P2P)
|
||||||
val certFactory = CertificateFactory.getInstance("X509")
|
|
||||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey)
|
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey)
|
||||||
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY)
|
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY)
|
||||||
val expected = certFactory.generateCertPath(listOf(certificate.cert, rootCACert.cert))
|
val expected = X509CertificateFactory().delegate.generateCertPath(listOf(certificate.cert, rootCACert.cert))
|
||||||
val serialized = expected.serialize(factory, context).bytes
|
val serialized = expected.serialize(factory, context).bytes
|
||||||
val actual: CertPath = serialized.deserialize(factory, context)
|
val actual: CertPath = serialized.deserialize(factory, context)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
|
@ -5,9 +5,9 @@ import net.corda.core.crypto.Crypto
|
|||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.node.utilities.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.node.utilities.getX509Certificate
|
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
||||||
import net.corda.node.utilities.loadKeyStore
|
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.rigorousMock
|
import net.corda.testing.rigorousMock
|
||||||
import net.corda.testing.testNodeConfiguration
|
import net.corda.testing.testNodeConfiguration
|
||||||
|
@ -15,6 +15,7 @@ import net.corda.finance.contracts.FixOf
|
|||||||
import net.corda.finance.contracts.asset.CASH
|
import net.corda.finance.contracts.asset.CASH
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
import net.corda.irs.flows.RatesFixFlow
|
import net.corda.irs.flows.RatesFixFlow
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
@ -22,8 +23,6 @@ import net.corda.testing.node.MockNetwork
|
|||||||
import net.corda.testing.node.MockNodeParameters
|
import net.corda.testing.node.MockNodeParameters
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
|
||||||
import net.corda.testing.node.createMockCordaService
|
import net.corda.testing.node.createMockCordaService
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.*
|
||||||
@ -69,7 +68,7 @@ class NodeInterestRatesTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
|
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock())
|
||||||
database.transaction {
|
database.transaction {
|
||||||
oracle = createMockCordaService(services, NodeInterestRates::Oracle)
|
oracle = createMockCordaService(services, NodeInterestRates::Oracle)
|
||||||
oracle.knownFixes = TEST_DATA
|
oracle.knownFixes = TEST_DATA
|
||||||
|
@ -6,24 +6,24 @@ import com.nhaarman.mockito_kotlin.doCallRealMethod
|
|||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
import net.corda.core.context.Actor
|
import net.corda.core.context.Actor
|
||||||
|
import net.corda.core.context.AuthServiceId
|
||||||
import net.corda.core.context.InvocationContext
|
import net.corda.core.context.InvocationContext
|
||||||
import net.corda.core.context.Origin
|
import net.corda.core.context.Origin
|
||||||
import net.corda.core.context.AuthServiceId
|
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.FlowStateMachine
|
import net.corda.core.internal.FlowStateMachine
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.seconds
|
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.services.api.StartedNodeServices
|
import net.corda.node.services.api.StartedNodeServices
|
||||||
import net.corda.node.services.config.CertChainPolicyConfig
|
import net.corda.node.services.config.CertChainPolicyConfig
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.services.config.VerifierType
|
import net.corda.node.services.config.VerifierType
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,7 +64,7 @@ fun testNodeConfiguration(
|
|||||||
doReturn(emptyList<User>()).whenever(it).rpcUsers
|
doReturn(emptyList<User>()).whenever(it).rpcUsers
|
||||||
doReturn(null).whenever(it).notary
|
doReturn(null).whenever(it).notary
|
||||||
doReturn(makeTestDataSourceProperties(myLegalName.organisation)).whenever(it).dataSourceProperties
|
doReturn(makeTestDataSourceProperties(myLegalName.organisation)).whenever(it).dataSourceProperties
|
||||||
doReturn(makeTestDatabaseProperties()).whenever(it).database
|
doReturn(DatabaseConfig()).whenever(it).database
|
||||||
doReturn("").whenever(it).emailAddress
|
doReturn("").whenever(it).emailAddress
|
||||||
doReturn("").whenever(it).exportJMXto
|
doReturn("").whenever(it).exportJMXto
|
||||||
doReturn(true).whenever(it).devMode
|
doReturn(true).whenever(it).devMode
|
||||||
|
@ -324,9 +324,11 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
override val serializationWhitelists: List<SerializationWhitelist>
|
override val serializationWhitelists: List<SerializationWhitelist>
|
||||||
get() = testSerializationWhitelists
|
get() = testSerializationWhitelists
|
||||||
private var dbCloser: (() -> Any?)? = null
|
private var dbCloser: (() -> Any?)? = null
|
||||||
override fun <T> initialiseDatabasePersistence(schemaService: SchemaService, insideTransaction: (CordaPersistence) -> T) = super.initialiseDatabasePersistence(schemaService) { database ->
|
override fun <T> initialiseDatabasePersistence(schemaService: SchemaService, identityService: IdentityService, insideTransaction: (CordaPersistence) -> T): T {
|
||||||
dbCloser = database::close
|
return super.initialiseDatabasePersistence(schemaService, identityService) { database ->
|
||||||
insideTransaction(database)
|
dbCloser = database::close
|
||||||
|
insideTransaction(database)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disableDBCloseOnStop() {
|
fun disableDBCloseOnStop() {
|
||||||
|
@ -21,6 +21,7 @@ import net.corda.node.internal.cordapp.CordappLoader
|
|||||||
import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage
|
import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage
|
||||||
import net.corda.node.services.api.VaultServiceInternal
|
import net.corda.node.services.api.VaultServiceInternal
|
||||||
import net.corda.node.services.api.WritableTransactionStorage
|
import net.corda.node.services.api.WritableTransactionStorage
|
||||||
|
import net.corda.node.services.config.DatabaseConfig
|
||||||
import net.corda.node.services.identity.InMemoryIdentityService
|
import net.corda.node.services.identity.InMemoryIdentityService
|
||||||
import net.corda.node.services.keys.freshCertificate
|
import net.corda.node.services.keys.freshCertificate
|
||||||
import net.corda.node.services.keys.getSigner
|
import net.corda.node.services.keys.getSigner
|
||||||
@ -76,22 +77,6 @@ open class MockServices(
|
|||||||
return props
|
return props
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Make properties appropriate for creating a Database for unit tests.
|
|
||||||
*
|
|
||||||
* @param key (optional) key of a database property to be set.
|
|
||||||
* @param value (optional) value of a database property to be set.
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
fun makeTestDatabaseProperties(key: String? = null, value: String? = null): Properties {
|
|
||||||
val props = Properties()
|
|
||||||
props.setProperty("transactionIsolationLevel", "repeatableRead") //for other possible values see net.corda.node.utilities.CordaPeristence.parserTransactionIsolationLevel(String)
|
|
||||||
if (key != null) {
|
|
||||||
props.setProperty(key, value)
|
|
||||||
}
|
|
||||||
return props
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of [InMemoryIdentityService] with [MOCK_IDENTITIES].
|
* Creates an instance of [InMemoryIdentityService] with [MOCK_IDENTITIES].
|
||||||
*/
|
*/
|
||||||
@ -107,9 +92,9 @@ open class MockServices(
|
|||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun makeTestDatabaseAndMockServices(keys: List<KeyPair> = listOf(MEGA_CORP_KEY),
|
fun makeTestDatabaseAndMockServices(keys: List<KeyPair> = listOf(MEGA_CORP_KEY),
|
||||||
createIdentityService: () -> IdentityService = { makeTestIdentityService() },
|
identityService: IdentityService = makeTestIdentityService(),
|
||||||
cordappPackages: List<String> = emptyList()): Pair<CordaPersistence, MockServices>
|
cordappPackages: List<String> = emptyList()): Pair<CordaPersistence, MockServices>
|
||||||
= makeTestDatabaseAndMockServices(keys, createIdentityService, cordappPackages, MEGA_CORP.name)
|
= makeTestDatabaseAndMockServices(keys, identityService, cordappPackages, MEGA_CORP.name)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes database and mock services appropriate for unit tests.
|
* Makes database and mock services appropriate for unit tests.
|
||||||
@ -120,17 +105,15 @@ open class MockServices(
|
|||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun makeTestDatabaseAndMockServices(keys: List<KeyPair> = listOf(MEGA_CORP_KEY),
|
fun makeTestDatabaseAndMockServices(keys: List<KeyPair> = listOf(MEGA_CORP_KEY),
|
||||||
createIdentityService: () -> IdentityService = { makeTestIdentityService() },
|
identityService: IdentityService = makeTestIdentityService(),
|
||||||
cordappPackages: List<String> = emptyList(),
|
cordappPackages: List<String> = emptyList(),
|
||||||
initialIdentityName: CordaX500Name): Pair<CordaPersistence, MockServices> {
|
initialIdentityName: CordaX500Name): Pair<CordaPersistence, MockServices> {
|
||||||
val cordappLoader = CordappLoader.createWithTestPackages(cordappPackages)
|
val cordappLoader = CordappLoader.createWithTestPackages(cordappPackages)
|
||||||
val dataSourceProps = makeTestDataSourceProperties()
|
val dataSourceProps = makeTestDataSourceProperties()
|
||||||
val databaseProperties = makeTestDatabaseProperties()
|
val database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService, NodeSchemaService(cordappLoader))
|
||||||
val identityServiceRef: IdentityService by lazy { createIdentityService() }
|
|
||||||
val database = configureDatabase(dataSourceProps, databaseProperties, { identityServiceRef }, NodeSchemaService(cordappLoader))
|
|
||||||
val mockService = database.transaction {
|
val mockService = database.transaction {
|
||||||
object : MockServices(cordappLoader, initialIdentityName = initialIdentityName, keys = *(keys.toTypedArray())) {
|
object : MockServices(cordappLoader, initialIdentityName = initialIdentityName, keys = *(keys.toTypedArray())) {
|
||||||
override val identityService: IdentityService = database.transaction { identityServiceRef }
|
override val identityService get() = identityService
|
||||||
override val vaultService: VaultServiceInternal = makeVaultService(database.hibernateConfig)
|
override val vaultService: VaultServiceInternal = makeVaultService(database.hibernateConfig)
|
||||||
|
|
||||||
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
|
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
|
||||||
|
@ -12,24 +12,22 @@ import net.corda.core.identity.Party
|
|||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.internal.cert
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.IdentityService
|
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER
|
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER
|
||||||
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
||||||
import net.corda.node.services.identity.InMemoryIdentityService
|
|
||||||
import net.corda.node.utilities.CertificateAndKeyPair
|
|
||||||
import net.corda.node.utilities.CertificateType
|
|
||||||
import net.corda.node.utilities.X509Utilities
|
|
||||||
import net.corda.nodeapi.config.SSLConfiguration
|
import net.corda.nodeapi.config.SSLConfiguration
|
||||||
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED
|
import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED
|
||||||
import org.mockito.internal.stubbing.answers.ThrowsException
|
import org.mockito.internal.stubbing.answers.ThrowsException
|
||||||
import org.mockito.stubbing.Answer
|
import org.mockito.stubbing.Answer
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,9 +129,8 @@ fun configureTestSSL(legalName: CordaX500Name = MEGA_CORP.name): SSLConfiguratio
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair = DEV_CA): PartyAndCertificate {
|
fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair = DEV_CA): PartyAndCertificate {
|
||||||
val certFactory = CertificateFactory.getInstance("X509")
|
|
||||||
val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey)
|
val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey)
|
||||||
val certPath = certFactory.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert))
|
val certPath = X509CertificateFactory().delegate.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert))
|
||||||
return PartyAndCertificate(certPath)
|
return PartyAndCertificate(certPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,10 +10,10 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.toX509CertHolder
|
import net.corda.core.internal.toX509CertHolder
|
||||||
import net.corda.node.utilities.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.node.utilities.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.node.utilities.getCertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.getCertificateAndKeyPair
|
||||||
import net.corda.node.utilities.loadKeyStore
|
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
|
Loading…
Reference in New Issue
Block a user