mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
CORDA-2347: Added backwards compatibility to SwapIdentitiesFlow (#4548)
The API has been reverted to be completely ABI compatible with V3, and the small changes that were made to the wire format in https://github.com/corda/corda/pull/4260 have also been reverted.
This commit is contained in:
parent
8785bc1b84
commit
caad18f6db
@ -4,29 +4,49 @@ import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.CordaInternal
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.verify
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.cordapp.CordappResolver
|
||||
import net.corda.core.internal.warnOnce
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.unwrap
|
||||
import java.security.PublicKey
|
||||
import java.security.SignatureException
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Very basic flow which generates new confidential identities for parties in a transaction and exchanges the transaction
|
||||
* key and certificate paths between the parties. This is intended for use as a sub-flow of another flow which builds a
|
||||
* transaction. The flow running on the other side must also call this flow at the correct location.
|
||||
*
|
||||
* NOTE: This is an inlined flow but for backwards compatibility is annotated with [InitiatingFlow].
|
||||
*/
|
||||
class SwapIdentitiesFlow @JvmOverloads constructor(private val otherSideSession: FlowSession,
|
||||
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<SwapIdentitiesFlow.AnonymousResult>() {
|
||||
@InitiatingFlow
|
||||
class SwapIdentitiesFlow
|
||||
private constructor(private val otherSideSession: FlowSession?,
|
||||
private val otherParty: Party?,
|
||||
override val progressTracker: ProgressTracker) : FlowLogic<LinkedHashMap<Party, AnonymousParty>>() {
|
||||
|
||||
@JvmOverloads
|
||||
constructor(otherSideSession: FlowSession, progressTracker: ProgressTracker = tracker()) : this(otherSideSession, null, progressTracker)
|
||||
|
||||
@Deprecated("It is unsafe to use this constructor as it requires nodes to automatically vend anonymous identities without first " +
|
||||
"checking if they should. Instead, use the constructor that takes in an existing FlowSession.")
|
||||
constructor(otherParty: Party, revocationEnabled: Boolean, progressTracker: ProgressTracker) : this(null, otherParty, progressTracker)
|
||||
|
||||
@Deprecated("It is unsafe to use this constructor as it requires nodes to automatically vend anonymous identities without first " +
|
||||
"checking if they should. Instead, use the constructor that takes in an existing FlowSession.")
|
||||
constructor(otherParty: Party) : this(null, otherParty, tracker())
|
||||
|
||||
companion object {
|
||||
object GENERATING_IDENTITY : ProgressTracker.Step("Generating our anonymous identity")
|
||||
object SIGNING_IDENTITY : ProgressTracker.Step("Signing our anonymous identity")
|
||||
@ -69,38 +89,49 @@ class SwapIdentitiesFlow @JvmOverloads constructor(private val otherSideSession:
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
override fun call(): AnonymousResult {
|
||||
override fun call(): LinkedHashMap<Party, AnonymousParty> {
|
||||
val session = otherSideSession ?: run {
|
||||
logger.warnOnce("The current usage of SwapIdentitiesFlow is unsafe. Please consider upgrading your CorDapp to use " +
|
||||
"SwapIdentitiesFlow with FlowSessions. (${CordappResolver.currentCordapp?.info})")
|
||||
initiateFlow(otherParty!!)
|
||||
}
|
||||
progressTracker.currentStep = GENERATING_IDENTITY
|
||||
val ourAnonymousIdentity = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, false)
|
||||
val data = buildDataToSign(ourAnonymousIdentity)
|
||||
progressTracker.currentStep = SIGNING_IDENTITY
|
||||
val signature = serviceHub.keyManagementService.sign(data, ourAnonymousIdentity.owningKey).withoutKey()
|
||||
val ourIdentWithSig = IdentityWithSignature(ourAnonymousIdentity, signature)
|
||||
val ourIdentWithSig = IdentityWithSignature(ourAnonymousIdentity.serialize(), signature)
|
||||
progressTracker.currentStep = AWAITING_IDENTITY
|
||||
val theirAnonymousIdentity = otherSideSession.sendAndReceive<IdentityWithSignature>(ourIdentWithSig).unwrap { theirIdentWithSig ->
|
||||
val theirAnonymousIdentity = session.sendAndReceive<IdentityWithSignature>(ourIdentWithSig).unwrap { theirIdentWithSig ->
|
||||
progressTracker.currentStep = VERIFYING_IDENTITY
|
||||
validateAndRegisterIdentity(serviceHub, otherSideSession.counterparty, theirIdentWithSig.identity, theirIdentWithSig.signature)
|
||||
validateAndRegisterIdentity(serviceHub, session.counterparty, theirIdentWithSig.identity.deserialize(), theirIdentWithSig.signature)
|
||||
}
|
||||
return AnonymousResult(ourAnonymousIdentity.party.anonymise(), theirAnonymousIdentity.party.anonymise())
|
||||
|
||||
val identities = LinkedHashMap<Party, AnonymousParty>()
|
||||
identities[ourIdentity] = ourAnonymousIdentity.party.anonymise()
|
||||
identities[session.counterparty] = theirAnonymousIdentity.party.anonymise()
|
||||
return identities
|
||||
}
|
||||
|
||||
/**
|
||||
* Result class containing the caller's anonymous identity ([ourIdentity]) and the counterparty's ([theirIdentity]).
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class AnonymousResult(val ourIdentity: AnonymousParty, val theirIdentity: AnonymousParty)
|
||||
|
||||
@CordaSerializable
|
||||
private data class IdentityWithSignature(val identity: PartyAndCertificate, val signature: DigitalSignature)
|
||||
|
||||
/**
|
||||
* Data class used only in the context of asserting that the owner of the private key for the listed key wants to use it
|
||||
* to represent the named entity. This is paired with an X.509 certificate (which asserts the signing identity says
|
||||
* the key represents the named entity) and protects against a malicious party incorrectly claiming others'
|
||||
* keys.
|
||||
*/
|
||||
@CordaSerializable
|
||||
private data class CertificateOwnershipAssertion(val name: CordaX500Name, val owningKey: PublicKey)
|
||||
data class IdentityWithSignature(val identity: SerializedBytes<PartyAndCertificate>, val signature: DigitalSignature)
|
||||
}
|
||||
|
||||
// Data class used only in the context of asserting that the owner of the private key for the listed key wants to use it
|
||||
// to represent the named entity. This is paired with an X.509 certificate (which asserts the signing identity says
|
||||
// the key represents the named entity) and protects against a malicious party incorrectly claiming others'
|
||||
// keys.
|
||||
@CordaSerializable
|
||||
data class CertificateOwnershipAssertion(val x500Name: CordaX500Name, val publicKey: PublicKey)
|
||||
|
||||
open class SwapIdentitiesException @JvmOverloads constructor(message: String, cause: Throwable? = null) : FlowException(message, cause)
|
||||
|
||||
// This only exists for backwards compatibility
|
||||
@InitiatedBy(SwapIdentitiesFlow::class)
|
||||
private class SwapIdentitiesHandler(private val otherSide: FlowSession) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
subFlow(SwapIdentitiesFlow(otherSide))
|
||||
logger.warnOnce("Insecure API to swap anonymous identities was used by ${otherSide.counterparty} (${otherSide.getCounterpartyFlowInfo()})")
|
||||
}
|
||||
}
|
||||
|
@ -171,10 +171,7 @@ class SwapIdentitiesFlowTests {
|
||||
@InitiatingFlow
|
||||
private class SwapIdentitiesInitiator(private val otherSide: Party) : FlowLogic<Map<Party, AnonymousParty>>() {
|
||||
@Suspendable
|
||||
override fun call(): Map<Party, AnonymousParty> {
|
||||
val (anonymousUs, anonymousThem) = subFlow(SwapIdentitiesFlow(initiateFlow(otherSide)))
|
||||
return mapOf(ourIdentity to anonymousUs, otherSide to anonymousThem)
|
||||
}
|
||||
override fun call(): Map<Party, AnonymousParty> = subFlow(SwapIdentitiesFlow(initiateFlow(otherSide)))
|
||||
}
|
||||
|
||||
@InitiatedBy(SwapIdentitiesInitiator::class)
|
||||
|
@ -7,6 +7,7 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.groupAbstractPartyByWellKnownParty
|
||||
import net.corda.core.internal.cordapp.CordappResolver
|
||||
import net.corda.core.internal.pushToLoggingContext
|
||||
import net.corda.core.internal.warnOnce
|
||||
import net.corda.core.node.StatesToRecord
|
||||
import net.corda.core.node.StatesToRecord.ONLY_RELEVANT
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
@ -117,8 +118,8 @@ class FinalityFlow private constructor(val transaction: SignedTransaction,
|
||||
"A flow session for each external participant to the transaction must be provided. If you wish to continue " +
|
||||
"using this insecure API then specify a target platform version of less than 4 for your CorDapp."
|
||||
}
|
||||
logger.warn("The current usage of FinalityFlow is unsafe. Please consider upgrading your CorDapp to use " +
|
||||
"FinalityFlow with FlowSessions.")
|
||||
logger.warnOnce("The current usage of FinalityFlow is unsafe. Please consider upgrading your CorDapp to use " +
|
||||
"FinalityFlow with FlowSessions. (${CordappResolver.currentCordapp?.info})")
|
||||
} else {
|
||||
require(sessions.none { serviceHub.myInfo.isLegalIdentity(it.counterparty) }) {
|
||||
"Do not provide flow sessions for the local node. FinalityFlow will record the notarised transaction locally."
|
||||
|
@ -55,17 +55,6 @@ Version 4.0
|
||||
* The experimental confidential-identities is now a separate CorDapp and must now be loaded onto the node alongside any CorDapp that needs it.
|
||||
This also means your gradle dependency for it should be ``cordapp`` and not ``cordaCompile``.
|
||||
|
||||
* ``SwapIdentitiesFlow``, from the experimental confidential-identities module, is now an inlined flow. Instead of passing in a ``Party`` with
|
||||
whom to exchange the anonymous identity, a ``FlowSession`` to that party is required instead. The flow running on the other side must
|
||||
also call ``SwapIdentitiesFlow``. This change was required as the previous API allowed any counterparty to generate anonoymous identities
|
||||
with a node at will with no checks.
|
||||
|
||||
The result type has changed to a simple wrapper class, instead of a Map, to make extracting the identities easier. Also, the wire protocol
|
||||
of the flow has slightly changed.
|
||||
|
||||
.. note:: V3 and V4 of confidential-identities are not compatible and confidential-identities V3 will not work with a V4 Corda node. CorDapps
|
||||
in such scenarios using confidential-identities must be updated.
|
||||
|
||||
* Fixed a bug resulting in poor vault query performance and incorrect results when sorting.
|
||||
|
||||
* Improved exception thrown by `AttachmentsClassLoader` when an attachment cannot be used because its uploader is not trusted.
|
||||
@ -103,9 +92,12 @@ Version 4.0
|
||||
|
||||
* ``FinalityFlow`` is now an inlined flow and requires ``FlowSession`` s to each party intended to receive the transaction. This is to fix the
|
||||
security problem with the old API that required every node to accept any transaction it received without any checks. Existing CorDapp
|
||||
binaries relying on this old behaviour will continue to function as previously. However, it is strongly recommended that CorDapps switch to
|
||||
binaries relying on this old behaviour will continue to function as previously. However, it is strongly recommended CorDapps switch to
|
||||
this new API. See :doc:`app-upgrade-notes` for further details.
|
||||
|
||||
* For similar reasons, ``SwapIdentitiesFlow``, from confidential-identities, is also now an inlined flow. The old API has been preserved but
|
||||
it is strongly recommended CorDapps switch to this new API. See :doc:`app-upgrade-notes` for further details.
|
||||
|
||||
* Introduced new optional network bootstrapper command line option (--minimum-platform-version) to set as a network parameter
|
||||
|
||||
* Vault storage of contract state constraints metadata and associated vault query functions to retrieve and sort by constraint type.
|
||||
|
@ -52,7 +52,7 @@ open class CashPaymentFlow(
|
||||
val recipientSession = initiateFlow(recipient)
|
||||
recipientSession.send(anonymous)
|
||||
val anonymousRecipient = if (anonymous) {
|
||||
subFlow(SwapIdentitiesFlow(recipientSession)).theirIdentity
|
||||
subFlow(SwapIdentitiesFlow(recipientSession))[recipient]!!
|
||||
} else {
|
||||
recipient
|
||||
}
|
||||
|
@ -55,7 +55,9 @@ object TwoPartyDealFlow {
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
progressTracker.currentStep = GENERATING_ID
|
||||
val (anonymousMe, anonymousCounterparty) = subFlow(SwapIdentitiesFlow(otherSideSession))
|
||||
val txIdentities = subFlow(SwapIdentitiesFlow(otherSideSession))
|
||||
val anonymousMe = txIdentities[ourIdentity]!!
|
||||
val anonymousCounterparty = txIdentities[otherSideSession.counterparty]!!
|
||||
// DOCEND 2
|
||||
progressTracker.currentStep = SENDING_PROPOSAL
|
||||
// Make the first message we'll send to kick off the flow.
|
||||
|
@ -7,6 +7,7 @@ import net.corda.core.contracts.requireThat
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.ContractUpgradeUtils
|
||||
import net.corda.core.internal.warnOnce
|
||||
import net.corda.core.node.StatesToRecord
|
||||
import net.corda.core.transactions.ContractUpgradeWireTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -15,6 +16,7 @@ class FinalityHandler(val sender: FlowSession) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
subFlow(ReceiveTransactionFlow(sender, true, StatesToRecord.ONLY_RELEVANT))
|
||||
logger.warnOnce("Insecure API to record finalised transaction was used by ${sender.counterparty} (${sender.getCounterpartyFlowInfo()})")
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user