Merge remote-tracking branch 'open/master' into shams-os-merge-040118

This commit is contained in:
Shams Asari
2018-01-04 18:32:15 +00:00
17 changed files with 200 additions and 28 deletions

View File

@ -30,6 +30,7 @@ import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
import org.bouncycastle.jce.spec.ECParameterSpec import org.bouncycastle.jce.spec.ECParameterSpec
import org.bouncycastle.jce.spec.ECPrivateKeySpec import org.bouncycastle.jce.spec.ECPrivateKeySpec
import org.bouncycastle.jce.spec.ECPublicKeySpec import org.bouncycastle.jce.spec.ECPublicKeySpec
@ -808,7 +809,7 @@ object Crypto {
/** /**
* Returns a key pair derived from the given [BigInteger] entropy. This is useful for unit tests * Returns a key pair derived from the given [BigInteger] entropy. This is useful for unit tests
* and other cases where you want hard-coded private keys. * and other cases where you want hard-coded private keys.
* Currently, [EDDSA_ED25519_SHA512] is the sole scheme supported for this operation. * Currently, the following schemes are supported: [EDDSA_ED25519_SHA512], [ECDSA_SECP256R1_SHA256] and [ECDSA_SECP256K1_SHA256].
* @param signatureScheme a supported [SignatureScheme], see [Crypto]. * @param signatureScheme a supported [SignatureScheme], see [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.
@ -818,6 +819,7 @@ object Crypto {
fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair { fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
return when (signatureScheme) { return when (signatureScheme) {
EDDSA_ED25519_SHA512 -> deriveEdDSAKeyPairFromEntropy(entropy) EDDSA_ED25519_SHA512 -> deriveEdDSAKeyPairFromEntropy(entropy)
ECDSA_SECP256R1_SHA256, ECDSA_SECP256K1_SHA256 -> deriveECDSAKeyPairFromEntropy(signatureScheme, entropy)
else -> throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair " + else -> throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair " +
"generation: ${signatureScheme.schemeCodeName}") "generation: ${signatureScheme.schemeCodeName}")
} }
@ -832,6 +834,9 @@ object Crypto {
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy) fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
// Custom key pair generator from entropy. // Custom key pair generator from entropy.
// The BigIntenger.toByteArray() uses the two's-complement representation.
// The entropy is transformed to a byte array in big-endian byte-order and
// only the first ed25519.field.getb() / 8 bytes are used.
private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair { private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair {
val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length. val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
@ -840,6 +845,32 @@ object Crypto {
return KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv)) return KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
} }
// Custom key pair generator from an entropy required for various tests. It is similar to deriveKeyPairECDSA,
// but the accepted range of the input entropy is more relaxed:
// 2 <= entropy < N, where N is the order of base-point G.
private fun deriveECDSAKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
val parameterSpec = signatureScheme.algSpec as ECNamedCurveParameterSpec
// The entropy might be a negative number and/or out of range (e.g. PRNG output).
// In such cases we retry with hash(currentEntropy).
while (entropy < ECConstants.TWO || entropy >= parameterSpec.n) {
return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes))
}
val privateKeySpec = ECPrivateKeySpec(entropy, parameterSpec)
val priv = BCECPrivateKey("EC", privateKeySpec, BouncyCastleProvider.CONFIGURATION)
val pointQ = FixedPointCombMultiplier().multiply(parameterSpec.g, entropy)
while (pointQ.isInfinity) {
// Instead of throwing an exception, we retry with hash(entropy).
return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes))
}
val publicKeySpec = ECPublicKeySpec(pointQ, parameterSpec)
val pub = BCECPublicKey("EC", publicKeySpec, BouncyCastleProvider.CONFIGURATION)
return KeyPair(pub, priv)
}
// Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray. // Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray.
private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray { private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray {
// Compute hmac(privateKey, seed). // Compute hmac(privateKey, seed).

View File

@ -123,13 +123,14 @@ operator fun KeyPair.component1(): PrivateKey = this.private
/* The [PublicKey] of this [KeyPair]. */ /* The [PublicKey] of this [KeyPair]. */
operator fun KeyPair.component2(): PublicKey = this.public operator fun KeyPair.component2(): PublicKey = this.public
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future. */ /** A simple wrapper that will make it easier to swap out the signature algorithm we use in future. */
fun generateKeyPair(): KeyPair = Crypto.generateKeyPair() fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
/** /**
* Returns a key pair derived from the given private key entropy. This is useful for unit tests and other cases where * Returns a key pair derived from the given private key entropy. This is useful for unit tests and other cases where
* you want hard-coded private keys. * you want hard-coded private keys.
* This currently works for the default signature scheme EdDSA ed25519 only. * @param entropy a [BigInteger] value.
* @return a deterministically generated [KeyPair] for the [Crypto.DEFAULT_SIGNATURE_SCHEME].
*/ */
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy) fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)

View File

@ -10,7 +10,17 @@ import net.corda.core.serialization.CordaSerializable
*/ */
@DoNotImplement @DoNotImplement
interface FlowLogicRefFactory { interface FlowLogicRefFactory {
/**
* Construct a FlowLogicRef. This is intended for cases where the calling code has the relevant class already
* and can provide it directly.
*/
@Deprecated("This should be avoided, and the version which takes a class name used instead to avoid requiring the class on the classpath to deserialize calling code")
fun create(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef fun create(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
/**
* Construct a FlowLogicRef. This is intended for cases where the calling code does not want to require the flow
* class on the classpath for all cases where the calling code is loaded.
*/
fun create(flowClassName: String, vararg args: Any?): FlowLogicRef
fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*> fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*>
} }

View File

@ -14,21 +14,22 @@ 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.jce.ECNamedCurveTable import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECKey import org.bouncycastle.jce.interfaces.ECKey
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNotEquals
import org.junit.Test import org.junit.Test
import java.math.BigInteger
import java.security.KeyPairGenerator import java.security.KeyPairGenerator
import java.util.* import java.util.*
import kotlin.test.* import kotlin.test.*
/** /**
* Run tests for cryptographic algorithms * Run tests for cryptographic algorithms.
*/ */
class CryptoUtilsTest { class CryptoUtilsTest {
private val testString = "Hello World" private val testBytes = "Hello World".toByteArray()
private val testBytes = testString.toByteArray()
// key generation test // key generation test
@Test @Test
@ -781,4 +782,113 @@ class CryptoUtilsTest {
assertEquals(dpriv2, dpriv_2) assertEquals(dpriv2, dpriv_2)
assertEquals(dpub2, dpub_2) assertEquals(dpub2, dpub_2)
} }
@Test
fun `EdDSA ed25519 keyPair from entropy`() {
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("10"))
assertEquals("DLBL3iHCp9uRReWhhCGfCsrxZZpfAm9h9GLbfN8ijqXTq", keyPairPositive.public.toStringShort())
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("-10"))
assertEquals("DLC5HXnYsJAFqmM9hgPj5G8whQ4TpyE9WMBssqCayLBwA2", keyPairNegative.public.toStringShort())
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("0"))
assertEquals("DL4UVhGh4tqu1G86UVoGNaDDNCMsBtNHzE6BSZuNNJN7W2", keyPairZero.public.toStringShort())
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("1"))
assertEquals("DL8EZUdHixovcCynKMQzrMWBnXQAcbVDHi6ArPphqwJVzq", keyPairOne.public.toStringShort())
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger.TEN))
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bits.public.toStringShort())
// The underlying implementation uses the first 256 bytes of the entropy. Thus, 2^258-10 and 2^258-50 and 2^514-10 have the same impact.
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger("50")))
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bitsV2.public.toStringShort())
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(514).minus(BigInteger.TEN))
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan512bits.public.toStringShort())
// Try another big number.
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(259).plus(BigInteger.ONE))
assertEquals("DL5tEFVMXMGrzwjfCAW34JjkhsRkPfFyJ38iEnmpB6L2Z9", keyPairBiggerThan258bits.public.toStringShort())
}
@Test
fun `ECDSA R1 keyPair from entropy`() {
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("10"))
assertEquals("DLHDcxuSt9J3cbjd2Dsx4rAgYYA7BAP7A8VLrFiq1tH9yy", keyPairPositive.public.toStringShort())
// The underlying implementation uses the hash of entropy if it is out of range 2 < entropy < N, where N the order of the group.
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("-10"))
assertEquals("DLBASmjiMZuu1g3EtdHJxfSueXE8PRoUWbkdU61Qcnpamt", keyPairNegative.public.toStringShort())
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("0"))
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZero.public.toStringShort())
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, zeroHashed)
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZeroHashed.public.toStringShort())
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("1"))
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOne.public.toStringShort())
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, oneHashed)
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOneHashed.public.toStringShort())
// 2 is in the range.
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2"))
assertEquals("DLFoz6txJ3vHcKNSM1vFxHJUoEQ69PorBwW64dHsAnEoZB", keyPairTwo.public.toStringShort())
// Try big numbers that are out of range.
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
assertEquals("DLBv6fZqaCTbE4L7sgjbt19biXHMgU9CzR5s8g8XBJjZ11", keyPairBiggerThan256bits.public.toStringShort())
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
assertEquals("DLANmjhGSVdLyghxcPHrn3KuGatscf6LtvqifUDxw7SGU8", keyPairBiggerThan256bitsV2.public.toStringShort())
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
assertEquals("DL9sKwMExBTD3MnJN6LWGqo496Erkebs9fxZtXLVJUBY9Z", keyPairBiggerThan512bits.public.toStringShort())
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
assertEquals("DLBwjWwPJSF9E7b1NWaSbEJ4oK8CF7RDGWd648TiBhZoL1", keyPairBiggerThan258bits.public.toStringShort())
}
@Test
fun `ECDSA K1 keyPair from entropy`() {
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("10"))
assertEquals("DL6pYKUgH17az8MLdonvvUtUPN8TqwpCGcdgLr7vg3skCU", keyPairPositive.public.toStringShort())
// The underlying implementation uses the hash of entropy if it is out of range 2 <= entropy < N, where N the order of the group.
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("-10"))
assertEquals("DLnpXhxece69Nyqgm3pPt3yV7ESQYDJKoYxs1hKgfBAEu", keyPairNegative.public.toStringShort())
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("0"))
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZero.public.toStringShort())
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, zeroHashed)
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZeroHashed.public.toStringShort())
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("1"))
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOne.public.toStringShort())
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, oneHashed)
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOneHashed.public.toStringShort())
// 2 is in the range.
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2"))
assertEquals("DLG32UWaevGw9YY7w1Rf9mmK88biavgpDnJA9bG4GapVPs", keyPairTwo.public.toStringShort())
// Try big numbers that are out of range.
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
assertEquals("DLGHsdv2xeAuM7n3sBc6mFfiphXe6VSf3YxqvviKDU6Vbd", keyPairBiggerThan256bits.public.toStringShort())
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
assertEquals("DL9yJfiNGqteRrKPjGUkRQkeqzuQ4kwcYQWMCi5YKuUHrk", keyPairBiggerThan256bitsV2.public.toStringShort())
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
assertEquals("DL3Wr5EQGrMTaKBy5XMvG8rvSfKX1AYZLCRU8kixGbxt1E", keyPairBiggerThan512bits.public.toStringShort())
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
assertEquals("DL7NbssqvuuJ4cqFkkaVYu9j1MsVswESGgCfbqBS9ULwuM", keyPairBiggerThan258bits.public.toStringShort())
}
} }

View File

@ -29,8 +29,8 @@ import java.util.*
class CashExitFlow(private val amount: Amount<Currency>, class CashExitFlow(private val amount: Amount<Currency>,
private val issuerRef: OpaqueBytes, private val issuerRef: OpaqueBytes,
progressTracker: ProgressTracker) : AbstractCashFlow<AbstractCashFlow.Result>(progressTracker) { progressTracker: ProgressTracker) : AbstractCashFlow<AbstractCashFlow.Result>(progressTracker) {
constructor(amount: Amount<Currency>, issueRef: OpaqueBytes) : this(amount, issueRef, tracker()) constructor(amount: Amount<Currency>, issuerRef: OpaqueBytes) : this(amount, issuerRef, tracker())
constructor(request: ExitRequest) : this(request.amount, request.issueRef, tracker()) constructor(request: ExitRequest) : this(request.amount, request.issuerRef, tracker())
companion object { companion object {
fun tracker() = ProgressTracker(GENERATING_TX, SIGNING_TX, FINALISING_TX) fun tracker() = ProgressTracker(GENERATING_TX, SIGNING_TX, FINALISING_TX)
@ -78,5 +78,5 @@ class CashExitFlow(private val amount: Amount<Currency>,
} }
@CordaSerializable @CordaSerializable
class ExitRequest(amount: Amount<Currency>, val issueRef: OpaqueBytes) : AbstractRequest(amount) class ExitRequest(amount: Amount<Currency>, val issuerRef: OpaqueBytes) : AbstractRequest(amount)
} }

View File

@ -6,7 +6,7 @@ Generates a text summary of Corda's public API that we can check for API-breakin
$ gradlew generateApi $ gradlew generateApi
``` ```
See [here](../../docs/source/api-index.rst) for Corda's public API strategy. We will need to See [here](../../docs/source/corda-api.rst) for Corda's public API strategy. We will need to
apply this plugin to other modules in future Corda releases as those modules' APIs stabilise. apply this plugin to other modules in future Corda releases as those modules' APIs stabilise.
Basically, this plugin will document a module's `public` and `protected` classes/methods/fields, Basically, this plugin will document a module's `public` and `protected` classes/methods/fields,

View File

@ -85,7 +85,7 @@ private fun <T : Any> propertiesForSerializationFromConstructor(kotlinConstructo
val matchingProperty = properties[name] ?: val matchingProperty = properties[name] ?:
try { try {
clazz.getDeclaredField(param.name) clazz.getDeclaredField(param.name)
throw NotSerializableException("Property '$name' or it's getter is non public, this renders class '$clazz' unserializable") throw NotSerializableException("Property '$name' or its getter is non public, this renders class '$clazz' unserializable")
} catch (e: NoSuchFieldException) { } catch (e: NoSuchFieldException) {
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " + throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " +
"If using Java, check that you have the -parameters option specified in the Java compiler. " + "If using Java, check that you have the -parameters option specified in the Java compiler. " +

View File

@ -10,7 +10,7 @@ public class ErrorMessageTests {
private String errMsg(String property, String testname) { private String errMsg(String property, String testname) {
return "Property '" return "Property '"
+ property + property
+ "' or it's getter is non public, this renders class 'class " + "' or its getter is non public, this renders class 'class "
+ testname + testname
+ "$C' unserializable -> class " + "$C' unserializable -> class "
+ testname + testname

View File

@ -10,7 +10,7 @@ class ErrorMessagesTests {
} }
private fun errMsg(property:String, testname: String) = private fun errMsg(property:String, testname: String) =
"Property '$property' or it's getter is non public, this renders class 'class $testname\$C' unserializable -> class $testname\$C" "Property '$property' or its getter is non public, this renders class 'class $testname\$C' unserializable -> class $testname\$C"
@Test @Test
fun privateProperty() { fun privateProperty() {

View File

@ -40,6 +40,18 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS
return createForRPC(flowClass, *args) return createForRPC(flowClass, *args)
} }
override fun create(flowClassName: String, vararg args: Any?): FlowLogicRef {
val flowClass = Class.forName(flowClassName, true, classloader).asSubclass(FlowLogic::class.java)
if (flowClass == null) {
throw IllegalArgumentException("The class $flowClassName is not a subclass of FlowLogic.")
} else {
if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) {
throw IllegalFlowLogicException(flowClass, "because it's not a schedulable flow")
}
return createForRPC(flowClass, *args)
}
}
override fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef { override fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef {
// TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly // TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly
// to avoid requiring only a single constructor. // to avoid requiring only a single constructor.

View File

@ -30,6 +30,7 @@ import org.junit.Assert.*
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.time.Instant import java.time.Instant
import kotlin.reflect.jvm.jvmName
import kotlin.test.assertEquals import kotlin.test.assertEquals
class ScheduledFlowTests { class ScheduledFlowTests {
@ -52,7 +53,7 @@ class ScheduledFlowTests {
override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState { override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState {
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
return if (!processed) { return if (!processed) {
val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef) val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.jvmName, thisStateRef)
ScheduledActivity(logicRef, creationTime) ScheduledActivity(logicRef, creationTime)
} else { } else {
null null

View File

@ -1,17 +1,20 @@
package net.corda.node.services.events package net.corda.node.services.statemachine
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.IllegalFlowLogicException import net.corda.core.flows.IllegalFlowLogicException
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl import net.corda.core.flows.SchedulableFlow
import org.junit.Test import org.junit.Test
import java.time.Duration import java.time.Duration
import kotlin.reflect.jvm.jvmName
import kotlin.test.assertEquals
class FlowLogicRefTest { class FlowLogicRefFactoryImplTest {
data class ParamType1(val value: Int) data class ParamType1(val value: Int)
data class ParamType2(val value: String) data class ParamType2(val value: String)
@Suppress("UNUSED_PARAMETER", "unused") // Things are used via reflection. @Suppress("UNUSED_PARAMETER", "unused") // Things are used via reflection.
@SchedulableFlow
class KotlinFlowLogic(A: ParamType1, b: ParamType2) : FlowLogic<Unit>() { class KotlinFlowLogic(A: ParamType1, b: ParamType2) : FlowLogic<Unit>() {
constructor() : this(ParamType1(1), ParamType2("2")) constructor() : this(ParamType1(1), ParamType2("2"))
@ -26,6 +29,7 @@ class FlowLogicRefTest {
override fun call() = Unit override fun call() = Unit
} }
@SchedulableFlow
class KotlinNoArgFlowLogic : FlowLogic<Unit>() { class KotlinNoArgFlowLogic : FlowLogic<Unit>() {
override fun call() = Unit override fun call() = Unit
} }
@ -37,18 +41,18 @@ class FlowLogicRefTest {
private val flowLogicRefFactory = FlowLogicRefFactoryImpl(FlowLogicRefFactoryImpl::class.java.classLoader) private val flowLogicRefFactory = FlowLogicRefFactoryImpl(FlowLogicRefFactoryImpl::class.java.classLoader)
@Test @Test
fun `create kotlin no arg`() { fun `create kotlin no arg`() {
flowLogicRefFactory.createForRPC(KotlinNoArgFlowLogic::class.java) flowLogicRefFactory.create(KotlinNoArgFlowLogic::class.jvmName)
} }
@Test @Test
fun `create kotlin`() { fun `should create kotlin types`() {
val args = mapOf(Pair("A", ParamType1(1)), Pair("b", ParamType2("Hello Jack"))) val args = mapOf(Pair("A", ParamType1(1)), Pair("b", ParamType2("Hello Jack")))
flowLogicRefFactory.createKotlin(KotlinFlowLogic::class.java, args) flowLogicRefFactory.createKotlin(KotlinFlowLogic::class.java, args)
} }
@Test @Test
fun `create primary`() { fun `create primary`() {
flowLogicRefFactory.createForRPC(KotlinFlowLogic::class.java, ParamType1(1), ParamType2("Hello Jack")) flowLogicRefFactory.create(KotlinFlowLogic::class.jvmName, ParamType1(1), ParamType2("Hello Jack"))
} }
@Test @Test
@ -76,6 +80,6 @@ class FlowLogicRefTest {
@Test(expected = IllegalFlowLogicException::class) @Test(expected = IllegalFlowLogicException::class)
fun `create for non-schedulable flow logic`() { fun `create for non-schedulable flow logic`() {
flowLogicRefFactory.create(NonSchedulableFlow::class.java) flowLogicRefFactory.create(NonSchedulableFlow::class.jvmName)
} }
} }

View File

@ -24,6 +24,7 @@ import net.corda.testing.node.startFlow
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
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -99,6 +100,7 @@ class NotaryServiceTests {
assertThat(ex.error).isInstanceOf(NotaryError.TimeWindowInvalid::class.java) assertThat(ex.error).isInstanceOf(NotaryError.TimeWindowInvalid::class.java)
} }
@Ignore("Only applies to deterministic signature schemes (e.g. EdDSA) and when deterministic metadata is attached (no timestamps or nonces)")
@Test @Test
fun `should sign identical transaction multiple times (signing is idempotent)`() { fun `should sign identical transaction multiple times (signing is idempotent)`() {
val stx = run { val stx = run {

View File

@ -616,7 +616,7 @@ class InterestRateSwap : Contract {
// This is perhaps not how we should determine the time point in the business day, but instead expect the schedule to detail some of these aspects // This is perhaps not how we should determine the time point in the business day, but instead expect the schedule to detail some of these aspects
val instant = suggestInterestRateAnnouncementTimeWindow(index = nextFixingOf.name, source = floatingLeg.indexSource, date = nextFixingOf.forDay).fromTime!! val instant = suggestInterestRateAnnouncementTimeWindow(index = nextFixingOf.name, source = floatingLeg.indexSource, date = nextFixingOf.forDay).fromTime!!
return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant) return ScheduledActivity(flowLogicRefFactory.create("net.corda.irs.flows.FixingFlow\$FixingRoleDecider", thisStateRef), instant)
} }
// DOCEND 1 // DOCEND 1

View File

@ -7,7 +7,6 @@ import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.finance.contracts.DealState import net.corda.finance.contracts.DealState
import net.corda.vega.flows.SimmRevaluation
import java.time.LocalDate import java.time.LocalDate
import java.time.ZoneOffset import java.time.ZoneOffset
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
@ -32,7 +31,7 @@ data class PortfolioState(val portfolio: List<StateRef>,
val valuer: AbstractParty get() = participants[0] val valuer: AbstractParty get() = participants[0]
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity {
val flow = flowLogicRefFactory.create(SimmRevaluation.Initiator::class.java, thisStateRef, LocalDate.now()) val flow = flowLogicRefFactory.create("net.corda.vega.flows.SimmRevaluation\$Initiator", thisStateRef, LocalDate.now())
return ScheduledActivity(flow, LocalDate.now().plus(1, ChronoUnit.DAYS).atStartOfDay().toInstant(ZoneOffset.UTC)) return ScheduledActivity(flow, LocalDate.now().plus(1, ChronoUnit.DAYS).atStartOfDay().toInstant(ZoneOffset.UTC))
} }

View File

@ -6,6 +6,7 @@ import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.DoNotImplement import net.corda.core.DoNotImplement
import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.crypto.Crypto
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.identity.Party import net.corda.core.identity.Party
@ -326,7 +327,8 @@ open class MockNetwork(private val cordappPackages: List<String>,
// This is not thread safe, but node construction is done on a single thread, so that should always be fine // This is not thread safe, but node construction is done on a single thread, so that should always be fine
override fun generateKeyPair(): KeyPair { override fun generateKeyPair(): KeyPair {
counter = counter.add(BigInteger.ONE) counter = counter.add(BigInteger.ONE)
return entropyToKeyPair(counter) // The MockNode specifically uses EdDSA keys as they are fixed and stored in json files for some tests (e.g IRSSimulation).
return Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, counter)
} }
/** /**

View File

@ -152,7 +152,7 @@ class ExplorerSimulation(private val options: OptionSet) {
it.startFlow(::CashIssueAndPaymentFlow, request).log(i, "${request.amount.token}Issuer") it.startFlow(::CashIssueAndPaymentFlow, request).log(i, "${request.amount.token}Issuer")
} }
is ExitRequest -> issuers[request.amount.token]?.let { is ExitRequest -> issuers[request.amount.token]?.let {
println("${Instant.now()} [$i] EXITING ${request.amount} with ref ${request.issueRef}") println("${Instant.now()} [$i] EXITING ${request.amount} with ref ${request.issuerRef}")
it.startFlow(::CashExitFlow, request).log(i, "${request.amount.token}Exit") it.startFlow(::CashExitFlow, request).log(i, "${request.amount.token}Exit")
} }
else -> throw IllegalArgumentException("Unsupported command: $request") else -> throw IllegalArgumentException("Unsupported command: $request")