mirror of
https://github.com/corda/corda.git
synced 2025-03-14 16:26:36 +00:00
Merge remote-tracking branch 'remotes/open/master' into mnesbit-merge-20180530
# Conflicts: # docs/source/changelog.rst
This commit is contained in:
commit
a3c6ef351d
@ -907,6 +907,13 @@ object Crypto {
|
||||
return signatureScheme.schemeCodeName in signatureSchemeMap
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a public key satisfies algorithm specs.
|
||||
* For instance, an ECC key should lie on the curve and not being point-at-infinity.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun validatePublicKey(key: PublicKey): Boolean = validatePublicKey(findSignatureScheme(key), key)
|
||||
|
||||
// Validate a key, by checking its algorithmic params.
|
||||
private fun validateKey(signatureScheme: SignatureScheme, key: Key): Boolean {
|
||||
return when (key) {
|
||||
@ -920,7 +927,8 @@ object Crypto {
|
||||
private fun validatePublicKey(signatureScheme: SignatureScheme, key: PublicKey): Boolean {
|
||||
return when (key) {
|
||||
is BCECPublicKey, is EdDSAPublicKey -> publicKeyOnCurve(signatureScheme, key)
|
||||
is BCRSAPublicKey, is BCSphincs256PublicKey -> true // TODO: Check if non-ECC keys satisfy params (i.e. approved/valid RSA modulus size).
|
||||
is BCRSAPublicKey -> key.modulus.bitLength() >= 2048 // Although the recommended RSA key size is 3072, we accept any key >= 2048bits.
|
||||
is BCSphincs256PublicKey -> true
|
||||
else -> throw IllegalArgumentException("Unsupported key type: ${key::class}")
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ Unreleased
|
||||
==========
|
||||
* Introduced a hierarchy of ``DatabaseMigrationException``s, allowing ``NodeStartup`` to gracefully inform users of problems related to database migrations before exiting with a non-zero code.
|
||||
|
||||
* Shell now kills an ongoing flow when CTRL+C is pressed in the terminal.
|
||||
|
||||
* ``ServiceHub`` and ``CordaRPCOps`` can now safely be used from multiple threads without incurring in database transaction problems.
|
||||
|
||||
* Doorman and NetworkMap url's can now be configured individually rather than being assumed to be
|
||||
@ -105,6 +107,15 @@ Unreleased
|
||||
(even if JPA annotation nullable=false was absent).
|
||||
In case your Cordapps use this entity class to persist data in own custom tables as non Primary Key columns refer to :doc:`upgrade-notes` for upgrade instructions.
|
||||
|
||||
* Adding a public method to check if a public key satisfies Corda recommended algorithm specs, `Crypto.validatePublicKey(java.security.PublicKey)`.
|
||||
For instance, this method will check if an ECC key lies on a valid curve or if an RSA key is >= 2048bits. This might
|
||||
be required for extra key validation checks, e.g., for Doorman to check that a CSR key meets the minimum security requirements.
|
||||
|
||||
.. _changelog_v3.1:
|
||||
|
||||
Version 3.1
|
||||
-----------
|
||||
|
||||
* Update the fast-classpath-scanner dependent library version from 2.0.21 to 2.12.3
|
||||
|
||||
* Added `database.hibernateDialect` node configuration option
|
||||
|
@ -13,4 +13,8 @@ package net.corda.nodeapi.internal.protonwrapper.netty
|
||||
import java.net.InetSocketAddress
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean, val badCert: Boolean)
|
||||
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean, val badCert: Boolean) {
|
||||
override fun toString(): String {
|
||||
return "ConnectionChange remoteAddress: $remoteAddress connected state: $connected cert subject: ${remoteCert?.subjectDN} cert ok: ${!badCert}"
|
||||
}
|
||||
}
|
@ -141,9 +141,10 @@ class NodeVaultService(
|
||||
fun makeUpdate(tx: WireTransaction): Vault.Update<ContractState>? {
|
||||
val ourNewStates = when (statesToRecord) {
|
||||
StatesToRecord.NONE -> throw AssertionError("Should not reach here")
|
||||
StatesToRecord.ONLY_RELEVANT -> tx.outputs.filter { isRelevant(it.data, keyManagementService.filterMyKeys(tx.outputs.flatMap { it.data.participants.map { it.owningKey } }).toSet()) }
|
||||
StatesToRecord.ALL_VISIBLE -> tx.outputs
|
||||
}.map { tx.outRef<ContractState>(it.data) }
|
||||
StatesToRecord.ONLY_RELEVANT -> tx.outputs.withIndex().filter {
|
||||
isRelevant(it.value.data, keyManagementService.filterMyKeys(tx.outputs.flatMap { it.data.participants.map { it.owningKey } }).toSet()) }
|
||||
StatesToRecord.ALL_VISIBLE -> tx.outputs.withIndex()
|
||||
}.map { tx.outRef<ContractState>(it.index) }
|
||||
|
||||
// Retrieve all unconsumed states for this transaction's inputs
|
||||
val consumedStates = loadStates(tx.inputs)
|
||||
|
@ -14,10 +14,7 @@ import co.paralleluniverse.fibers.Suspendable
|
||||
import com.nhaarman.mockito_kotlin.argThat
|
||||
import com.nhaarman.mockito_kotlin.doNothing
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.Issued
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.NullKeys
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.identity.*
|
||||
@ -697,4 +694,50 @@ class NodeVaultServiceTest {
|
||||
}
|
||||
assertEquals(currentCashStates + 1, countCash())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `insert equal cash states issued by single transaction`() {
|
||||
val nodeIdentity = MEGA_CORP
|
||||
val coins = listOf(1.DOLLARS, 1.DOLLARS).map { it.issuedBy(nodeIdentity.ref(1)) }
|
||||
|
||||
//create single transaction with 2 'identical' cash outputs
|
||||
val txb = TransactionBuilder(DUMMY_NOTARY)
|
||||
coins.map { txb.addOutputState(TransactionState(Cash.State(it, nodeIdentity), Cash.PROGRAM_ID, DUMMY_NOTARY)) }
|
||||
txb.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey)
|
||||
val issueTx = txb.toWireTransaction(services)
|
||||
|
||||
// ensure transaction contract state is persisted in DBStorage
|
||||
val signedIssuedTx = services.signInitialTransaction(txb)
|
||||
(services.validatedTransactions as WritableTransactionStorage).addTransaction(signedIssuedTx)
|
||||
|
||||
database.transaction { vaultService.notify(StatesToRecord.ONLY_RELEVANT, issueTx) }
|
||||
|
||||
val recordedStates = database.transaction {
|
||||
vaultService.queryBy<Cash.State>().states.size
|
||||
}
|
||||
assertThat(recordedStates).isEqualTo(coins.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `insert different cash states issued by single transaction`() {
|
||||
val nodeIdentity = MEGA_CORP
|
||||
val coins = listOf(2.DOLLARS, 1.DOLLARS).map { it.issuedBy(nodeIdentity.ref(1)) }
|
||||
|
||||
//create single transaction with 2 'identical' cash outputs
|
||||
val txb = TransactionBuilder(DUMMY_NOTARY)
|
||||
coins.map { txb.addOutputState(TransactionState(Cash.State(it, nodeIdentity), Cash.PROGRAM_ID, DUMMY_NOTARY)) }
|
||||
txb.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey)
|
||||
val issueTx = txb.toWireTransaction(services)
|
||||
|
||||
// ensure transaction contract state is persisted in DBStorage
|
||||
val signedIssuedTx = services.signInitialTransaction(txb)
|
||||
(services.validatedTransactions as WritableTransactionStorage).addTransaction(signedIssuedTx)
|
||||
|
||||
database.transaction { vaultService.notify(StatesToRecord.ONLY_RELEVANT, issueTx) }
|
||||
|
||||
val recordedStates = database.transaction {
|
||||
vaultService.queryBy<Cash.State>().states.size
|
||||
}
|
||||
assertThat(recordedStates).isEqualTo(coins.size)
|
||||
}
|
||||
}
|
||||
|
@ -25,10 +25,18 @@ import net.corda.core.CordaException
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.Emoji
|
||||
import net.corda.core.internal.concurrent.doneFuture
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.messaging.*
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.rootCause
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.messaging.FlowProgressHandle
|
||||
import net.corda.core.messaging.StateMachineUpdate
|
||||
import net.corda.core.messaging.pendingFlowsCount
|
||||
import net.corda.tools.shell.utlities.ANSIProgressRenderer
|
||||
import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer
|
||||
import org.crsh.command.InvocationContext
|
||||
@ -278,12 +286,19 @@ object InteractiveShell {
|
||||
|
||||
val latch = CountDownLatch(1)
|
||||
ansiProgressRenderer.render(stateObservable, { latch.countDown() })
|
||||
try {
|
||||
// Wait for the flow to end and the progress tracker to notice. By the time the latch is released
|
||||
// the tracker is done with the screen.
|
||||
latch.await()
|
||||
} catch (e: InterruptedException) {
|
||||
// TODO: When the flow framework allows us to kill flows mid-flight, do so here.
|
||||
// Wait for the flow to end and the progress tracker to notice. By the time the latch is released
|
||||
// the tracker is done with the screen.
|
||||
while (!Thread.currentThread().isInterrupted) {
|
||||
try {
|
||||
latch.await()
|
||||
} catch (e: InterruptedException) {
|
||||
try {
|
||||
rpcOps.killFlow(stateObservable.id)
|
||||
} finally {
|
||||
Thread.currentThread().interrupt()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
stateObservable.returnValue.get()?.apply {
|
||||
if (this !is Throwable) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user