Merge commit '39434dcbecdd2cd656e40622530e84d02443b8e2' into chrisr3-os-merge

This commit is contained in:
Chris Rankin
2018-10-08 10:42:55 +01:00
48 changed files with 273 additions and 306 deletions

View File

@ -13,7 +13,7 @@ import net.corda.core.utilities.days
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.nodeapi.internal.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport import net.corda.nodeapi.internal.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
import net.corda.nodeapi.internal.PLATFORM_VERSION import net.corda.core.internal.PLATFORM_VERSION
import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
import java.time.Duration import java.time.Duration

View File

@ -1,7 +1,9 @@
gradlePluginsVersion=4.0.29 gradlePluginsVersion=4.0.29
kotlinVersion=1.2.51 kotlinVersion=1.2.51
# When adjusting platformVersion upwards please also modify CordaRPCClientConfiguration.minimumServerProtocolVersion \ # ***************************************************************#
# if there have been any RPC changes. Also please modify InternalMockNetwork.kt:MOCK_VERSION_INFO and NodeBasedTest.startNode # When incrementing platformVersion make sure to update #
# net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. #
# ***************************************************************#
platformVersion=4 platformVersion=4
guavaVersion=25.1-jre guavaVersion=25.1-jre
proguardVersion=6.0.3 proguardVersion=6.0.3

View File

@ -0,0 +1,68 @@
@file:JvmName("CryptoSignUtils")
package net.corda.deterministic.crypto
import net.corda.core.crypto.*
import net.corda.core.crypto.Crypto.findSignatureScheme
import net.corda.core.crypto.Crypto.isSupportedSignatureScheme
import net.corda.core.serialization.serialize
import java.security.*
/**
* This is a slightly modified copy of signing utils from net.corda.core.crypto.Crypto, which are normally removed from DJVM.
* However, we need those for TransactionSignatureTest.
*/
object CryptoSignUtils {
@JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class)
fun doSign(schemeCodeName: String, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
return doSign(findSignatureScheme(schemeCodeName), privateKey, clearData)
}
/**
* Generic way to sign [ByteArray] data with a [PrivateKey] and a known [Signature].
* @param signatureScheme a [SignatureScheme] object, retrieved from supported signature schemes, see [Crypto].
* @param privateKey the signer's [PrivateKey].
* @param clearData the data/message to be signed in [ByteArray] form (usually the Merkle root).
* @return the digital signature (in [ByteArray]) on the input message.
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
* @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key.
*/
@JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class)
fun doSign(signatureScheme: SignatureScheme, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
require(isSupportedSignatureScheme(signatureScheme)) {
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
}
require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" }
val signature = Signature.getInstance(signatureScheme.signatureName, signatureScheme.providerName)
signature.initSign(privateKey)
signature.update(clearData)
return signature.sign()
}
/**
* Generic way to sign [SignableData] objects with a [PrivateKey].
* [SignableData] is a wrapper over the transaction's id (Merkle root) in order to attach extra information, such as
* a timestamp or partial and blind signature indicators.
* @param keyPair the signer's [KeyPair].
* @param signableData a [SignableData] object that adds extra information to a transaction.
* @return a [TransactionSignature] object than contains the output of a successful signing, signer's public key and
* the signature metadata.
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
* @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key.
*/
@JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class)
fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature {
val sigKey: SignatureScheme = findSignatureScheme(keyPair.private)
val sigMetaData: SignatureScheme = findSignatureScheme(keyPair.public)
require(sigKey == sigMetaData) {
"Metadata schemeCodeName: ${sigMetaData.schemeCodeName} is not aligned with the key type: ${sigKey.schemeCodeName}."
}
val signatureBytes = doSign(sigKey.schemeCodeName, keyPair.private, signableData.serialize().bytes)
return TransactionSignature(signatureBytes, keyPair.public, signableData.signatureMetadata)
}
}

View File

@ -37,7 +37,7 @@ class TransactionSignatureTest {
// Sign the meta object. // Sign the meta object.
val transactionSignature: TransactionSignature = CheatingSecurityProvider().use { val transactionSignature: TransactionSignature = CheatingSecurityProvider().use {
keyPair.sign(signableData) CryptoSignUtils.doSign(keyPair, signableData)
} }
// Check auto-verification. // Check auto-verification.
@ -52,7 +52,7 @@ class TransactionSignatureTest {
fun `Signature metadata full failure clearData has changed`() { fun `Signature metadata full failure clearData has changed`() {
val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID)) val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
val transactionSignature = CheatingSecurityProvider().use { val transactionSignature = CheatingSecurityProvider().use {
keyPair.sign(signableData) CryptoSignUtils.doSign(keyPair, signableData)
} }
Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature) Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature)
} }
@ -137,7 +137,7 @@ class TransactionSignatureTest {
private fun signOneTx(txId: SecureHash, keyPair: KeyPair): TransactionSignature { private fun signOneTx(txId: SecureHash, keyPair: KeyPair): TransactionSignature {
val signableData = SignableData(txId, SignatureMetadata(3, Crypto.findSignatureScheme(keyPair.public).schemeNumberID)) val signableData = SignableData(txId, SignatureMetadata(3, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
return CheatingSecurityProvider().use { return CheatingSecurityProvider().use {
keyPair.sign(signableData) CryptoSignUtils.doSign(keyPair, signableData)
} }
} }
} }

View File

@ -64,7 +64,6 @@ dependencies {
testCompile "com.google.guava:guava-testlib:$guava_version" testCompile "com.google.guava:guava-testlib:$guava_version"
// Bring in the MockNode infrastructure for writing protocol unit tests. // Bring in the MockNode infrastructure for writing protocol unit tests.
testCompile project(":node")
testCompile project(":node-driver") testCompile project(":node-driver")
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

View File

@ -2,6 +2,7 @@ package net.corda.core.crypto
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.core.StubOutForDJVM
import net.corda.core.crypto.internal.* import net.corda.core.crypto.internal.*
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.i2p.crypto.eddsa.EdDSAEngine import net.i2p.crypto.eddsa.EdDSAEngine
@ -23,6 +24,7 @@ import org.bouncycastle.asn1.sec.SECObjectIdentifiers
import org.bouncycastle.asn1.x509.AlgorithmIdentifier import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers import org.bouncycastle.asn1.x9.X9ObjectIdentifiers
import org.bouncycastle.crypto.CryptoServicesRegistrar
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateKey
@ -381,6 +383,7 @@ object Crypto {
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@DeleteForDJVM
@JvmStatic @JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun doSign(privateKey: PrivateKey, clearData: ByteArray): ByteArray = doSign(findSignatureScheme(privateKey), privateKey, clearData) fun doSign(privateKey: PrivateKey, clearData: ByteArray): ByteArray = doSign(findSignatureScheme(privateKey), privateKey, clearData)
@ -395,6 +398,7 @@ object Crypto {
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@DeleteForDJVM
@JvmStatic @JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun doSign(schemeCodeName: String, privateKey: PrivateKey, clearData: ByteArray): ByteArray { fun doSign(schemeCodeName: String, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
@ -411,6 +415,7 @@ object Crypto {
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@DeleteForDJVM
@JvmStatic @JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun doSign(signatureScheme: SignatureScheme, privateKey: PrivateKey, clearData: ByteArray): ByteArray { fun doSign(signatureScheme: SignatureScheme, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
@ -419,7 +424,16 @@ object Crypto {
} }
require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" } require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" }
val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName]) val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
// Note that deterministic signature schemes, such as EdDSA, do not require extra randomness, but we have to
// ensure that non-deterministic algorithms (i.e., ECDSA) use non-blocking SecureRandom implementations (if possible).
// TODO consider updating this when the related BC issue for Sphincs is fixed.
if (signatureScheme != SPHINCS256_SHA256) {
signature.initSign(privateKey, newSecureRandom())
} else {
// Special handling for Sphincs, due to a BC implementation issue.
// As Sphincs is deterministic, it does not require RNG input anyway.
signature.initSign(privateKey) signature.initSign(privateKey)
}
signature.update(clearData) signature.update(clearData)
return signature.sign() return signature.sign()
} }
@ -436,6 +450,7 @@ object Crypto {
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@DeleteForDJVM
@JvmStatic @JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature { fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature {
@ -794,7 +809,6 @@ object Crypto {
* @return a new [KeyPair] from an entropy input. * @return a new [KeyPair] from an entropy input.
* @throws IllegalArgumentException if the requested signature scheme is not supported for KeyPair generation using an entropy input. * @throws IllegalArgumentException if the requested signature scheme is not supported for KeyPair generation using an entropy input.
*/ */
@DeleteForDJVM
@JvmStatic @JvmStatic
fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair { fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
return when (signatureScheme) { return when (signatureScheme) {
@ -810,7 +824,6 @@ object Crypto {
* @param entropy a [BigInteger] value. * @param entropy a [BigInteger] value.
* @return a new [KeyPair] from an entropy input. * @return a new [KeyPair] from an entropy input.
*/ */
@DeleteForDJVM
@JvmStatic @JvmStatic
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy) fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
@ -1017,5 +1030,15 @@ object Crypto {
@JvmStatic @JvmStatic
fun registerProviders() { fun registerProviders() {
providerMap providerMap
// Adding our non-blocking newSecureRandom as default for any BouncyCastle operations
// (applies only when a SecureRandom is not specifically defined, i.e., if we call
// signature.initSign(privateKey) instead of signature.initSign(privateKey, newSecureRandom()
// for a BC algorithm, i.e., ECDSA).
setBouncyCastleRNG()
}
@StubOutForDJVM
private fun setBouncyCastleRNG() {
CryptoServicesRegistrar.setSecureRandom(newSecureRandom())
} }
} }

View File

@ -28,6 +28,7 @@ import java.security.cert.X509Certificate
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@DeleteForDJVM
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature(Crypto.doSign(this, bytesToSign)) fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature(Crypto.doSign(this, bytesToSign))
@ -40,6 +41,7 @@ fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@DeleteForDJVM
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey { fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
return DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes) return DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes)
@ -53,10 +55,12 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignat
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@DeleteForDJVM
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun KeyPair.sign(bytesToSign: ByteArray): DigitalSignature.WithKey = private.sign(bytesToSign, public) fun KeyPair.sign(bytesToSign: ByteArray): DigitalSignature.WithKey = private.sign(bytesToSign, public)
/** Helper function to sign the bytes of [bytesToSign] with a key pair. */ /** Helper function to sign the bytes of [bytesToSign] with a key pair. */
@DeleteForDJVM
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun KeyPair.sign(bytesToSign: OpaqueBytes): DigitalSignature.WithKey = sign(bytesToSign.bytes) fun KeyPair.sign(bytesToSign: OpaqueBytes): DigitalSignature.WithKey = sign(bytesToSign.bytes)
@ -68,6 +72,7 @@ fun KeyPair.sign(bytesToSign: OpaqueBytes): DigitalSignature.WithKey = sign(byte
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@DeleteForDJVM
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun KeyPair.sign(signableData: SignableData): TransactionSignature = Crypto.doSign(this, signableData) fun KeyPair.sign(signableData: SignableData): TransactionSignature = Crypto.doSign(this, signableData)
@ -151,7 +156,6 @@ fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
* @param entropy a [BigInteger] value. * @param entropy a [BigInteger] value.
* @return a deterministically generated [KeyPair] for the [Crypto.DEFAULT_SIGNATURE_SCHEME]. * @return a deterministically generated [KeyPair] for the [Crypto.DEFAULT_SIGNATURE_SCHEME].
*/ */
@DeleteForDJVM
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy) fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)
/** /**

View File

@ -17,6 +17,8 @@ import org.slf4j.MDC
// *Internal* Corda-specific utilities // *Internal* Corda-specific utilities
const val PLATFORM_VERSION = 4
fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) { fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) {
val currentMinPlatformVersion = networkParameters.minimumPlatformVersion val currentMinPlatformVersion = networkParameters.minimumPlatformVersion
if (currentMinPlatformVersion < requiredMinPlatformVersion) { if (currentMinPlatformVersion < requiredMinPlatformVersion) {

View File

@ -457,11 +457,13 @@ $trustAnchor""", e, this, e.index)
} }
} }
@DeleteForDJVM
inline fun <T : Any> T.signWithCert(signer: (SerializedBytes<T>) -> DigitalSignatureWithCert): SignedDataWithCert<T> { inline fun <T : Any> T.signWithCert(signer: (SerializedBytes<T>) -> DigitalSignatureWithCert): SignedDataWithCert<T> {
val serialised = serialize() val serialised = serialize()
return SignedDataWithCert(serialised, signer(serialised)) return SignedDataWithCert(serialised, signer(serialised))
} }
@DeleteForDJVM
fun <T : Any> T.signWithCert(privateKey: PrivateKey, certificate: X509Certificate): SignedDataWithCert<T> { fun <T : Any> T.signWithCert(privateKey: PrivateKey, certificate: X509Certificate): SignedDataWithCert<T> {
return signWithCert { return signWithCert {
val signature = Crypto.doSign(privateKey, it.bytes) val signature = Crypto.doSign(privateKey, it.bytes)
@ -469,10 +471,12 @@ fun <T : Any> T.signWithCert(privateKey: PrivateKey, certificate: X509Certificat
} }
} }
@DeleteForDJVM
inline fun <T : Any> SerializedBytes<T>.sign(signer: (SerializedBytes<T>) -> DigitalSignature.WithKey): SignedData<T> { inline fun <T : Any> SerializedBytes<T>.sign(signer: (SerializedBytes<T>) -> DigitalSignature.WithKey): SignedData<T> {
return SignedData(this, signer(this)) return SignedData(this, signer(this))
} }
@DeleteForDJVM
fun <T : Any> SerializedBytes<T>.sign(keyPair: KeyPair): SignedData<T> = SignedData(this, keyPair.sign(this.bytes)) fun <T : Any> SerializedBytes<T>.sign(keyPair: KeyPair): SignedData<T> = SignedData(this, keyPair.sign(this.bytes))
fun ByteBuffer.copyBytes(): ByteArray = ByteArray(remaining()).also { get(it) } fun ByteBuffer.copyBytes(): ByteArray = ByteArray(remaining()).also { get(it) }

View File

@ -42,8 +42,7 @@ data class CordappImpl(
// TODO Why a seperate Info class and not just have the fields directly in CordappImpl? // TODO Why a seperate Info class and not just have the fields directly in CordappImpl?
data class Info(val shortName: String, val vendor: String, val version: String, val minimumPlatformVersion: Int, val targetPlatformVersion: Int) { data class Info(val shortName: String, val vendor: String, val version: String, val minimumPlatformVersion: Int, val targetPlatformVersion: Int) {
companion object { companion object {
private const val UNKNOWN_VALUE = "Unknown" const val UNKNOWN_VALUE = "Unknown"
val UNKNOWN = Info(UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE, 1, 1) val UNKNOWN = Info(UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE, 1, 1)
} }

View File

@ -91,6 +91,7 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
return descriptions return descriptions
} }
@DeleteForDJVM
@VisibleForTesting @VisibleForTesting
fun withAdditionalSignature(keyPair: KeyPair, signatureMetadata: SignatureMetadata): SignedTransaction { fun withAdditionalSignature(keyPair: KeyPair, signatureMetadata: SignatureMetadata): SignedTransaction {
val signableData = SignableData(tx.id, signatureMetadata) val signableData = SignableData(tx.id, signatureMetadata)

View File

@ -10,11 +10,9 @@ import net.corda.core.utilities.getOrThrow
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.smoketesting.NodeConfig import net.corda.smoketesting.NodeConfig
import net.corda.smoketesting.NodeProcess import net.corda.smoketesting.NodeProcess
import net.corda.testing.common.internal.ProjectStructure
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
import java.nio.file.Paths import java.nio.file.Paths
import java.util.*
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import java.util.jar.JarFile import java.util.jar.JarFile
import kotlin.streams.toList import kotlin.streams.toList
@ -23,12 +21,6 @@ class NodeVersioningTest {
private companion object { private companion object {
val user = User("user1", "test", permissions = setOf("ALL")) val user = User("user1", "test", permissions = setOf("ALL"))
val port = AtomicInteger(15100) val port = AtomicInteger(15100)
val expectedPlatformVersion = (ProjectStructure.projectRootDir / "constants.properties").read {
val constants = Properties()
constants.load(it)
constants.getProperty("platformVersion").toInt()
}
} }
private val factory = NodeProcess.Factory() private val factory = NodeProcess.Factory()
@ -45,7 +37,7 @@ class NodeVersioningTest {
@Test @Test
fun `platform version in manifest file`() { fun `platform version in manifest file`() {
val manifest = JarFile(factory.cordaJar.toFile()).manifest val manifest = JarFile(factory.cordaJar.toFile()).manifest
assertThat(manifest.mainAttributes.getValue("Corda-Platform-Version").toInt()).isEqualTo(expectedPlatformVersion) assertThat(manifest.mainAttributes.getValue("Corda-Platform-Version").toInt()).isEqualTo(PLATFORM_VERSION)
} }
@Test @Test
@ -60,9 +52,9 @@ class NodeVersioningTest {
factory.create(aliceConfig).use { alice -> factory.create(aliceConfig).use { alice ->
alice.connect().use { alice.connect().use {
val rpc = it.proxy val rpc = it.proxy
assertThat(rpc.protocolVersion).isEqualTo(expectedPlatformVersion) assertThat(rpc.protocolVersion).isEqualTo(PLATFORM_VERSION)
assertThat(rpc.nodeInfo().platformVersion).isEqualTo(expectedPlatformVersion) assertThat(rpc.nodeInfo().platformVersion).isEqualTo(PLATFORM_VERSION)
assertThat(rpc.startFlow(NodeVersioningTest::GetPlatformVersionFlow).returnValue.getOrThrow()).isEqualTo(expectedPlatformVersion) assertThat(rpc.startFlow(NodeVersioningTest::GetPlatformVersionFlow).returnValue.getOrThrow()).isEqualTo(PLATFORM_VERSION)
} }
} }
} }

View File

@ -52,7 +52,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
@Test @Test
fun `2 parties contract upgrade`() { fun `2 parties contract upgrade`() {
// Create dummy contract. // Create dummy contract.
val signedByA = aliceNode.signDummyContract(alice.ref(1),0, bob.ref(1)) val signedByA = aliceNode.signDummyContract(alice.ref(1), 0, bob.ref(1))
val stx = bobNode.addSignatureTo(signedByA) val stx = bobNode.addSignatureTo(signedByA)
aliceNode.finalise(stx, bob) aliceNode.finalise(stx, bob)

View File

@ -24,14 +24,14 @@ import org.junit.After
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
// A dummy reference state contract. // A dummy reference state contract.
internal class RefState : Contract { internal class RefState : Contract {
companion object { companion object {
val CONTRACT_ID = "net.corda.core.flows.RefState" const val CONTRACT_ID = "net.corda.core.flows.RefState"
} }
override fun verify(tx: LedgerTransaction) = Unit override fun verify(tx: LedgerTransaction) = Unit
data class State(val owner: Party, val version: Int = 0, override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState { data class State(val owner: Party, val version: Int = 0, override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState {
override val participants: List<AbstractParty> get() = listOf(owner) override val participants: List<AbstractParty> get() = listOf(owner)
fun update() = copy(version = version + 1) fun update() = copy(version = version + 1)
@ -46,34 +46,32 @@ internal class CreateRefState : FlowLogic<SignedTransaction>() {
@Suspendable @Suspendable
override fun call(): SignedTransaction { override fun call(): SignedTransaction {
val notary = serviceHub.networkMapCache.notaryIdentities.first() val notary = serviceHub.networkMapCache.notaryIdentities.first()
return subFlow(FinalityFlow( val stx = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
transaction = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
addOutputState(RefState.State(ourIdentity), RefState.CONTRACT_ID) addOutputState(RefState.State(ourIdentity), RefState.CONTRACT_ID)
addCommand(RefState.Create(), listOf(ourIdentity.owningKey)) addCommand(RefState.Create(), listOf(ourIdentity.owningKey))
}) })
)) return subFlow(FinalityFlow(stx))
} }
} }
// A flow to update a specific reference state. // A flow to update a specific reference state.
internal class UpdateRefState(val stateAndRef: StateAndRef<ContractState>) : FlowLogic<SignedTransaction>() { internal class UpdateRefState(private val stateAndRef: StateAndRef<ContractState>) : FlowLogic<SignedTransaction>() {
@Suspendable @Suspendable
override fun call(): SignedTransaction { override fun call(): SignedTransaction {
val notary = serviceHub.networkMapCache.notaryIdentities.first() val notary = serviceHub.networkMapCache.notaryIdentities.first()
return subFlow(FinalityFlow( val stx = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
transaction = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
addInputState(stateAndRef) addInputState(stateAndRef)
addOutputState((stateAndRef.state.data as RefState.State).update(), RefState.CONTRACT_ID) addOutputState((stateAndRef.state.data as RefState.State).update(), RefState.CONTRACT_ID)
addCommand(RefState.Update(), listOf(ourIdentity.owningKey)) addCommand(RefState.Update(), listOf(ourIdentity.owningKey))
}) })
)) return subFlow(FinalityFlow(stx))
} }
} }
// A set of flows to share a stateref with all other nodes in the mock network. // A set of flows to share a stateref with all other nodes in the mock network.
internal object ShareRefState { internal object ShareRefState {
@InitiatingFlow @InitiatingFlow
class Initiator(val stateAndRef: StateAndRef<ContractState>) : FlowLogic<Unit>() { class Initiator(private val stateAndRef: StateAndRef<ContractState>) : FlowLogic<Unit>() {
@Suspendable @Suspendable
override fun call() { override fun call() {
val sessions = serviceHub.networkMapCache.allNodes.flatMap { it.legalIdentities }.map { initiateFlow(it) } val sessions = serviceHub.networkMapCache.allNodes.flatMap { it.legalIdentities }.map { initiateFlow(it) }
@ -85,7 +83,7 @@ internal object ShareRefState {
} }
@InitiatedBy(ShareRefState.Initiator::class) @InitiatedBy(ShareRefState.Initiator::class)
class Responder(val otherSession: FlowSession) : FlowLogic<Unit>() { class Responder(private val otherSession: FlowSession) : FlowLogic<Unit>() {
@Suspendable @Suspendable
override fun call() { override fun call() {
logger.info("Receiving dependencies.") logger.info("Receiving dependencies.")
@ -99,7 +97,7 @@ internal object ShareRefState {
} }
// A flow to use a reference state in another transaction. // A flow to use a reference state in another transaction.
internal class UseRefState(val linearId: UniqueIdentifier) : FlowLogic<SignedTransaction>() { internal class UseRefState(private val linearId: UniqueIdentifier) : FlowLogic<SignedTransaction>() {
@Suspendable @Suspendable
override fun call(): SignedTransaction { override fun call(): SignedTransaction {
val notary = serviceHub.networkMapCache.notaryIdentities.first() val notary = serviceHub.networkMapCache.notaryIdentities.first()
@ -108,14 +106,12 @@ internal class UseRefState(val linearId: UniqueIdentifier) : FlowLogic<SignedTra
relevancyStatus = Vault.RelevancyStatus.ALL relevancyStatus = Vault.RelevancyStatus.ALL
) )
val referenceState = serviceHub.vaultService.queryBy<ContractState>(query).states.single() val referenceState = serviceHub.vaultService.queryBy<ContractState>(query).states.single()
return subFlow(FinalityFlow( val stx = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
transaction = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
@Suppress("DEPRECATION") // To be removed when feature is finalised.
addReferenceState(referenceState.referenced()) addReferenceState(referenceState.referenced())
addOutputState(DummyState(), DummyContract.PROGRAM_ID) addOutputState(DummyState(), DummyContract.PROGRAM_ID)
addCommand(DummyContract.Commands.Create(), listOf(ourIdentity.owningKey)) addCommand(DummyContract.Commands.Create(), listOf(ourIdentity.owningKey))
}) })
)) return subFlow(FinalityFlow(stx))
} }
} }

View File

@ -1,37 +1,42 @@
package net.corda.core.flows.mixins package net.corda.core.flows.mixins
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import com.natpryce.hamkrest.MatchResult
import com.natpryce.hamkrest.Matcher import com.natpryce.hamkrest.Matcher
import com.natpryce.hamkrest.equalTo import com.natpryce.hamkrest.equalTo
import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.FlowStateMachine
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.FlowHandle
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.node.internal.TestStartedNode import net.corda.testing.node.internal.TestStartedNode
interface WithFinality : WithMockNet { interface WithFinality : WithMockNet {
//region Operations //region Operations
fun TestStartedNode.finalise(stx: SignedTransaction, vararg additionalParties: Party) = fun TestStartedNode.finalise(stx: SignedTransaction, vararg additionalParties: Party): FlowStateMachine<SignedTransaction> {
startFlowAndRunNetwork(FinalityFlow(stx, additionalParties.toSet())) return startFlowAndRunNetwork(FinalityFlow(stx, additionalParties.toSet()))
}
fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction) = fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction): SignedTransaction {
services.validatedTransactions.getTransaction(stx.id)!! return services.validatedTransactions.getTransaction(stx.id)!!
}
fun CordaRPCOps.finalise(stx: SignedTransaction, vararg parties: Party) = fun CordaRPCOps.finalise(stx: SignedTransaction, vararg parties: Party): FlowHandle<SignedTransaction> {
startFlow(::FinalityInvoker, stx, parties.toSet()) return startFlow(::FinalityInvoker, stx, parties.toSet()).andRunNetwork()
.andRunNetwork() }
//endregion //endregion
//region Matchers //region Matchers
fun visibleTo(other: TestStartedNode) = object : Matcher<SignedTransaction> { fun visibleTo(other: TestStartedNode) = object : Matcher<SignedTransaction> {
override val description = "has a transaction visible to ${other.info.singleIdentity()}" override val description = "has a transaction visible to ${other.info.singleIdentity()}"
override fun invoke(actual: SignedTransaction) = override fun invoke(actual: SignedTransaction): MatchResult {
equalTo(actual)(other.getValidatedTransaction(actual)) return equalTo(actual)(other.getValidatedTransaction(actual))
}
} }
//endregion //endregion

View File

@ -1,42 +1,4 @@
![Corda](https://www.corda.net/wp-content/uploads/2016/11/fg005_corda_b.png) # Reference states
# Design
DOCUMENT MANAGEMENT
---
Design documents should follow the standard GitHub version management and pull request (PR) review workflow mechanism.
## Document Control
| Title | |
| -------------------- | ---------------------------------------- |
| Date | 27 March 2018 |
| Author | Roger Willis |
| Distribution | Matthew Nesbit, Rick Parker |
| Corda target version | open source and enterprise |
| JIRA reference | No JIRA's raised. |
## Approvals
#### Document Sign-off
| Author | |
| ----------------- | ---------------------------------------- |
| Reviewer(s) | (GitHub PR reviewers) |
| Final approver(s) | (GitHub PR approver(s) from Design Approval Board) |
#### Design Decisions
There's only really one way to do this that satisfies our requirements - add a new input `StateAndRef` component group to the transaction classes. Other possible solutions are discussed below and why they are inappropriate.
## Document History
* [Version 1](https://github.com/corda/enterprise/blob/779aaefa5c09a6a28191496dd45252b6e207b7f7/docs/source/design/reference-states/design.md) (Received comments from Richard Brown and Mark Oldfield).
* [Version 2](https://github.com/corda/enterprise/blob/a87f1dcb22ba15081b0da92ba1501b6b81ae2baf/docs/source/design/reference-states/design.md) (Version presented to the DRB).
HIGH LEVEL DESIGN
---
## Overview ## Overview
@ -44,13 +6,15 @@ See a prototype implementation here: https://github.com/corda/corda/pull/2889
There is an increasing need for Corda to support use-cases which require reference data which is issued and updated by specific parties, but available for use, by reference, in transactions built by other parties. There is an increasing need for Corda to support use-cases which require reference data which is issued and updated by specific parties, but available for use, by reference, in transactions built by other parties.
Why is this type of reference data required? Why is this type of reference data required? A key benefit of blockchain systems is that everybody is sure they see the
same as their counterpart - and for this to work in situations where accurate processing depends on reference data
requires everybody to be operating on the same reference data. This, in turn, requires any given piece of reference data
to be uniquely identifiable and, requires that any given transaction must be certain to be operating on the most current
version of that reference data. In cases where the latter condition applies, only the notary can attest to this fact and
this, in turn, means the reference data must be in the form of an unconsumed state.
1. A key benefit of blockchain systems is that everybody is sure they see the same as their counterpart - and for this to work in situations where accurate processing depends on reference data requires everybody to be operating on the same reference data. This document outlines the approach for adding support for this type of reference data to the Corda transaction model
2. This, in turn, requires any given piece of reference data to be uniquely identifiable and, requires that any given transaction must be certain to be operating on the most current version of that reference data. via a new approach called "reference input states".
3. In cases where the latter condition applies, only the notary can attest to this fact and this, in turn, means the reference data must be in the form of an unconsumed state.
This document outlines the approach for adding support for this type of reference data to the Corda transaction model via a new approach called "reference input states".
## Background ## Background
@ -71,16 +35,10 @@ However, neither of these solutions are optimal for reasons discussed in later s
As such, this design document introduces the concept of a "reference input state" which is a better way to serve "periodically changing subjective reference data" on Corda. As such, this design document introduces the concept of a "reference input state" which is a better way to serve "periodically changing subjective reference data" on Corda.
*What is a "reference input state"?*
A reference input state is a `ContractState` which can be referred to in a transaction by the contracts of input and output states but whose contract is not executed as part of the transaction verification process and is not consumed when the transaction is committed to the ledger but _is_ checked for "current-ness". In other words, the contract logic isn't run for the referencing transaction only. It's still a normal state when it occurs in an input or output position. A reference input state is a `ContractState` which can be referred to in a transaction by the contracts of input and output states but whose contract is not executed as part of the transaction verification process and is not consumed when the transaction is committed to the ledger but _is_ checked for "current-ness". In other words, the contract logic isn't run for the referencing transaction only. It's still a normal state when it occurs in an input or output position.
*What will reference input states enable?*
Reference data states will enable many parties to "reuse" the same state in their transactions as reference data whilst still allowing the reference data state owner the capability to update the state. When data distribution groups are available then reference state owners will be able to distribute updates to subscribers more easily. Currently, distribution would have to be performed manually. Reference data states will enable many parties to "reuse" the same state in their transactions as reference data whilst still allowing the reference data state owner the capability to update the state. When data distribution groups are available then reference state owners will be able to distribute updates to subscribers more easily. Currently, distribution would have to be performed manually.
*Roughly, how are reference input states implemented?*
Reference input states can be added to Corda by adding a new transaction component group that allows developers to add reference data `ContractState`s that are not consumed when the transaction is committed to the ledger. This eliminates the problems created by long chains of provenance, contention, and allows developers to use any `ContractState` for reference data. The feature should allow developers to add _any_ `ContractState` available in their vault, even if they are not a `participant` whilst nevertheless providing a guarantee that the state being used is the most recent version of that piece of information. Reference input states can be added to Corda by adding a new transaction component group that allows developers to add reference data `ContractState`s that are not consumed when the transaction is committed to the ledger. This eliminates the problems created by long chains of provenance, contention, and allows developers to use any `ContractState` for reference data. The feature should allow developers to add _any_ `ContractState` available in their vault, even if they are not a `participant` whilst nevertheless providing a guarantee that the state being used is the most recent version of that piece of information.
## Scope ## Scope
@ -93,14 +51,6 @@ Non-goals (eg. out of scope)
* Data distribution groups are required to realise the full potential of reference data states. This design document does not discuss data distribution groups. * Data distribution groups are required to realise the full potential of reference data states. This design document does not discuss data distribution groups.
## Timeline
This work should be ready by the release of Corda V4. There is a prototype which is currently good enough for one of the firm's most critical projects, but more work is required:
* to assess the impact of this change
* write tests
* write documentation
## Requirements ## Requirements
1. Reference states can be any `ContractState` created by one or more `Party`s and subsequently updated by those `Party`s. E.g. `Cash`, `CompanyData`, `InterestRateSwap`, `FxRate`. Reference states can be `OwnableState`s, but it's more likely they will be `LinearState`s. 1. Reference states can be any `ContractState` created by one or more `Party`s and subsequently updated by those `Party`s. E.g. `Cash`, `CompanyData`, `InterestRateSwap`, `FxRate`. Reference states can be `OwnableState`s, but it's more likely they will be `LinearState`s.
@ -223,17 +173,3 @@ It does the following:
5. Then it re-does the initial calculation, re-creates the subflow with the new resolved tips using the factory, and re-runs it as a new subflow. 5. Then it re-does the initial calculation, re-creates the subflow with the new resolved tips using the factory, and re-runs it as a new subflow.
Care must be taken to handle progress tracking correctly in case of loops. Care must be taken to handle progress tracking correctly in case of loops.
## Complementary solutions
See discussion of alternative approaches above in the "design decisions" section.
## Final recommendation
Proceed to Implementation
TECHNICAL DESIGN
---
* Summary of changes to be included.
* Summary of outstanding issues to be included.

View File

@ -66,11 +66,11 @@ public class IOUFlow extends FlowLogic<Void> {
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder); final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Creating a session with the other party. // Creating a session with the other party.
FlowSession otherpartySession = initiateFlow(otherParty); FlowSession otherPartySession = initiateFlow(otherParty);
// Obtaining the counterparty's signature. // Obtaining the counterparty's signature.
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow( SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, ImmutableList.of(otherpartySession), CollectSignaturesFlow.tracker())); signedTx, ImmutableList.of(otherPartySession), CollectSignaturesFlow.tracker()));
// Finalising the transaction. // Finalising the transaction.
subFlow(new FinalityFlow(fullySignedTx)); subFlow(new FinalityFlow(fullySignedTx));

View File

@ -1,3 +1,5 @@
@file:Suppress("MemberVisibilityCanBePrivate")
package net.corda.docs.kotlin.tutorial.helloworld package net.corda.docs.kotlin.tutorial.helloworld
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState

View File

@ -1,3 +1,5 @@
@file:Suppress("MemberVisibilityCanBePrivate")
package net.corda.docs.kotlin.tutorial.twoparty package net.corda.docs.kotlin.tutorial.twoparty
// DOCSTART 01 // DOCSTART 01
@ -48,10 +50,10 @@ class IOUFlow(val iouValue: Int,
val signedTx = serviceHub.signInitialTransaction(txBuilder) val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Creating a session with the other party. // Creating a session with the other party.
val otherpartySession = initiateFlow(otherParty) val otherPartySession = initiateFlow(otherParty)
// Obtaining the counterparty's signature. // Obtaining the counterparty's signature.
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, listOf(otherpartySession), CollectSignaturesFlow.tracker())) val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, listOf(otherPartySession), CollectSignaturesFlow.tracker()))
// Finalising the transaction. // Finalising the transaction.
subFlow(FinalityFlow(fullySignedTx)) subFlow(FinalityFlow(fullySignedTx))

View File

@ -1,3 +1,5 @@
@file:Suppress("unused")
package net.corda.docs.kotlin.tutorial.twoparty package net.corda.docs.kotlin.tutorial.twoparty
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable

View File

@ -1,4 +1,4 @@
package net.corda.docs package net.corda.docs.kotlin
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
@ -8,7 +8,6 @@ import net.corda.core.node.services.vault.*
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.docs.java.tutorial.helloworld.IOUFlow import net.corda.docs.java.tutorial.helloworld.IOUFlow
import net.corda.docs.kotlin.TopupIssuerFlow
import net.corda.finance.* import net.corda.finance.*
import net.corda.finance.contracts.getCashBalances import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow

View File

@ -1,18 +1,12 @@
package net.corda.docs package net.corda.docs.kotlin
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.docs.kotlin.ForeignExchangeFlow import net.corda.finance.*
import net.corda.docs.kotlin.ForeignExchangeRemoteFlow
import net.corda.finance.DOLLARS
import net.corda.finance.GBP
import net.corda.finance.POUNDS
import net.corda.finance.USD
import net.corda.finance.contracts.getCashBalances import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.issuedBy
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.StartedMockNode import net.corda.testing.node.StartedMockNode

View File

@ -1,4 +1,4 @@
package net.corda.docs package net.corda.docs.kotlin
import net.corda.core.contracts.LinearState import net.corda.core.contracts.LinearState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
@ -9,10 +9,6 @@ import net.corda.core.node.services.queryBy
import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.docs.kotlin.SubmitCompletionFlow
import net.corda.docs.kotlin.SubmitTradeApprovalFlow
import net.corda.docs.kotlin.TradeApprovalContract
import net.corda.docs.kotlin.WorkflowState
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork

View File

@ -73,7 +73,8 @@ annotation out will lead to some very weird error messages!
There are also a few more annotations, on the ``FlowLogic`` subclass itself: There are also a few more annotations, on the ``FlowLogic`` subclass itself:
* ``@InitiatingFlow`` means that this flow can be started directly by the node * ``@InitiatingFlow`` means that this flow is part of a flow pair and that it triggers the other side to run the
the counterpart flow.
* ``@StartableByRPC`` allows the node owner to start this flow via an RPC call * ``@StartableByRPC`` allows the node owner to start this flow via an RPC call
Let's walk through the steps of ``FlowLogic.call`` itself. This is where we actually describe the procedure for Let's walk through the steps of ``FlowLogic.call`` itself. This is where we actually describe the procedure for

View File

@ -1,26 +1,5 @@
buildscript { ext {
// For sharing constants between builds javaassist_version = "3.12.1.GA"
Properties constants = new Properties()
file("$projectDir/../../constants.properties").withInputStream { constants.load(it) }
ext.kotlin_version = constants.getProperty("kotlinVersion")
ext.javaassist_version = "3.12.1.GA"
repositories {
mavenLocal()
mavenCentral()
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
repositories {
mavenLocal()
mavenCentral()
jcenter()
} }
apply plugin: 'kotlin' apply plugin: 'kotlin'
@ -29,7 +8,10 @@ apply plugin: 'idea'
description 'A javaagent to allow hooking into Kryo' description 'A javaagent to allow hooking into Kryo'
dependencies { dependencies {
compile project(':node') compile(project(':node')) {
// The Node only needs this for binary compatibility with Cordapps written in Kotlin 1.1.
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jre8'
}
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "javassist:javassist:$javaassist_version" compile "javassist:javassist:$javaassist_version"

View File

@ -26,7 +26,10 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile project(':core') compile project(':core')
compile project(':node') compile(project(':node')) {
// The Node only needs this for binary compatibility with Cordapps written in Kotlin 1.1.
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jre8'
}
compile project(path: ':node-api', configuration: 'shadow') compile project(path: ':node-api', configuration: 'shadow')
testCompile "org.apache.curator:curator-test:${curator_version}" testCompile "org.apache.curator:curator-test:${curator_version}"
// Apache Curator: a client library for Zookeeper // Apache Curator: a client library for Zookeeper

View File

@ -27,7 +27,10 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
// TODO: Eliminate this dependency and relocate all the necessary classes into an independent module // TODO: Eliminate this dependency and relocate all the necessary classes into an independent module
cordaCompile project(':node') cordaCompile(project(':node')) {
// The Node only needs this for binary compatibility with Cordapps written in Kotlin 1.1.
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jre8'
}
// Logging // Logging
compile "org.slf4j:log4j-over-slf4j:$slf4j_version" compile "org.slf4j:log4j-over-slf4j:$slf4j_version"

View File

@ -13,6 +13,7 @@ import net.corda.core.crypto.Crypto
import net.corda.core.crypto.sign import net.corda.core.crypto.sign
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.internal.PLATFORM_VERSION
import net.corda.core.internal.concurrent.flatMap import net.corda.core.internal.concurrent.flatMap
import net.corda.core.internal.concurrent.map import net.corda.core.internal.concurrent.map
import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.concurrent.transpose
@ -33,7 +34,6 @@ import net.corda.node.services.messaging.ArtemisMessagingServer
import net.corda.node.services.network.NodeInfoWatcher import net.corda.node.services.network.NodeInfoWatcher
import net.corda.node.services.rpc.ArtemisRpcBroker import net.corda.node.services.rpc.ArtemisRpcBroker
import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.PLATFORM_VERSION
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.internal.config.toConfig import net.corda.nodeapi.internal.config.toConfig
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities

View File

@ -2,4 +2,3 @@ package net.corda.nodeapi.internal
// TODO: Add to Corda node.conf to allow customisation // TODO: Add to Corda node.conf to allow customisation
const val NODE_INFO_DIRECTORY = "additional-node-infos" const val NODE_INFO_DIRECTORY = "additional-node-infos"
const val PLATFORM_VERSION = 4

View File

@ -11,7 +11,7 @@ import net.corda.core.serialization.serialize
import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.NotaryConfig
import net.corda.nodeapi.internal.DEV_ROOT_CA import net.corda.nodeapi.internal.DEV_ROOT_CA
import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY
import net.corda.nodeapi.internal.PLATFORM_VERSION import net.corda.core.internal.PLATFORM_VERSION
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.config.parseAs import net.corda.nodeapi.internal.config.parseAs
import net.corda.nodeapi.internal.config.toConfig import net.corda.nodeapi.internal.config.toConfig

View File

@ -1,6 +1,6 @@
package net.corda.node package net.corda.node
import net.corda.nodeapi.internal.PLATFORM_VERSION import net.corda.core.internal.PLATFORM_VERSION
/** /**
* Encapsulates various pieces of version information of the node. * Encapsulates various pieces of version information of the node.

View File

@ -29,7 +29,7 @@ import net.corda.node.utilities.registration.NodeRegistrationException
import net.corda.node.utilities.registration.NodeRegistrationHelper import net.corda.node.utilities.registration.NodeRegistrationHelper
import net.corda.node.utilities.saveToKeyStore import net.corda.node.utilities.saveToKeyStore
import net.corda.node.utilities.saveToTrustStore import net.corda.node.utilities.saveToTrustStore
import net.corda.nodeapi.internal.PLATFORM_VERSION import net.corda.core.internal.PLATFORM_VERSION
import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.addShutdownHook
import net.corda.nodeapi.internal.config.UnknownConfigurationKeysException import net.corda.nodeapi.internal.config.UnknownConfigurationKeysException
import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException

View File

@ -112,10 +112,12 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
) )
private fun loadCordapps(): List<CordappImpl> { private fun loadCordapps(): List<CordappImpl> {
val cordapps = cordappJarPaths.map { scanCordapp(it).toCordapp(it) } val cordapps = cordappJarPaths
.map { scanCordapp(it).toCordapp(it) }
.filter { .filter {
if (it.info.minimumPlatformVersion > versionInfo.platformVersion) { if (it.info.minimumPlatformVersion > versionInfo.platformVersion) {
logger.warn("Not loading CorDapp ${it.info.shortName} (${it.info.vendor}) as it requires minimum platform version ${it.info.minimumPlatformVersion} (This node is running version ${versionInfo.platformVersion}).") logger.warn("Not loading CorDapp ${it.info.shortName} (${it.info.vendor}) as it requires minimum " +
"platform version ${it.info.minimumPlatformVersion} (This node is running version ${versionInfo.platformVersion}).")
false false
} else { } else {
true true
@ -126,7 +128,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
} }
private fun RestrictedScanResult.toCordapp(url: RestrictedURL): CordappImpl { private fun RestrictedScanResult.toCordapp(url: RestrictedURL): CordappImpl {
val info = url.url.openStream().let(::JarInputStream).use { it.manifest }.toCordappInfo(CordappImpl.jarName(url.url)) val info = url.url.openStream().let(::JarInputStream).use { it.manifest?.toCordappInfo(CordappImpl.jarName(url.url)) ?: CordappImpl.Info.UNKNOWN }
return CordappImpl( return CordappImpl(
findContractClassNames(this), findContractClassNames(this),
findInitiatedFlows(this), findInitiatedFlows(this),

View File

@ -1,6 +1,7 @@
package net.corda.node.internal.cordapp package net.corda.node.internal.cordapp
import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.internal.cordapp.CordappImpl.Info.Companion.UNKNOWN_VALUE
import java.util.jar.Attributes import java.util.jar.Attributes
import java.util.jar.Manifest import java.util.jar.Manifest
@ -23,23 +24,23 @@ fun createTestManifest(name: String, title: String, version: String, vendor: Str
return manifest return manifest
} }
operator fun Manifest.set(key: String, value: String) { operator fun Manifest.set(key: String, value: String): String? {
mainAttributes.putValue(key, value) return mainAttributes.putValue(key, value)
} }
fun Manifest?.toCordappInfo(defaultShortName: String): CordappImpl.Info { operator fun Manifest.get(key: String): String? = mainAttributes.getValue(key)
var info = CordappImpl.Info.UNKNOWN
(this?.mainAttributes?.getValue("Name") ?: defaultShortName).let { shortName -> fun Manifest.toCordappInfo(defaultShortName: String): CordappImpl.Info {
info = info.copy(shortName = shortName) val shortName = this["Name"] ?: defaultShortName
} val vendor = this["Implementation-Vendor"] ?: UNKNOWN_VALUE
this?.mainAttributes?.getValue("Implementation-Vendor")?.let { vendor -> val version = this["Implementation-Version"] ?: UNKNOWN_VALUE
info = info.copy(vendor = vendor) val minPlatformVersion = this["Min-Platform-Version"]?.toIntOrNull() ?: 1
} val targetPlatformVersion = this["Target-Platform-Version"]?.toIntOrNull() ?: minPlatformVersion
this?.mainAttributes?.getValue("Implementation-Version")?.let { version -> return CordappImpl.Info(
info = info.copy(version = version) shortName = shortName,
} vendor = vendor,
val minPlatformVersion = this?.mainAttributes?.getValue("Min-Platform-Version")?.toInt() ?: 1 version = version,
val targetPlatformVersion = this?.mainAttributes?.getValue("Target-Platform-Version")?.toInt() ?: minPlatformVersion minimumPlatformVersion = minPlatformVersion,
info = info.copy(minimumPlatformVersion = minPlatformVersion, targetPlatformVersion = targetPlatformVersion) targetPlatformVersion = targetPlatformVersion
return info )
} }

View File

@ -4,7 +4,6 @@ import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.nodeapi.internal.PLATFORM_VERSION
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.MockCordappConfigProvider import net.corda.testing.internal.MockCordappConfigProvider
import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockAttachmentStorage

View File

@ -4,7 +4,6 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.* import net.corda.core.flows.*
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.cordapp.CordappLoader import net.corda.node.cordapp.CordappLoader
import net.corda.nodeapi.internal.PLATFORM_VERSION
import net.corda.testing.node.internal.cordappsForPackages import net.corda.testing.node.internal.cordappsForPackages
import net.corda.testing.node.internal.getTimestampAsDirectoryName import net.corda.testing.node.internal.getTimestampAsDirectoryName
import net.corda.testing.node.internal.packageInDirectory import net.corda.testing.node.internal.packageInDirectory
@ -45,7 +44,7 @@ class JarScanningCordappLoaderTest {
} }
@Test @Test
fun `test that classes that aren't in cordapps aren't loaded`() { fun `classes that aren't in cordapps aren't loaded`() {
// Basedir will not be a corda node directory so the dummy flow shouldn't be recognised as a part of a cordapp // Basedir will not be a corda node directory so the dummy flow shouldn't be recognised as a part of a cordapp
val loader = JarScanningCordappLoader.fromDirectories(listOf(Paths.get("."))) val loader = JarScanningCordappLoader.fromDirectories(listOf(Paths.get(".")))
assertThat(loader.cordapps).containsOnly(loader.coreCordapp) assertThat(loader.cordapps).containsOnly(loader.coreCordapp)
@ -56,10 +55,9 @@ class JarScanningCordappLoaderTest {
val isolatedJAR = JarScanningCordappLoaderTest::class.java.getResource("isolated.jar")!! val isolatedJAR = JarScanningCordappLoaderTest::class.java.getResource("isolated.jar")!!
val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR)) val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR))
val actual = loader.cordapps.toTypedArray() assertThat(loader.cordapps).hasSize(2)
assertThat(actual).hasSize(2)
val actualCordapp = actual.single { it != loader.coreCordapp } val actualCordapp = loader.cordapps.single { it != loader.coreCordapp }
assertThat(actualCordapp.contractClassNames).isEqualTo(listOf(isolatedContractId)) assertThat(actualCordapp.contractClassNames).isEqualTo(listOf(isolatedContractId))
assertThat(actualCordapp.initiatedFlows.single().name).isEqualTo("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Acceptor") assertThat(actualCordapp.initiatedFlows.single().name).isEqualTo("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Acceptor")
assertThat(actualCordapp.rpcFlows).isEmpty() assertThat(actualCordapp.rpcFlows).isEmpty()
@ -113,7 +111,7 @@ class JarScanningCordappLoaderTest {
fun `cordapp classloader sets target and min version to 1 if not specified`() { fun `cordapp classloader sets target and min version to 1 if not specified`() {
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/no-min-or-target-version.jar")!! val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/no-min-or-target-version.jar")!!
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN) val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN)
loader.cordapps.filter { !it.info.shortName.equals("corda-core") }.forEach { loader.cordapps.filter { it.info.shortName != "corda-core" }.forEach {
assertThat(it.info.targetPlatformVersion).isEqualTo(1) assertThat(it.info.targetPlatformVersion).isEqualTo(1)
assertThat(it.info.minimumPlatformVersion).isEqualTo(1) assertThat(it.info.minimumPlatformVersion).isEqualTo(1)
} }
@ -126,7 +124,7 @@ class JarScanningCordappLoaderTest {
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!! val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!!
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN) val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN)
// exclude the core cordapp // exclude the core cordapp
val cordapp = loader.cordapps.filter { it.cordappClasses.contains("net.corda.core.internal.cordapp.CordappImpl")}.single() val cordapp = loader.cordapps.single { it.cordappClasses.contains("net.corda.core.internal.cordapp.CordappImpl") }
assertThat(cordapp.info.targetPlatformVersion).isEqualTo(3) assertThat(cordapp.info.targetPlatformVersion).isEqualTo(3)
assertThat(cordapp.info.minimumPlatformVersion).isEqualTo(2) assertThat(cordapp.info.minimumPlatformVersion).isEqualTo(2)
} }
@ -137,17 +135,17 @@ class JarScanningCordappLoaderTest {
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-no-target.jar")!! val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-no-target.jar")!!
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN) val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN)
// exclude the core cordapp // exclude the core cordapp
val cordapp = loader.cordapps.filter { it.cordappClasses.contains("net.corda.core.internal.cordapp.CordappImpl")}.single() val cordapp = loader.cordapps.single { it.cordappClasses.contains("net.corda.core.internal.cordapp.CordappImpl") }
assertThat(cordapp.info.targetPlatformVersion).isEqualTo(2) assertThat(cordapp.info.targetPlatformVersion).isEqualTo(2)
assertThat(cordapp.info.minimumPlatformVersion).isEqualTo(2) assertThat(cordapp.info.minimumPlatformVersion).isEqualTo(2)
} }
@Test @Test
fun `cordapp classloader does not load apps when their min platform version is greater than the platform version`() { fun `cordapp classloader does not load apps when their min platform version is greater than the node platform version`() {
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!! val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-no-target.jar")!!
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1)) val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1))
// exclude the core cordapp // exclude the core cordapp
assertThat(loader.cordapps.size).isEqualTo(1) assertThat(loader.cordapps).hasSize(1)
} }
@Test @Test
@ -155,7 +153,7 @@ class JarScanningCordappLoaderTest {
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!! val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!!
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1000)) val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 1000))
// exclude the core cordapp // exclude the core cordapp
assertThat(loader.cordapps.size).isEqualTo(2) assertThat(loader.cordapps).hasSize(2)
} }
@Test @Test
@ -163,11 +161,10 @@ class JarScanningCordappLoaderTest {
val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!! val jar = JarScanningCordappLoaderTest::class.java.getResource("versions/min-2-target-3.jar")!!
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 2)) val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 2))
// exclude the core cordapp // exclude the core cordapp
assertThat(loader.cordapps.size).isEqualTo(2) assertThat(loader.cordapps).hasSize(2)
} }
private fun cordappLoaderForPackages(packages: Iterable<String>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): CordappLoader { private fun cordappLoaderForPackages(packages: Iterable<String>): CordappLoader {
val cordapps = cordappsForPackages(packages) val cordapps = cordappsForPackages(packages)
return testDirectory().let { directory -> return testDirectory().let { directory ->
cordapps.packageInDirectory(directory) cordapps.packageInDirectory(directory)
@ -176,7 +173,6 @@ class JarScanningCordappLoaderTest {
} }
private fun testDirectory(): Path { private fun testDirectory(): Path {
return Paths.get("build", getTimestampAsDirectoryName()) return Paths.get("build", getTimestampAsDirectoryName())
} }
} }

View File

@ -28,7 +28,6 @@ import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.schema.NodeSchemaService
import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.transactions.InMemoryTransactionVerifierService
import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.NodeVaultService
import net.corda.nodeapi.internal.PLATFORM_VERSION
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity

View File

@ -25,7 +25,7 @@ import net.corda.node.services.Permissions
import net.corda.node.services.config.* import net.corda.node.services.config.*
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
import net.corda.node.utilities.registration.NodeRegistrationHelper import net.corda.node.utilities.registration.NodeRegistrationHelper
import net.corda.nodeapi.internal.PLATFORM_VERSION import net.corda.core.internal.PLATFORM_VERSION
import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.addShutdownHook
@ -51,10 +51,8 @@ import net.corda.testing.node.User
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import rx.Observable
import rx.Subscription import rx.Subscription
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import rx.subjects.AsyncSubject
import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
import java.net.ConnectException import java.net.ConnectException
import java.net.URL import java.net.URL

View File

@ -74,7 +74,7 @@ import java.time.Clock
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
val MOCK_VERSION_INFO = VersionInfo(4, "Mock release", "Mock revision", "Mock Vendor") val MOCK_VERSION_INFO = VersionInfo(PLATFORM_VERSION, "Mock release", "Mock revision", "Mock Vendor")
data class MockNodeArgs( data class MockNodeArgs(
val config: NodeConfiguration, val config: NodeConfiguration,

View File

@ -2,6 +2,7 @@ package net.corda.testing.node.internal
import com.typesafe.config.ConfigValueFactory import com.typesafe.config.ConfigValueFactory
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.PLATFORM_VERSION
import net.corda.core.internal.concurrent.fork import net.corda.core.internal.concurrent.fork
import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.concurrent.transpose
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
@ -32,7 +33,9 @@ import java.nio.file.Path
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.concurrent.thread import kotlin.concurrent.thread
// TODO Some of the logic here duplicates what's in the driver - the reason why it's not straightforward to replace it by using DriverDSLImpl in `init()` and `stopAllNodes()` is because of the platform version passed to nodes (driver doesn't support this, and it's a property of the Corda JAR) // TODO Some of the logic here duplicates what's in the driver - the reason why it's not straightforward to replace it by
// using DriverDSLImpl in `init()` and `stopAllNodes()` is because of the platform version passed to nodes (driver doesn't
// support this, and it's a property of the Corda JAR)
abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyList()) : IntegrationTest() { abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyList()) : IntegrationTest() {
companion object { companion object {
private val WHITESPACE = "\\s++".toRegex() private val WHITESPACE = "\\s++".toRegex()
@ -87,7 +90,7 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
@JvmOverloads @JvmOverloads
fun initNode(legalName: CordaX500Name, fun initNode(legalName: CordaX500Name,
platformVersion: Int = 4, platformVersion: Int = PLATFORM_VERSION,
rpcUsers: List<User> = emptyList(), rpcUsers: List<User> = emptyList(),
configOverrides: Map<String, Any> = emptyMap()): InProcessNode { configOverrides: Map<String, Any> = emptyMap()): InProcessNode {
val baseDirectory = baseDirectory(legalName).createDirectories() val baseDirectory = baseDirectory(legalName).createDirectories()
@ -127,7 +130,7 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
@JvmOverloads @JvmOverloads
fun startNode(legalName: CordaX500Name, fun startNode(legalName: CordaX500Name,
platformVersion: Int = 4, platformVersion: Int = PLATFORM_VERSION,
rpcUsers: List<User> = emptyList(), rpcUsers: List<User> = emptyList(),
configOverrides: Map<String, Any> = emptyMap()): NodeWithInfo { configOverrides: Map<String, Any> = emptyMap()): NodeWithInfo {
val node = initNode(legalName,platformVersion, rpcUsers,configOverrides) val node = initNode(legalName,platformVersion, rpcUsers,configOverrides)

View File

@ -9,6 +9,7 @@ import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
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.internal.PLATFORM_VERSION
import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.internal.openHttpConnection import net.corda.core.internal.openHttpConnection
import net.corda.core.internal.responseAs import net.corda.core.internal.responseAs
@ -24,7 +25,6 @@ import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.PLATFORM_VERSION
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import java.io.InputStream import java.io.InputStream
import java.net.URL import java.net.URL

View File

@ -9,7 +9,10 @@ description 'Testing utilities for Corda'
dependencies { dependencies {
compile project(':test-common') compile project(':test-common')
compile project(':node') compile(project(':node')) {
// The Node only needs this for binary compatibility with Cordapps written in Kotlin 1.1.
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jre8'
}
compile project(':client:mock') compile project(':client:mock')
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

View File

@ -2,7 +2,7 @@ package net.corda.bootstrapper
import net.corda.cliutils.CordaCliWrapper import net.corda.cliutils.CordaCliWrapper
import net.corda.cliutils.start import net.corda.cliutils.start
import net.corda.nodeapi.internal.PLATFORM_VERSION import net.corda.core.internal.PLATFORM_VERSION
import net.corda.nodeapi.internal.network.NetworkBootstrapper import net.corda.nodeapi.internal.network.NetworkBootstrapper
import picocli.CommandLine.Option import picocli.CommandLine.Option
import java.nio.file.Path import java.nio.file.Path

View File

@ -13,7 +13,10 @@ apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'com.jfrog.artifactory' apply plugin: 'com.jfrog.artifactory'
dependencies { dependencies {
compile project(':node') compile(project(':node')) {
// The Node only needs this for binary compatibility with Cordapps written in Kotlin 1.1.
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jre8'
}
compile project(':tools:cliutils') compile project(':tools:cliutils')
} }

View File

@ -1,57 +0,0 @@
package net.corda.loadtest.tests
import net.corda.client.mock.Generator
import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowException
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.concurrent.thenMatch
import net.corda.core.messaging.startFlow
import net.corda.core.transactions.SignedTransaction
import net.corda.loadtest.LoadTest
import net.corda.loadtest.NodeConnection
import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.TestIdentity
import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestIdentityService
import org.slf4j.LoggerFactory
private val log = LoggerFactory.getLogger("NotaryTest")
private val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10)
private val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1)
private val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20)
private val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB"))
private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB"))
data class NotariseCommand(val issueTx: SignedTransaction, val moveTx: SignedTransaction, val node: NodeConnection)
val dummyNotarisationTest = LoadTest<NotariseCommand, Unit>(
"Notarising dummy transactions",
generate = { _, _ ->
val issuerServices = MockServices(emptyList(), megaCorp.name, makeTestIdentityService(megaCorp.identity, miniCorp.identity, dummyCashIssuer.identity, dummyNotary.identity), dummyCashIssuer.keyPair)
val generateTx = Generator.pickOne(simpleNodes).flatMap { node ->
Generator.int().map {
val issueBuilder = DummyContract.generateInitial(it, notary.info.legalIdentities[0], DUMMY_CASH_ISSUER) // TODO notary choice
val issueTx = issuerServices.signInitialTransaction(issueBuilder)
val asset = issueTx.tx.outRef<DummyContract.SingleOwnerState>(0)
val moveBuilder = DummyContract.move(asset, dummyCashIssuer.party)
val moveTx = issuerServices.signInitialTransaction(moveBuilder)
NotariseCommand(issueTx, moveTx, node)
}
}
Generator.replicate(10, generateTx)
},
interpret = { _, _ -> },
execute = { (issueTx, moveTx, node) ->
try {
val proxy = node.proxy
val issueFlow = proxy.startFlow(::FinalityFlow, issueTx)
issueFlow.returnValue.thenMatch({
proxy.startFlow(::FinalityFlow, moveTx)
}, {})
} catch (e: FlowException) {
log.error("Failure", e)
}
},
gatherRemoteState = {}
)

View File

@ -16,6 +16,9 @@ configurations {
compile { compile {
exclude group: "log4j", module: "log4j" exclude group: "log4j", module: "log4j"
exclude group: "org.apache.logging.log4j" exclude group: "org.apache.logging.log4j"
// The Node only needs this for binary compatibility with Cordapps written in Kotlin 1.1.
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jre8'
} }
} }

View File

@ -15,7 +15,10 @@ sourceSets {
} }
dependencies { dependencies {
cordaCompile project(":node") // Not node-api so we get metrics. cordaCompile(project(":node")) { // Not node-api so we get metrics.
// The Node only needs this for binary compatibility with Cordapps written in Kotlin 1.1.
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jre8'
}
cordapp project(":tools:notary-healthcheck:contract") cordapp project(":tools:notary-healthcheck:contract")
testCompile project(":node-driver") testCompile project(":node-driver")
} }