mirror of
https://github.com/corda/corda.git
synced 2024-12-20 21:43:14 +00:00
Add utility functions for requesting transaction keys
Add utility functions for requesting transaction keys and a small example flow which uses these functions for testing purposes. These keys are required in for the anonymisation of transactions, by providing per-transaction keys which cannot be associated with the owning party's keys without an intermediary certificate.
This commit is contained in:
parent
cec4e20bc8
commit
c73a2e6034
43
core/src/main/kotlin/net/corda/flows/TxKeyFlowUtilities.kt
Normal file
43
core/src/main/kotlin/net/corda/flows/TxKeyFlowUtilities.kt
Normal file
@ -0,0 +1,43 @@
|
||||
package net.corda.flows
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.crypto.Party
|
||||
import net.corda.core.crypto.composite
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.utilities.unwrap
|
||||
import java.security.cert.Certificate
|
||||
|
||||
object TxKeyFlowUtilities {
|
||||
/**
|
||||
* Receive a key from a counterparty. This would normally be triggered by a flow as part of a transaction assembly
|
||||
* process.
|
||||
*/
|
||||
@Suspendable
|
||||
fun receiveKey(flow: FlowLogic<*>, otherSide: Party): Pair<CompositeKey, Certificate?> {
|
||||
val untrustedKey = flow.receive<Response>(otherSide)
|
||||
return untrustedKey.unwrap {
|
||||
// TODO: Verify the certificate connects the given key to the counterparty, once we have certificates
|
||||
Pair(it.key, it.certificate)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new key and then returns it to the counterparty and as the result from the function. Note that this
|
||||
* is an expensive operation, and should only be called once the calling flow has confirmed it wants to be part of
|
||||
* a transaction with the counterparty, in order to avoid a DoS risk.
|
||||
*/
|
||||
@Suspendable
|
||||
fun provideKey(flow: FlowLogic<*>, otherSide: Party): CompositeKey {
|
||||
val key = flow.serviceHub.keyManagementService.freshKey().public.composite
|
||||
// TODO: Generate and sign certificate for the key, once we have signing support for composite keys
|
||||
// (in this case the legal identity key)
|
||||
flow.send(otherSide, Response(key, null))
|
||||
return key
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
data class Response(val key: CompositeKey, val certificate: Certificate?)
|
||||
}
|
58
core/src/test/kotlin/net/corda/core/flows/TxKeyFlow.kt
Normal file
58
core/src/test/kotlin/net/corda/core/flows/TxKeyFlow.kt
Normal file
@ -0,0 +1,58 @@
|
||||
package net.corda.core.flows
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.crypto.Party
|
||||
import net.corda.core.node.PluginServiceHub
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.flows.TxKeyFlowUtilities
|
||||
import java.security.cert.Certificate
|
||||
|
||||
/**
|
||||
* Very basic flow which requests a transaction key from a counterparty, used for testing [TxKeyFlowUtilities].
|
||||
* This MUST not be provided on any real node, as the ability for arbitrary parties to request keys would enable
|
||||
* DoS of the node, as key generation/storage is vastly more expensive than submitting a request.
|
||||
*/
|
||||
object TxKeyFlow {
|
||||
fun registerFlowInitiator(services: PluginServiceHub) {
|
||||
services.registerFlowInitiator(Requester::class.java, ::Provider)
|
||||
}
|
||||
|
||||
class Requester(val otherSide: Party,
|
||||
override val progressTracker: ProgressTracker): FlowLogic<Pair<CompositeKey, Certificate?>>() {
|
||||
constructor(otherSide: Party) : this(otherSide, tracker())
|
||||
|
||||
companion object {
|
||||
object AWAITING_KEY : ProgressTracker.Step("Awaiting key")
|
||||
|
||||
fun tracker() = ProgressTracker(AWAITING_KEY)
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
override fun call(): Pair<CompositeKey, Certificate?> {
|
||||
progressTracker.currentStep = AWAITING_KEY
|
||||
return TxKeyFlowUtilities.receiveKey(this, otherSide)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flow which waits for a key request from a counterparty, generates a new key and then returns it to the
|
||||
* counterparty and as the result from the flow.
|
||||
*/
|
||||
class Provider(val otherSide: Party,
|
||||
override val progressTracker: ProgressTracker): FlowLogic<CompositeKey>() {
|
||||
constructor(otherSide: Party) : this(otherSide, tracker())
|
||||
|
||||
companion object {
|
||||
object SENDING_KEY : ProgressTracker.Step("Sending key")
|
||||
|
||||
fun tracker() = ProgressTracker(SENDING_KEY)
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
override fun call(): CompositeKey {
|
||||
progressTracker.currentStep == SENDING_KEY
|
||||
return TxKeyFlowUtilities.provideKey(this, otherSide)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package net.corda.core.flows
|
||||
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.crypto.Party
|
||||
import net.corda.core.utilities.DUMMY_NOTARY
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.BOB
|
||||
import net.corda.testing.MOCK_IDENTITY_SERVICE
|
||||
import net.corda.testing.ledger
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class TxKeyFlowUtilitiesTests {
|
||||
lateinit var net: MockNetwork
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
net = MockNetwork(false)
|
||||
net.identities += MOCK_IDENTITY_SERVICE.identities
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `issue key`() {
|
||||
// We run this in parallel threads to help catch any race conditions that may exist.
|
||||
net = MockNetwork(false, true)
|
||||
|
||||
// Set up values we'll need
|
||||
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
|
||||
val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name)
|
||||
val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name)
|
||||
val aliceKey: Party = aliceNode.services.myInfo.legalIdentity
|
||||
val bobKey: Party = bobNode.services.myInfo.legalIdentity
|
||||
|
||||
// Run the flows
|
||||
TxKeyFlow.registerFlowInitiator(bobNode.services)
|
||||
val requesterFlow = aliceNode.services.startFlow(TxKeyFlow.Requester(bobKey))
|
||||
|
||||
// Get the results
|
||||
val actual: CompositeKey = requesterFlow.resultFuture.get().first
|
||||
assertNotNull(actual)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user