First cut at removing PrivateKey leakage from KeyManagementService

Fixup after rebase

Restore original key property names

Fixup after rebase

Undo extra import that IntelliJ keeps erroneously adding.

Add comments and fix docs for transaction signing.

Fixes after rebase

More fixes after rebase

Address PR requests

Address PR requests
This commit is contained in:
Matthew Nesbit 2017-05-12 17:42:13 +01:00
parent ccbe76eb84
commit 05a97b11f3
58 changed files with 454 additions and 399 deletions

View File

@ -1,10 +1,11 @@
package net.corda.core.node package net.corda.core.node
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.keys import net.corda.core.crypto.DigitalSignature
import net.corda.core.node.services.* import net.corda.core.node.services.*
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import java.security.KeyPair import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
import java.time.Clock import java.time.Clock
/** /**
@ -82,23 +83,107 @@ interface ServiceHub : ServicesForResolution {
} }
/** /**
* Helper property to shorten code for fetching the Node's KeyPair associated with the * Helper property to shorten code for fetching the the [PublicKey] portion of the
* public legalIdentity Party from the key management service. * Node's primary signing identity.
* Typical use is during signing in flows and for unit test signing. * Typical use is during signing in flows and for unit test signing.
* * When this [PublicKey] is passed into the signing methods below, or on the KeyManagementService
* TODO: legalIdentity can now be composed of multiple keys, should we return a list of keyPairs here? Right now * the matching [PrivateKey] will be looked up internally and used to sign.
* the logic assumes the legal identity has a composite key with only one node * If the key is actually a CompositeKey, the first leaf key hosted on this node
* will be used to create the signature.
*/ */
val legalIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.legalIdentity.owningKey.keys) val legalIdentityKey: PublicKey get() = this.myInfo.legalIdentity.owningKey
/** /**
* Helper property to shorten code for fetching the Node's KeyPair associated with the * Helper property to shorten code for fetching the the [PublicKey] portion of the
* public notaryIdentity Party from the key management service. It is assumed that this is only * Node's Notary signing identity. It is required that the Node hosts a notary service,
* used in contexts where the Node knows it is hosting a Notary Service. Otherwise, it will throw * otherwise an IllegalArgumentException will be thrown.
* an IllegalArgumentException.
* Typical use is during signing in flows and for unit test signing. * Typical use is during signing in flows and for unit test signing.
* * When this [PublicKey] is passed into the signing methods below, or on the KeyManagementService
* TODO: same problem as with legalIdentityKey. * the matching [PrivateKey] will be looked up internally and used to sign.
* If the key is actually a [CompositeKey], the first leaf key hosted on this node
* will be used to create the signature.
*/ */
val notaryIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.notaryIdentity.owningKey.keys) val notaryIdentityKey: PublicKey get() = this.myInfo.notaryIdentity.owningKey
}
/**
* Helper method to construct an initial partially signed transaction from a [TransactionBuilder]
* using keys stored inside the node.
* @param builder The [TransactionBuilder] to seal with the node's signature.
* Any existing signatures on the builder will be preserved.
* @param publicKey The [PublicKey] matched to the internal [PrivateKey] to use in signing this transaction.
* If the passed in key is actually a CompositeKey the code searches for the first child key hosted within this node
* to sign with.
* @return Returns a SignedTransaction with the new node signature attached.
*/
fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey): SignedTransaction {
val sig = keyManagementService.sign(builder.toWireTransaction().id.bytes, publicKey)
builder.addSignatureUnchecked(sig)
return builder.toSignedTransaction(false)
}
/**
* Helper method to construct an initial partially signed transaction from a TransactionBuilder
* using the default identity key contained in the node.
* @param builder The TransactionBuilder to seal with the node's signature.
* Any existing signatures on the builder will be preserved.
* @return Returns a SignedTransaction with the new node signature attached.
*/
fun signInitialTransaction(builder: TransactionBuilder): SignedTransaction = signInitialTransaction(builder, legalIdentityKey)
/**
* Helper method to construct an initial partially signed transaction from a [TransactionBuilder]
* using a set of keys all held in this node.
* @param builder The [TransactionBuilder] to seal with the node's signature.
* Any existing signatures on the builder will be preserved.
* @param signingPubKeys A list of [PublicKeys] used to lookup the matching [PrivateKey] and sign.
* @throws IllegalArgumentException is thrown if any keys are unavailable locally.
* @return Returns a [SignedTransaction] with the new node signature attached.
*/
fun signInitialTransaction(builder: TransactionBuilder, signingPubKeys: List<PublicKey>): SignedTransaction {
var stx: SignedTransaction? = null
for (pubKey in signingPubKeys) {
stx = if (stx == null) {
signInitialTransaction(builder, pubKey)
} else {
addSignature(stx, pubKey)
}
}
return stx!!
}
/**
* Helper method to create an additional signature for an existing (partially) [SignedTransaction].
* @param signedTransaction The [SignedTransaction] to which the signature will apply.
* @param publicKey The [PublicKey] matching to a signing [PrivateKey] hosted in the node.
* If the [PublicKey] is actually a [CompositeKey] the first leaf key found locally will be used for signing.
* @return The [DigitalSignature.WithKey] generated by signing with the internally held [PrivateKey].
*/
fun createSignature(signedTransaction: SignedTransaction, publicKey: PublicKey): DigitalSignature.WithKey = keyManagementService.sign(signedTransaction.id.bytes, publicKey)
/**
* Helper method to create an additional signature for an existing (partially) SignedTransaction
* using the default identity signing key of the node.
* @param signedTransaction The SignedTransaction to which the signature will apply.
* @return The DigitalSignature.WithKey generated by signing with the internally held identity PrivateKey.
*/
fun createSignature(signedTransaction: SignedTransaction): DigitalSignature.WithKey = createSignature(signedTransaction, legalIdentityKey)
/**
* Helper method to append an additional signature to an existing (partially) [SignedTransaction].
* @param signedTransaction The [SignedTransaction] to which the signature will be added.
* @param publicKey The [PublicKey] matching to a signing [PrivateKey] hosted in the node.
* If the [PublicKey] is actually a [CompositeKey] the first leaf key found locally will be used for signing.
* @return A new [SignedTransaction] with the addition of the new signature.
*/
fun addSignature(signedTransaction: SignedTransaction, publicKey: PublicKey): SignedTransaction = signedTransaction + createSignature(signedTransaction, publicKey)
/**
* Helper method to ap-pend an additional signature for an existing (partially) [SignedTransaction]
* using the default identity signing key of the node.
* @param signedTransaction The [SignedTransaction] to which the signature will be added.
* @return A new [SignedTransaction] with the addition of the new signature.
*/
fun addSignature(signedTransaction: SignedTransaction): SignedTransaction = addSignature(signedTransaction, legalIdentityKey)
}

View File

@ -3,9 +3,8 @@ package net.corda.core.node.services
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -20,8 +19,6 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import rx.Observable import rx.Observable
import java.io.InputStream import java.io.InputStream
import java.security.KeyPair
import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -373,32 +370,32 @@ class StatesNotAvailableException(override val message: String?, override val ca
/** /**
* The KMS is responsible for storing and using private keys to sign things. An implementation of this may, for example, * The KMS is responsible for storing and using private keys to sign things. An implementation of this may, for example,
* call out to a hardware security module that enforces various auditing and frequency-of-use requirements. * call out to a hardware security module that enforces various auditing and frequency-of-use requirements.
*
* The current interface is obviously not usable for those use cases: this is just where we'd put a real signing
* interface if/when one is developed.
*/ */
interface KeyManagementService { interface KeyManagementService {
/** Returns a snapshot of the current pubkey->privkey mapping. */ /**
val keys: Map<PublicKey, PrivateKey> * Returns a snapshot of the current signing [PublicKey]s.
* For each of these keys a [PrivateKey] is available, that can be used later for signing.
*/
val keys: Set<PublicKey>
@Throws(IllegalStateException::class) /**
fun toPrivate(publicKey: PublicKey) = keys[publicKey] ?: throw IllegalStateException("No private key known for requested public key ${publicKey.toStringShort()}") * Generates a new random [KeyPair] and adds it to the internal key storage. Returns the public part of the pair.
*/
@Suspendable
fun freshKey(): PublicKey
@Throws(IllegalArgumentException::class) /** Using the provided signing [PublicKey] internally looks up the matching [PrivateKey] and signs the data.
fun toKeyPair(publicKey: PublicKey): KeyPair { * @param bytes The data to sign over using the chosen key.
when (publicKey) { * @param publicKey The [PublicKey] partner to an internally held [PrivateKey], either derived from the node's primary identity,
is CompositeKey -> throw IllegalArgumentException("Got CompositeKey when single PublicKey expected.") * or previously generated via the [freshKey] method.
else -> return KeyPair(publicKey, toPrivate(publicKey)) * If the [PublicKey] is actually a [CompositeKey] the first leaf signing key hosted by the node is used.
} * @throws IllegalArgumentException if the input key is not a member of [keys].
} * TODO A full [KeyManagementService] implementation needs to record activity to the [AuditService] and to limit signing to
* appropriately authorised contexts and initiating users.
/** Returns the first [KeyPair] matching any of the [publicKeys] */ */
@Throws(IllegalArgumentException::class) @Suspendable
fun toKeyPair(publicKeys: Iterable<PublicKey>) = publicKeys.first { keys.contains(it) }.let { toKeyPair(it) } fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey
/** Generates a new random key and adds it to the exposed map. */
fun freshKey(): KeyPair
} }
// TODO: Move to a more appropriate location // TODO: Move to a more appropriate location

View File

@ -3,14 +3,12 @@ package net.corda.core.transactions
import net.corda.core.contracts.AttachmentResolutionException import net.corda.core.contracts.AttachmentResolutionException
import net.corda.core.contracts.NamedByHash import net.corda.core.contracts.NamedByHash
import net.corda.core.contracts.TransactionResolutionException import net.corda.core.contracts.TransactionResolutionException
import net.corda.core.node.ServiceHub
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.sign import net.corda.core.node.ServiceHub
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
import java.util.* import java.util.*
@ -146,14 +144,5 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class, SignatureException::class) @Throws(AttachmentResolutionException::class, TransactionResolutionException::class, SignatureException::class)
fun toLedgerTransaction(services: ServiceHub) = verifySignatures().toLedgerTransaction(services) fun toLedgerTransaction(services: ServiceHub) = verifySignatures().toLedgerTransaction(services)
/**
* Utility to simplify the act of signing the transaction.
*
* @param keyPair the signer's public/private key pair.
*
* @return a digital signature of the transaction.
*/
fun signWithECDSA(keyPair: KeyPair) = keyPair.sign(this.id.bytes)
override fun toString(): String = "${javaClass.simpleName}(id=$id)" override fun toString(): String = "${javaClass.simpleName}(id=$id)"
} }

View File

@ -4,7 +4,8 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.* import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
@ -188,8 +189,7 @@ abstract class AbstractStateReplacementFlow {
} }
private fun sign(stx: SignedTransaction): DigitalSignature.WithKey { private fun sign(stx: SignedTransaction): DigitalSignature.WithKey {
val myKey = serviceHub.legalIdentityKey return serviceHub.createSignature(stx)
return myKey.sign(stx.id)
} }
} }
} }

View File

@ -1,7 +1,9 @@
package net.corda.flows package net.corda.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.* import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.toBase58String
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -201,7 +203,7 @@ abstract class SignTransactionFlow(val otherParty: Party,
// Sign and send back our signature to the Initiator. // Sign and send back our signature to the Initiator.
progressTracker.currentStep = SIGNING progressTracker.currentStep = SIGNING
val mySignature = serviceHub.legalIdentityKey.sign(checkedProposal.id) val mySignature = serviceHub.createSignature(checkedProposal)
send(otherParty, mySignature) send(otherParty, mySignature)
// Return the fully signed transaction once it has been committed. // Return the fully signed transaction once it has been committed.

View File

@ -59,9 +59,8 @@ class ContractUpgradeFlow<OldState : ContractState, out NewState : ContractState
} }
override fun assembleTx(): Pair<SignedTransaction, Iterable<AbstractParty>> { override fun assembleTx(): Pair<SignedTransaction, Iterable<AbstractParty>> {
val stx = assembleBareTx(originalState, modification) val baseTx = assembleBareTx(originalState, modification)
.signWith(serviceHub.legalIdentityKey) val stx = serviceHub.signInitialTransaction(baseTx)
.toSignedTransaction(false)
return stx to originalState.state.data.participants return stx to originalState.state.data.participants
} }
} }

View File

@ -40,10 +40,7 @@ class NotaryChangeFlow<out T : ContractState>(
participants = resolveEncumbrances(tx) participants = resolveEncumbrances(tx)
} }
val myKey = serviceHub.legalIdentityKey val stx = serviceHub.signInitialTransaction(tx)
tx.signWith(myKey)
val stx = tx.toSignedTransaction(false)
return Pair(stx, participants) return Pair(stx, participants)
} }

View File

@ -3,7 +3,10 @@ package net.corda.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.Timestamp import net.corda.core.contracts.Timestamp
import net.corda.core.crypto.* import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.keys
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
@ -144,8 +147,7 @@ object NotaryFlow {
} }
private fun sign(bits: ByteArray): DigitalSignature.WithKey { private fun sign(bits: ByteArray): DigitalSignature.WithKey {
val mySigningKey = serviceHub.notaryIdentityKey return serviceHub.keyManagementService.sign(bits, serviceHub.notaryIdentityKey)
return mySigningKey.sign(bits)
} }
private fun notaryException(txId: SecureHash, e: UniquenessException): NotaryException { private fun notaryException(txId: SecureHash, e: UniquenessException): NotaryException {

View File

@ -17,7 +17,6 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
/** /**
@ -48,7 +47,7 @@ object TwoPartyDealFlow {
abstract val payload: Any abstract val payload: Any
abstract val notaryNode: NodeInfo abstract val notaryNode: NodeInfo
abstract val otherParty: Party abstract val otherParty: Party
abstract val myKeyPair: KeyPair abstract val myKey: PublicKey
@Suspendable override fun call(): SignedTransaction { @Suspendable override fun call(): SignedTransaction {
progressTracker.currentStep = SENDING_PROPOSAL progressTracker.currentStep = SENDING_PROPOSAL
@ -138,12 +137,7 @@ object TwoPartyDealFlow {
private fun signWithOurKeys(signingPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction { private fun signWithOurKeys(signingPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction {
// Now sign the transaction with whatever keys we need to move the cash. // Now sign the transaction with whatever keys we need to move the cash.
for (publicKey in signingPubKeys.expandedCompositeKeys) { return serviceHub.signInitialTransaction(ptx, signingPubKeys)
val privateKey = serviceHub.keyManagementService.toPrivate(publicKey)
ptx.signWith(KeyPair(publicKey, privateKey))
}
return ptx.toSignedTransaction(checkSufficientSignatures = false)
} }
@Suspendable protected abstract fun validateHandshake(handshake: Handshake<U>): Handshake<U> @Suspendable protected abstract fun validateHandshake(handshake: Handshake<U>): Handshake<U>
@ -158,7 +152,7 @@ object TwoPartyDealFlow {
*/ */
open class Instigator(override val otherParty: Party, open class Instigator(override val otherParty: Party,
override val payload: AutoOffer, override val payload: AutoOffer,
override val myKeyPair: KeyPair, override val myKey: PublicKey,
override val progressTracker: ProgressTracker = Primary.tracker()) : Primary() { override val progressTracker: ProgressTracker = Primary.tracker()) : Primary() {
override val notaryNode: NodeInfo get() = override val notaryNode: NodeInfo get() =

View File

@ -29,7 +29,7 @@ object TxKeyFlowUtilities {
*/ */
@Suspendable @Suspendable
fun provideKey(flow: FlowLogic<*>, otherSide: Party): PublicKey { fun provideKey(flow: FlowLogic<*>, otherSide: Party): PublicKey {
val key = flow.serviceHub.keyManagementService.freshKey().public val key = flow.serviceHub.keyManagementService.freshKey()
// TODO: Generate and sign certificate for the key, once we have signing support for composite keys // TODO: Generate and sign certificate for the key, once we have signing support for composite keys
// (in this case the legal identity key) // (in this case the legal identity key)
flow.send(otherSide, ProvidedTransactionKey(key, null)) flow.send(otherSide, ProvidedTransactionKey(key, null))

View File

@ -90,7 +90,7 @@ class CollectSignaturesFlowTests {
val command = Command(DummyContract.Commands.Create(), state.participants.map { it.owningKey }) val command = Command(DummyContract.Commands.Create(), state.participants.map { it.owningKey })
val builder = TransactionType.General.Builder(notary = notary).withItems(state, command) val builder = TransactionType.General.Builder(notary = notary).withItems(state, command)
val ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false) val ptx = serviceHub.signInitialTransaction(builder)
val stx = subFlow(CollectSignaturesFlow(ptx)) val stx = subFlow(CollectSignaturesFlow(ptx))
val ftx = subFlow(FinalityFlow(stx)).single() val ftx = subFlow(FinalityFlow(stx)).single()
@ -110,7 +110,7 @@ class CollectSignaturesFlowTests {
val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
val command = Command(DummyContract.Commands.Create(), state.participants.map { it.owningKey }) val command = Command(DummyContract.Commands.Create(), state.participants.map { it.owningKey })
val builder = TransactionType.General.Builder(notary = notary).withItems(state, command) val builder = TransactionType.General.Builder(notary = notary).withItems(state, command)
val ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false) val ptx = serviceHub.signInitialTransaction(builder)
val stx = subFlow(CollectSignaturesFlow(ptx)) val stx = subFlow(CollectSignaturesFlow(ptx))
val ftx = subFlow(FinalityFlow(stx)).single() val ftx = subFlow(FinalityFlow(stx)).single()
@ -154,7 +154,7 @@ class CollectSignaturesFlowTests {
@Test @Test
fun `no need to collect any signatures`() { fun `no need to collect any signatures`() {
val onePartyDummyContract = DummyContract.generateInitial(1337, notary, a.info.legalIdentity.ref(1)) val onePartyDummyContract = DummyContract.generateInitial(1337, notary, a.info.legalIdentity.ref(1))
val ptx = onePartyDummyContract.signWith(a.services.legalIdentityKey).toSignedTransaction(false) val ptx = a.services.signInitialTransaction(onePartyDummyContract)
val flow = a.services.startFlow(CollectSignaturesFlow(ptx)) val flow = a.services.startFlow(CollectSignaturesFlow(ptx))
mockNet.runNetwork() mockNet.runNetwork()
val result = flow.resultFuture.getOrThrow() val result = flow.resultFuture.getOrThrow()
@ -180,8 +180,9 @@ class CollectSignaturesFlowTests {
a.info.legalIdentity.ref(1), a.info.legalIdentity.ref(1),
b.info.legalIdentity.ref(2), b.info.legalIdentity.ref(2),
b.info.legalIdentity.ref(3)) b.info.legalIdentity.ref(3))
val ptx = twoPartyDummyContract.signWith(a.services.legalIdentityKey).signWith(b.services.legalIdentityKey).toSignedTransaction(false) val signedByA = a.services.signInitialTransaction(twoPartyDummyContract)
val flow = a.services.startFlow(CollectSignaturesFlow(ptx)) val signedByBoth = b.services.addSignature(signedByA)
val flow = a.services.startFlow(CollectSignaturesFlow(signedByBoth))
mockNet.runNetwork() mockNet.runNetwork()
val result = flow.resultFuture.getOrThrow() val result = flow.resultFuture.getOrThrow()
println(result.tx) println(result.tx)

View File

@ -58,9 +58,8 @@ class ContractUpgradeFlowTest {
fun `2 parties contract upgrade`() { fun `2 parties contract upgrade`() {
// Create dummy contract. // Create dummy contract.
val twoPartyDummyContract = DummyContract.generateInitial(0, notary, a.info.legalIdentity.ref(1), b.info.legalIdentity.ref(1)) val twoPartyDummyContract = DummyContract.generateInitial(0, notary, a.info.legalIdentity.ref(1), b.info.legalIdentity.ref(1))
val stx = twoPartyDummyContract.signWith(a.services.legalIdentityKey) val signedByA = a.services.signInitialTransaction(twoPartyDummyContract)
.signWith(b.services.legalIdentityKey) val stx = b.services.addSignature(signedByA)
.toSignedTransaction()
a.services.startFlow(FinalityFlow(stx, setOf(a.info.legalIdentity, b.info.legalIdentity))) a.services.startFlow(FinalityFlow(stx, setOf(a.info.legalIdentity, b.info.legalIdentity)))
mockNet.runNetwork() mockNet.runNetwork()
@ -120,9 +119,8 @@ class ContractUpgradeFlowTest {
rpcDriver { rpcDriver {
// Create dummy contract. // Create dummy contract.
val twoPartyDummyContract = DummyContract.generateInitial(0, notary, a.info.legalIdentity.ref(1), b.info.legalIdentity.ref(1)) val twoPartyDummyContract = DummyContract.generateInitial(0, notary, a.info.legalIdentity.ref(1), b.info.legalIdentity.ref(1))
val stx = twoPartyDummyContract.signWith(a.services.legalIdentityKey) val signedByA = a.services.signInitialTransaction(twoPartyDummyContract)
.signWith(b.services.legalIdentityKey) val stx = b.services.addSignature(signedByA)
.toSignedTransaction()
val user = rpcTestUser.copy(permissions = setOf( val user = rpcTestUser.copy(permissions = setOf(
startFlowPermission<FinalityInvoker>(), startFlowPermission<FinalityInvoker>(),

View File

@ -32,7 +32,6 @@ UNRELEASED
* ``FlowLogic.getCounterpartyMarker`` is no longer used and been deprecated for removal. If you were using this to * ``FlowLogic.getCounterpartyMarker`` is no longer used and been deprecated for removal. If you were using this to
manage multiple independent message streams with the same party in the same flow then use sub-flows instead. manage multiple independent message streams with the same party in the same flow then use sub-flows instead.
* There are major changes to the ``Party`` class as part of confidential identities: * There are major changes to the ``Party`` class as part of confidential identities:
* ``Party`` has moved to the ``net.corda.core.identity`` package; there is a deprecated class in its place for * ``Party`` has moved to the ``net.corda.core.identity`` package; there is a deprecated class in its place for
@ -44,6 +43,25 @@ UNRELEASED
* Names of parties are now stored as a ``X500Name`` rather than a ``String``, to correctly enforce basic structure of the * Names of parties are now stored as a ``X500Name`` rather than a ``String``, to correctly enforce basic structure of the
name. As a result all node legal names must now be structured as X.500 distinguished names. name. As a result all node legal names must now be structured as X.500 distinguished names.
* There are major changes to the ``KeyManagementService`` and transaction signing in flows:
* ``ServiceHub.legalIdentityKey`` no longer returns a ``KeyPair`` it instead returns just the ``PublicKey`` portion of this pair.
The ``ServiceHub.notaryIdentityKey`` has changed similarly.
* The ``KeyManagementService`` now provides no mechanism to request the node's ``PrivateKey`` objects directly.
Instead signature creation occurs in the ``KeyManagementService.sign``, with the ``PublicKey`` used to indicate
which of the node's multiple keys to use. This lookup also works for ``CompositeKey`` scenarios
and the service will search for a leaf key hosted on the node.
* The ``KeyManagementService.freshKey`` method now returns only the ``PublicKey`` portion of the newly generated ``KeyPair``
with the ``PrivateKey kept internally to the service.
* Flows which used to acquire a node's ``KeyPair``, typically via ``ServiceHub.legalIdentityKey``,
should instead use the helper methods on ``ServiceHub``. In particular to freeze a ``TransactionBuilder`` and
generate an initial partially signed ``SignedTransaction`` the flow should use ``ServiceHub.signInitialTransaction``.
Flows generating additional party signatures should use ``ServiceHub.createSignature``. Each of these methods is
provided with two signatures. One version that signs with the default node key, the other which allows key slection
by passing in the ``PublicKey`` partner of the desired signing key.
* The original ``KeyPair`` signing methods have been left on the ``TransactionBuilder`` and ``SignedTransaction``, but
should only be used as part of unit testing.
* The ``InitiatingFlow`` annotation also has an integer ``version`` property which assigns the initiating flow a version * The ``InitiatingFlow`` annotation also has an integer ``version`` property which assigns the initiating flow a version
number, defaulting to 1 if it's specified. The flow version is included in the flow session request and the counterparty number, defaulting to 1 if it's specified. The flow version is included in the flow session request and the counterparty
will only respond and start their own flow if the version number matches to the one they've registered with. At some will only respond and start their own flow if the version number matches to the one they've registered with. At some

View File

@ -8,7 +8,6 @@ import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sign
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -206,11 +205,9 @@ class ForeignExchangeFlow(val tradeId: String,
builder.withItems(*theirStates.outputs.toTypedArray()) builder.withItems(*theirStates.outputs.toTypedArray())
// We have already validated their response and trust our own data // We have already validated their response and trust our own data
// so we can sign // so we can sign. Note the returned SignedTransaction is still not fully signed
builder.signWith(serviceHub.legalIdentityKey) // and would not pass full verification yet.
// create a signed transaction, but pass false as parameter, because we know it is not fully signed return serviceHub.signInitialTransaction(builder)
val signedTransaction = builder.toSignedTransaction(checkSufficientSignatures = false)
return signedTransaction
} }
// DOCEND 3 // DOCEND 3
} }
@ -260,7 +257,7 @@ class ForeignExchangeRemoteFlow(val source: Party) : FlowLogic<Unit>() {
} }
// assuming we have completed state and business level validation we can sign the trade // assuming we have completed state and business level validation we can sign the trade
val ourSignature = serviceHub.legalIdentityKey.sign(proposedTrade.id) val ourSignature = serviceHub.createSignature(proposedTrade)
// send the other side our signature. // send the other side our signature.
send(source, ourSignature) send(source, ourSignature)

View File

@ -5,7 +5,6 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.containsAny import net.corda.core.crypto.containsAny
import net.corda.core.crypto.sign
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
@ -135,9 +134,7 @@ class SubmitTradeApprovalFlow(val tradeId: String,
.withItems(tradeProposal, Command(TradeApprovalContract.Commands.Issue(), listOf(tradeProposal.source.owningKey))) .withItems(tradeProposal, Command(TradeApprovalContract.Commands.Issue(), listOf(tradeProposal.source.owningKey)))
tx.setTime(serviceHub.clock.instant(), Duration.ofSeconds(60)) tx.setTime(serviceHub.clock.instant(), Duration.ofSeconds(60))
// We can automatically sign as there is no untrusted data. // We can automatically sign as there is no untrusted data.
tx.signWith(serviceHub.legalIdentityKey) val signedTx = serviceHub.signInitialTransaction(tx)
// Convert to a SignedTransaction that we can send to the notary
val signedTx = tx.toSignedTransaction(false)
// Notarise and distribute. // Notarise and distribute.
subFlow(FinalityFlow(signedTx, setOf(serviceHub.myInfo.legalIdentity, counterparty))) subFlow(FinalityFlow(signedTx, setOf(serviceHub.myInfo.legalIdentity, counterparty)))
// Return the initial state // Return the initial state
@ -199,9 +196,9 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow
tx.setTime(serviceHub.clock.instant(), Duration.ofSeconds(60)) tx.setTime(serviceHub.clock.instant(), Duration.ofSeconds(60))
// We can sign this transaction immediately as we have already checked all the fields and the decision // We can sign this transaction immediately as we have already checked all the fields and the decision
// is ultimately a manual one from the caller. // is ultimately a manual one from the caller.
tx.signWith(serviceHub.legalIdentityKey) // As a SignedTransaction we can pass the data around certain that it cannot be modified,
// Convert to SignedTransaction we can pass around certain that it cannot be modified. // although we do require further signatures to complete the process.
val selfSignedTx = tx.toSignedTransaction(false) val selfSignedTx = serviceHub.signInitialTransaction(tx)
//DOCEND 2 //DOCEND 2
// Send the signed transaction to the originator and await their signature to confirm // Send the signed transaction to the originator and await their signature to confirm
val allPartySignedTx = sendAndReceive<DigitalSignature.WithKey>(newState.source, selfSignedTx).unwrap { val allPartySignedTx = sendAndReceive<DigitalSignature.WithKey>(newState.source, selfSignedTx).unwrap {
@ -257,7 +254,7 @@ class RecordCompletionFlow(val source: Party) : FlowLogic<Unit>() {
} }
// DOCEND 3 // DOCEND 3
// Having verified the SignedTransaction passed to us we can sign it too // Having verified the SignedTransaction passed to us we can sign it too
val ourSignature = serviceHub.legalIdentityKey.sign(completeTx.tx.id) val ourSignature = serviceHub.createSignature(completeTx)
// Send our signature to the other party. // Send our signature to the other party.
send(source, ourSignature) send(source, ourSignature)
// N.B. The FinalityProtocol will be responsible for Notarising the SignedTransaction // N.B. The FinalityProtocol will be responsible for Notarising the SignedTransaction

View File

@ -8,7 +8,6 @@ import net.corda.core.toFuture
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.flows.CashIssueFlow import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.services.network.NetworkMapService import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction

View File

@ -131,7 +131,7 @@ each side.
val notaryNode: NodeInfo, val notaryNode: NodeInfo,
val assetToSell: StateAndRef<OwnableState>, val assetToSell: StateAndRef<OwnableState>,
val price: Amount<Currency>, val price: Amount<Currency>,
val myKeyPair: KeyPair, val myKey: PublicKey,
override val progressTracker: ProgressTracker = Seller.tracker()) : FlowLogic<SignedTransaction>() { override val progressTracker: ProgressTracker = Seller.tracker()) : FlowLogic<SignedTransaction>() {
@Suspendable @Suspendable
override fun call(): SignedTransaction { override fun call(): SignedTransaction {
@ -160,7 +160,8 @@ Going through the data needed to become a seller, we have:
information on notaries. information on notaries.
- ``assetToSell: StateAndRef<OwnableState>`` - a pointer to the ledger entry that represents the thing being sold. - ``assetToSell: StateAndRef<OwnableState>`` - a pointer to the ledger entry that represents the thing being sold.
- ``price: Amount<Currency>`` - the agreed on price that the asset is being sold for (without an issuer constraint). - ``price: Amount<Currency>`` - the agreed on price that the asset is being sold for (without an issuer constraint).
- ``myKeyPair: KeyPair`` - the key pair that controls the asset being sold. It will be used to sign the transaction. - ``myKey: PublicKey`` - the PublicKey part of the node's internal KeyPair that controls the asset being sold.
The matching PrivateKey stored in the KeyManagementService will be used to sign the transaction.
And for the buyer: And for the buyer:
@ -439,7 +440,7 @@ to create a simple seller starter flow that has the annotation we need:
@Suspendable @Suspendable
override fun call(): SignedTransaction { override fun call(): SignedTransaction {
val notary: NodeInfo = serviceHub.networkMapCache.notaryNodes[0] val notary: NodeInfo = serviceHub.networkMapCache.notaryNodes[0]
val cpOwnerKey: KeyPair = serviceHub.legalIdentityKey val cpOwnerKey: PublicKey = serviceHub.legalIdentityKey
return subFlow(TwoPartyTradeFlow.Seller(otherParty, notary, assetToSell, price, cpOwnerKey)) return subFlow(TwoPartyTradeFlow.Seller(otherParty, notary, assetToSell, price, cpOwnerKey))
} }
} }

View File

@ -55,10 +55,9 @@ resolving the attachment references to the attachments. Commands with valid sign
When constructing a new transaction from scratch, you use ``TransactionBuilder``, which is a mutable transaction that When constructing a new transaction from scratch, you use ``TransactionBuilder``, which is a mutable transaction that
can be signed once its construction is complete. This builder class should be used to create the initial transaction representation can be signed once its construction is complete. This builder class should be used to create the initial transaction representation
(before signature, before verification). It is intended to be passed around code that may edit it by adding new states/commands. (before signature, before verification). It is intended to be passed around code that may edit it by adding new states/commands.
Then once the states and commands are right, this class can be used as a holding bucket to gather signatures from multiple parties. Then once the states and commands are right then an initial DigitalSignature.WithKey can be added to freeze the transaction data.
It is typical for contract classes to expose helper methods that can contribute to a ``TransactionBuilder``. Once a transaction Typically, the signInitialTransaction method on the flow's serviceHub object will be used to look up the default node identity PrivateKey,
has been constructed using the builders ``toWireTransaction`` or ``toSignedTransaction`` function, it shared with other sign the transaction and return a partially signed SignedTransaction. This can then be distributed to other participants using the :doc:`key-concepts-flow-framework`.
participants using the :doc:`key-concepts-flow-framework`.
Here's an example of building a transaction that creates an issuance of bananas (note that bananas are not a real Here's an example of building a transaction that creates an issuance of bananas (note that bananas are not a real
contract type in the library): contract type in the library):
@ -69,10 +68,9 @@ contract type in the library):
val notaryToUse: Party = ... val notaryToUse: Party = ...
val txb = TransactionBuilder(notary = notaryToUse).withItems(BananaState(Amount(20, Bananas), fromCountry = "Elbonia")) val txb = TransactionBuilder(notary = notaryToUse).withItems(BananaState(Amount(20, Bananas), fromCountry = "Elbonia"))
txb.signWith(myKey)
txb.setTime(Instant.now(), notaryToUse, 30.seconds) txb.setTime(Instant.now(), notaryToUse, 30.seconds)
// We must disable the check for sufficient signatures, because this transaction is not yet notarised. // Carry out the initial signing of the transaction and creation of a (partial) SignedTransation.
val stx = txb.toSignedTransaction(checkSufficientSignatures = false) val stx = serviceHub.signInitialTransaction(txb)
// Alternatively, let's just check it verifies pretending it was fully signed. To do this, we get // Alternatively, let's just check it verifies pretending it was fully signed. To do this, we get
// a WireTransaction, which is what the SignedTransaction wraps. Thus by verifying that directly we // a WireTransaction, which is what the SignedTransaction wraps. Thus by verifying that directly we
// skip signature checking. // skip signature checking.

View File

@ -65,12 +65,12 @@ PersistentKeyManagementService and E2ETestKeyManagementService
Typical usage of these services is to locate an appropriate Typical usage of these services is to locate an appropriate
``PrivateKey`` to complete and sign a verified transaction as part of a ``PrivateKey`` to complete and sign a verified transaction as part of a
flow. The normal node legal identifier keys are typically accessed via flow. The normal node legal identifier keys are typically accessed via
helper extension methods on the ``ServiceHub``, but these ultimately helper extension methods on the ``ServiceHub``, but these ultimately delegate
fetch the keys from the ``KeyManagementService``. The signing to internal ``PrivateKeys`` from the ``KeyManagementService``. The
``KeyManagementService`` interface also allows other keys to be ``KeyManagementService`` interface also allows other keys to be
generated if anonymous keys are needed in a flow. Note that this generated if anonymous keys are needed in a flow. Note that this
interface works at the level of individual ``PublicKey``/``PrivateKey`` interface works at the level of individual ``PublicKey`` and internally
pairs, but the signing authority will be represented by a matched ``PrivateKey` pairs, but the signing authority may be represented by a
``CompositeKey`` on the ``NodeInfo`` to allow key clustering and ``CompositeKey`` on the ``NodeInfo`` to allow key clustering and
threshold schemes. threshold schemes.

View File

@ -168,11 +168,12 @@ Let's see what parameters we pass to the constructor of this oracle.
.. sourcecode:: kotlin .. sourcecode:: kotlin
class Oracle(val identity: Party, private val signingKey: KeyPair, val clock: Clock) = TODO() class Oracle(val identity: Party, private val signingKey: PublicKey, val clock: Clock) = TODO()
Here we see the oracle needs to have its own identity, so it can check which transaction commands it is expected to Here we see the oracle needs to have its own identity, so it can check which transaction commands it is expected to
sign for, and also needs a pair of signing keys with which it signs transactions. The clock is used for the deadline sign for, and also needs the PublicKey portion of its signing key. Later this PublicKey will be passed to the KeyManagementService
functionality which we will not discuss further here. to identify the internal PrivateKey used for transaction signing.
The clock is used for the deadline functionality which we will not discuss further here.
Assuming you have a data source and can query it, it should be very easy to implement your ``query`` method and the Assuming you have a data source and can query it, it should be very easy to implement your ``query`` method and the
parameter and ``CommandData`` classes. parameter and ``CommandData`` classes.

View File

@ -704,10 +704,10 @@ from the ledger). Finally, we add a Redeem command that should be signed by the
versions with a more complete way to express issuer constraints. versions with a more complete way to express issuer constraints.
A ``TransactionBuilder`` is not by itself ready to be used anywhere, so first, we must convert it to something that A ``TransactionBuilder`` is not by itself ready to be used anywhere, so first, we must convert it to something that
is recognised by the network. The most important next step is for the participating entities to sign it using the is recognised by the network. The most important next step is for the participating entities to sign it. Typically,
``signWith()`` method. This takes a keypair, serialises the transaction, signs the serialised form and then stores the an initiating flow will create an initial partially signed ``SignedTransaction`` by calling the ``serviceHub.signInitialTransaction`` method.
signature inside the ``TransactionBuilder``. Once all parties have signed, you can call ``TransactionBuilder.toSignedTransaction()`` Then the frozen ``SignedTransaction`` can be passed to other nodes by the flow, these can sign using ``serviceHub.createSignature`` and distribute.
to get a ``SignedTransaction`` object. The ``CollectSignaturesFlow`` provides a generic implementation of this process that can be used as a ``subFlow`` .
You can see how transactions flow through the different stages of construction by examining the commercial paper You can see how transactions flow through the different stages of construction by examining the commercial paper
unit tests. unit tests.

View File

@ -63,9 +63,8 @@ We then sign the transaction, build and record it to our transaction storage:
.. sourcecode:: kotlin .. sourcecode:: kotlin
val mySigningKey: KeyPair = serviceHub.legalIdentityKey val mySigningKey: PublicKey = serviceHub.legalIdentityKey
builder.signWith(mySigningKey) val issueTransaction = serviceHub.signInitialTransaction(issueTransaction, mySigningKey)
val issueTransaction = builder.toSignedTransaction()
serviceHub.recordTransactions(issueTransaction) serviceHub.recordTransactions(issueTransaction)
The transaction is recorded and we now have a state (asset) in possession that we can transfer to someone else. Note The transaction is recorded and we now have a state (asset) in possession that we can transfer to someone else. Note
@ -97,9 +96,9 @@ Again we sign the transaction, and build it:
.. sourcecode:: kotlin .. sourcecode:: kotlin
moveTransactionBuilder.signWith(mySigningKey) // We build it and add our default identity signature without checking if all signatures are present,
// We build it without checking if all signatures are present, because we know that the notary signature is missing // Note we know that the notary signature is missing, so thie SignedTransaction is still partial.
val moveTransaction = builder.toSignedTransaction(checkSufficientSignatures = false) val moveTransaction = serviceHub.signInitialTransaction(moveTransactionBuilder)
Next we need to obtain a signature from the notary for the transaction to be valid. Prior to signing, the notary will Next we need to obtain a signature from the notary for the transaction to be valid. Prior to signing, the notary will
commit our old (input) state so it cannot be used again. commit our old (input) state so it cannot be used again.

View File

@ -24,17 +24,15 @@ fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>,
revisions: Int? = 0, revisions: Int? = 0,
participants: List<AbstractParty> = emptyList()) : Vault<DealState> { participants: List<AbstractParty> = emptyList()) : Vault<DealState> {
val freshKey = keyManagementService.freshKey() val freshKey = keyManagementService.freshKey()
val freshPublicKey = freshKey.public val recipient = AnonymousParty(freshKey)
val recipient = AnonymousParty(freshPublicKey)
val transactions: List<SignedTransaction> = dealIds.map { val transactions: List<SignedTransaction> = dealIds.map {
// Issue a deal state // Issue a deal state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(recipient))) addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(recipient)))
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
} }
return@map dummyIssue.toSignedTransaction() return@map signInitialTransaction(dummyIssue)
} }
recordTransactions(transactions) recordTransactions(transactions)
@ -52,18 +50,16 @@ fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int,
uid: UniqueIdentifier = UniqueIdentifier(), uid: UniqueIdentifier = UniqueIdentifier(),
participants: List<AbstractParty> = emptyList()) : Vault<LinearState> { participants: List<AbstractParty> = emptyList()) : Vault<LinearState> {
val freshKey = keyManagementService.freshKey() val freshKey = keyManagementService.freshKey()
val freshPublicKey = freshKey.public val recipient = AnonymousParty(freshKey)
val recipient = AnonymousParty(freshPublicKey)
val transactions: List<SignedTransaction> = (1..numberToCreate).map { val transactions: List<SignedTransaction> = (1..numberToCreate).map {
// Issue a Linear state // Issue a Linear state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(linearId = uid, participants = participants.plus(recipient))) addOutputState(DummyLinearContract.State(linearId = uid, participants = participants.plus(recipient)))
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
} }
return@map dummyIssue.toSignedTransaction(true) return@map signInitialTransaction(dummyIssue)
} }
recordTransactions(transactions) recordTransactions(transactions)

View File

@ -44,9 +44,6 @@ class CashExitFlow(val amount: Amount<Currency>, val issueRef: OpaqueBytes, prog
} catch (e: InsufficientBalanceException) { } catch (e: InsufficientBalanceException) {
throw CashException("Exiting more cash than exists", e) throw CashException("Exiting more cash than exists", e)
} }
progressTracker.currentStep = SIGNING_TX
val myKey = serviceHub.legalIdentityKey
builder.signWith(myKey)
// Work out who the owners of the burnt states were // Work out who the owners of the burnt states were
val inputStatesNullable = serviceHub.vaultService.statesForRefs(builder.inputStates()) val inputStatesNullable = serviceHub.vaultService.statesForRefs(builder.inputStates())
@ -63,9 +60,11 @@ class CashExitFlow(val amount: Amount<Currency>, val issueRef: OpaqueBytes, prog
.map { serviceHub.identityService.partyFromAnonymous(it.owner) } .map { serviceHub.identityService.partyFromAnonymous(it.owner) }
.filterNotNull() .filterNotNull()
.toSet() .toSet()
// Sign transaction
progressTracker.currentStep = SIGNING_TX
val tx = serviceHub.signInitialTransaction(builder)
// Commit the transaction // Commit the transaction
val tx = builder.toSignedTransaction(checkSufficientSignatures = false)
progressTracker.currentStep = FINALISING_TX progressTracker.currentStep = FINALISING_TX
finaliseTx(participants, tx, "Unable to notarise exit") finaliseTx(participants, tx, "Unable to notarise exit")
return tx return tx

View File

@ -40,9 +40,7 @@ class CashIssueFlow(val amount: Amount<Currency>,
// TODO: Get a transaction key, don't just re-use the owning key // TODO: Get a transaction key, don't just re-use the owning key
Cash().generateIssue(builder, amount.issuedBy(issuer), recipient, notary) Cash().generateIssue(builder, amount.issuedBy(issuer), recipient, notary)
progressTracker.currentStep = SIGNING_TX progressTracker.currentStep = SIGNING_TX
val myKey = serviceHub.legalIdentityKey val tx = serviceHub.signInitialTransaction(builder)
builder.signWith(myKey)
val tx = builder.toSignedTransaction()
progressTracker.currentStep = FINALISING_TX progressTracker.currentStep = FINALISING_TX
subFlow(FinalityFlow(tx)) subFlow(FinalityFlow(tx))
return tx return tx

View File

@ -4,14 +4,11 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
import net.corda.core.contracts.InsufficientBalanceException import net.corda.core.contracts.InsufficientBalanceException
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.expandedCompositeKeys
import net.corda.core.crypto.toStringShort
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.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import java.security.KeyPair
import java.util.* import java.util.*
/** /**
@ -47,13 +44,9 @@ open class CashPaymentFlow(
} }
progressTracker.currentStep = SIGNING_TX progressTracker.currentStep = SIGNING_TX
keysForSigning.expandedCompositeKeys.forEach { val tx = serviceHub.signInitialTransaction(spendTX, keysForSigning)
val key = serviceHub.keyManagementService.keys[it] ?: throw IllegalStateException("Could not find signing key for ${it.toStringShort()}")
builder.signWith(KeyPair(it, key))
}
progressTracker.currentStep = FINALISING_TX progressTracker.currentStep = FINALISING_TX
val tx = spendTX.toSignedTransaction(checkSufficientSignatures = false)
finaliseTx(setOf(recipient), tx, "Unable to notarise spend") finaliseTx(setOf(recipient), tx, "Unable to notarise spend")
return tx return tx
} }

View File

@ -4,8 +4,6 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.asset.sumCashBy import net.corda.contracts.asset.sumCashBy
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.expandedCompositeKeys
import net.corda.core.crypto.sign
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
@ -19,7 +17,6 @@ import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import java.util.* import java.util.*
@ -68,7 +65,7 @@ object TwoPartyTradeFlow {
val notaryNode: NodeInfo, val notaryNode: NodeInfo,
val assetToSell: StateAndRef<OwnableState>, val assetToSell: StateAndRef<OwnableState>,
val price: Amount<Currency>, val price: Amount<Currency>,
val myKeyPair: KeyPair, val myKey: PublicKey,
override val progressTracker: ProgressTracker = Seller.tracker()) : FlowLogic<SignedTransaction>() { override val progressTracker: ProgressTracker = Seller.tracker()) : FlowLogic<SignedTransaction>() {
companion object { companion object {
@ -102,9 +99,8 @@ object TwoPartyTradeFlow {
private fun receiveAndCheckProposedTransaction(): SignedTransaction { private fun receiveAndCheckProposedTransaction(): SignedTransaction {
progressTracker.currentStep = AWAITING_PROPOSAL progressTracker.currentStep = AWAITING_PROPOSAL
val myPublicKey = myKeyPair.public
// Make the first message we'll send to kick off the flow. // Make the first message we'll send to kick off the flow.
val hello = SellerTradeInfo(assetToSell, price, myPublicKey) val hello = SellerTradeInfo(assetToSell, price, myKey)
// What we get back from the other side is a transaction that *might* be valid and acceptable to us, // What we get back from the other side is a transaction that *might* be valid and acceptable to us,
// but we must check it out thoroughly before we sign! // but we must check it out thoroughly before we sign!
val untrustedSTX = sendAndReceive<SignedTransaction>(otherParty, hello) val untrustedSTX = sendAndReceive<SignedTransaction>(otherParty, hello)
@ -112,14 +108,14 @@ object TwoPartyTradeFlow {
progressTracker.currentStep = VERIFYING progressTracker.currentStep = VERIFYING
return untrustedSTX.unwrap { return untrustedSTX.unwrap {
// Check that the tx proposed by the buyer is valid. // Check that the tx proposed by the buyer is valid.
val wtx: WireTransaction = it.verifySignatures(myPublicKey, notaryNode.notaryIdentity.owningKey) val wtx: WireTransaction = it.verifySignatures(myKey, notaryNode.notaryIdentity.owningKey)
logger.trace { "Received partially signed transaction: ${it.id}" } logger.trace { "Received partially signed transaction: ${it.id}" }
// Download and check all the things that this transaction depends on and verify it is contract-valid, // Download and check all the things that this transaction depends on and verify it is contract-valid,
// even though it is missing signatures. // even though it is missing signatures.
subFlow(ResolveTransactionsFlow(wtx, otherParty)) subFlow(ResolveTransactionsFlow(wtx, otherParty))
if (wtx.outputs.map { it.data }.sumCashBy(AnonymousParty(myPublicKey)).withoutIssuer() != price) if (wtx.outputs.map { it.data }.sumCashBy(AnonymousParty(myKey)).withoutIssuer() != price)
throw FlowException("Transaction is not sending us the right amount of cash") throw FlowException("Transaction is not sending us the right amount of cash")
it it
@ -141,7 +137,7 @@ object TwoPartyTradeFlow {
open fun calculateOurSignature(partialTX: SignedTransaction): DigitalSignature.WithKey { open fun calculateOurSignature(partialTX: SignedTransaction): DigitalSignature.WithKey {
progressTracker.currentStep = SIGNING progressTracker.currentStep = SIGNING
return myKeyPair.sign(partialTX.id) return serviceHub.createSignature(partialTX, myKey)
} }
} }
@ -207,12 +203,7 @@ object TwoPartyTradeFlow {
private fun signWithOurKeys(cashSigningPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction { private fun signWithOurKeys(cashSigningPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction {
// Now sign the transaction with whatever keys we need to move the cash. // Now sign the transaction with whatever keys we need to move the cash.
for (publicKey in cashSigningPubKeys.expandedCompositeKeys) { return serviceHub.signInitialTransaction(ptx, cashSigningPubKeys)
val privateKey = serviceHub.keyManagementService.toPrivate(publicKey)
ptx.signWith(KeyPair(publicKey, privateKey))
}
return ptx.toSignedTransaction(checkSufficientSignatures = false)
} }
@Suspendable @Suspendable
@ -229,8 +220,8 @@ object TwoPartyTradeFlow {
// we want for privacy reasons: the key is here ONLY to manage and control ownership, it is not intended to // we want for privacy reasons: the key is here ONLY to manage and control ownership, it is not intended to
// reveal who the owner actually is. The key management service is expected to derive a unique key from some // reveal who the owner actually is. The key management service is expected to derive a unique key from some
// initial seed in order to provide privacy protection. // initial seed in order to provide privacy protection.
val freshPublicKey = serviceHub.keyManagementService.freshKey().public val freshKey = serviceHub.keyManagementService.freshKey()
val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(AnonymousParty(freshPublicKey)) val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(AnonymousParty(freshKey))
tx.addOutputState(state, tradeRequest.assetForSale.state.notary) tx.addOutputState(state, tradeRequest.assetForSale.state.notary)
tx.addCommand(command, tradeRequest.assetForSale.state.data.owner.owningKey) tx.addCommand(command, tradeRequest.assetForSale.state.data.owner.owningKey)

View File

@ -23,7 +23,6 @@ import net.corda.node.utilities.transaction
import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.NodeBasedTest
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.junit.Test import org.junit.Test
import java.security.KeyPair
import java.util.* import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
@ -43,25 +42,21 @@ class BFTNotaryServiceTests : NodeBasedTest() {
val alice = startNode(ALICE.name).getOrThrow() val alice = startNode(ALICE.name).getOrThrow()
val notaryParty = alice.netMapCache.getNotary(notaryCommonName)!! val notaryParty = alice.netMapCache.getNotary(notaryCommonName)!!
val notaryNodeKeyPair = with(masterNode) { database.transaction { services.notaryIdentityKey } }
val aliceKey = with(alice) { database.transaction { services.legalIdentityKey } }
val inputState = issueState(alice, notaryParty, notaryNodeKeyPair) val inputState = issueState(alice, notaryParty)
val firstSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run { val firstTxBuilder = TransactionType.General.Builder(notaryParty).withItems(inputState)
signWith(aliceKey) val firstSpendTx = alice.services.signInitialTransaction(firstTxBuilder)
toSignedTransaction(false)
}
val firstSpend = alice.services.startFlow(NotaryFlow.Client(firstSpendTx)) val firstSpend = alice.services.startFlow(NotaryFlow.Client(firstSpendTx))
firstSpend.resultFuture.getOrThrow() firstSpend.resultFuture.getOrThrow()
val secondSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run { val secondSpendBuilder = TransactionType.General.Builder(notaryParty).withItems(inputState).run {
val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity) val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity)
addOutputState(dummyState) addOutputState(dummyState)
signWith(aliceKey) this
toSignedTransaction(false)
} }
val secondSpendTx = alice.services.signInitialTransaction(secondSpendBuilder)
val secondSpend = alice.services.startFlow(NotaryFlow.Client(secondSpendTx)) val secondSpend = alice.services.startFlow(NotaryFlow.Client(secondSpendTx))
val ex = assertFailsWith(NotaryException::class) { secondSpend.resultFuture.getOrThrow() } val ex = assertFailsWith(NotaryException::class) { secondSpend.resultFuture.getOrThrow() }
@ -69,14 +64,12 @@ class BFTNotaryServiceTests : NodeBasedTest() {
assertEquals(error.txId, secondSpendTx.id) assertEquals(error.txId, secondSpendTx.id)
} }
private fun issueState(node: AbstractNode, notary: Party, notaryKey: KeyPair): StateAndRef<*> { private fun issueState(node: AbstractNode, notary: Party): StateAndRef<*> {
return node.database.transaction { return node.database.transaction {
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0)) val builder = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0))
tx.signWith(node.services.legalIdentityKey) val stx = node.services.signInitialTransaction(builder)
tx.signWith(notaryKey)
val stx = tx.toSignedTransaction()
node.services.recordTransactions(listOf(stx)) node.services.recordTransactions(listOf(stx))
StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0)) StateAndRef(builder.outputStates().first(), StateRef(stx.id, 0))
} }
} }

View File

@ -17,7 +17,6 @@ import net.corda.node.utilities.transaction
import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.NodeBasedTest
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.junit.Test import org.junit.Test
import java.security.KeyPair
import java.util.* import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
@ -33,24 +32,21 @@ class RaftNotaryServiceTests : NodeBasedTest() {
).getOrThrow() ).getOrThrow()
val notaryParty = alice.netMapCache.getNotary(notaryName)!! val notaryParty = alice.netMapCache.getNotary(notaryName)!!
val notaryNodeKeyPair = with(masterNode) { database.transaction { services.notaryIdentityKey } }
val aliceKey = with(alice) { database.transaction { services.legalIdentityKey } }
val inputState = issueState(alice, notaryParty, notaryNodeKeyPair) val inputState = issueState(alice, notaryParty)
val firstTxBuilder = TransactionType.General.Builder(notaryParty).withItems(inputState)
val firstSpendTx = alice.services.signInitialTransaction(firstTxBuilder)
val firstSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run {
signWith(aliceKey)
toSignedTransaction(false)
}
val firstSpend = alice.services.startFlow(NotaryFlow.Client(firstSpendTx)) val firstSpend = alice.services.startFlow(NotaryFlow.Client(firstSpendTx))
firstSpend.resultFuture.getOrThrow() firstSpend.resultFuture.getOrThrow()
val secondSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run { val secondSpendBuilder = TransactionType.General.Builder(notaryParty).withItems(inputState).run {
val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity) val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity)
addOutputState(dummyState) addOutputState(dummyState)
signWith(aliceKey) this
toSignedTransaction(false)
} }
val secondSpendTx = alice.services.signInitialTransaction(secondSpendBuilder)
val secondSpend = alice.services.startFlow(NotaryFlow.Client(secondSpendTx)) val secondSpend = alice.services.startFlow(NotaryFlow.Client(secondSpendTx))
val ex = assertFailsWith(NotaryException::class) { secondSpend.resultFuture.getOrThrow() } val ex = assertFailsWith(NotaryException::class) { secondSpend.resultFuture.getOrThrow() }
@ -58,14 +54,12 @@ class RaftNotaryServiceTests : NodeBasedTest() {
assertEquals(error.txId, secondSpendTx.id) assertEquals(error.txId, secondSpendTx.id)
} }
private fun issueState(node: AbstractNode, notary: Party, notaryKey: KeyPair): StateAndRef<*> { private fun issueState(node: AbstractNode, notary: Party): StateAndRef<*> {
return node.database.transaction { return node.database.transaction {
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0)) val builder = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0))
tx.signWith(node.services.legalIdentityKey) val stx = node.services.signInitialTransaction(builder)
tx.signWith(notaryKey)
val stx = tx.toSignedTransaction()
node.services.recordTransactions(listOf(stx)) node.services.recordTransactions(listOf(stx))
StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0)) StateAndRef(builder.outputStates().first(), StateRef(stx.id, 0))
} }
} }
} }

View File

@ -70,7 +70,7 @@ class P2PSecurityTest : NodeBasedTest() {
private fun SimpleNode.registerWithNetworkMap(registrationName: X500Name): ListenableFuture<NetworkMapService.RegistrationResponse> { private fun SimpleNode.registerWithNetworkMap(registrationName: X500Name): ListenableFuture<NetworkMapService.RegistrationResponse> {
val nodeInfo = NodeInfo(net.myAddress, Party(registrationName, identity.public), MOCK_VERSION_INFO.platformVersion) val nodeInfo = NodeInfo(net.myAddress, Party(registrationName, identity.public), MOCK_VERSION_INFO.platformVersion)
val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX) val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX)
val request = RegistrationRequest(registration.toWire(identity.private), net.myAddress) val request = RegistrationRequest(registration.toWire(keyService, identity.public), net.myAddress)
return net.sendRequest<NetworkMapService.RegistrationResponse>(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.net.myAddress) return net.sendRequest<NetworkMapService.RegistrationResponse>(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.net.myAddress)
} }
} }

View File

@ -490,7 +490,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
val expires = instant + NetworkMapService.DEFAULT_EXPIRATION_PERIOD val expires = instant + NetworkMapService.DEFAULT_EXPIRATION_PERIOD
val reg = NodeRegistration(info, instant.toEpochMilli(), ADD, expires) val reg = NodeRegistration(info, instant.toEpochMilli(), ADD, expires)
val legalIdentityKey = obtainLegalIdentityKey() val legalIdentityKey = obtainLegalIdentityKey()
val request = NetworkMapService.RegistrationRequest(reg.toWire(legalIdentityKey.private), net.myAddress) val request = NetworkMapService.RegistrationRequest(reg.toWire(keyManagement, legalIdentityKey.public), net.myAddress)
return net.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapAddress) return net.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapAddress)
} }

View File

@ -1,7 +1,10 @@
package net.corda.node.services.keys package net.corda.node.services.keys
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.keys
import net.corda.core.crypto.sign
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import java.security.KeyPair import java.security.KeyPair
@ -38,13 +41,26 @@ class E2ETestKeyManagementService(initialKeys: Set<KeyPair>) : SingletonSerializ
} }
// Accessing this map clones it. // Accessing this map clones it.
override val keys: Map<PublicKey, PrivateKey> get() = mutex.locked { HashMap(keys) } override val keys: Set<PublicKey> get() = mutex.locked { keys.keys }
override fun freshKey(): KeyPair { override fun freshKey(): PublicKey {
val keyPair = generateKeyPair() val keyPair = generateKeyPair()
mutex.locked { mutex.locked {
keys[keyPair.public] = keyPair.private keys[keyPair.public] = keyPair.private
} }
return keyPair return keyPair.public
}
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
return mutex.locked {
val pk = publicKey.keys.first { keys.containsKey(it) }
KeyPair(pk, keys[pk]!!)
}
}
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
val keyPair = getSigningKeyPair(publicKey)
val signature = keyPair.sign(bytes)
return signature
} }
} }

View File

@ -1,7 +1,10 @@
package net.corda.node.services.keys package net.corda.node.services.keys
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.keys
import net.corda.core.crypto.sign
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.node.utilities.* import net.corda.node.utilities.*
@ -10,7 +13,6 @@ import org.jetbrains.exposed.sql.statements.InsertStatement
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
import java.util.*
/** /**
* A persistent re-implementation of [E2ETestKeyManagementService] to support node re-start. * A persistent re-implementation of [E2ETestKeyManagementService] to support node re-start.
@ -50,13 +52,27 @@ class PersistentKeyManagementService(initialKeys: Set<KeyPair>) : SingletonSeria
} }
} }
override val keys: Map<PublicKey, PrivateKey> get() = mutex.locked { HashMap(keys) } override val keys: Set<PublicKey> get() = mutex.locked { keys.keys }
override fun freshKey(): KeyPair { override fun freshKey(): PublicKey {
val keyPair = generateKeyPair() val keyPair = generateKeyPair()
mutex.locked { mutex.locked {
keys[keyPair.public] = keyPair.private keys[keyPair.public] = keyPair.private
} }
return keyPair return keyPair.public
} }
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
return mutex.locked {
val pk = publicKey.keys.first { keys.containsKey(it) }
KeyPair(pk, keys[pk]!!)
}
}
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
val keyPair = getSigningKeyPair(publicKey)
val signature = keyPair.sign(bytes)
return signature
}
} }

View File

@ -8,6 +8,7 @@ import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.DEFAULT_SESSION_ID import net.corda.core.node.services.DEFAULT_SESSION_ID
import net.corda.core.node.services.KeyManagementService
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.random63BitValue import net.corda.core.random63BitValue
@ -31,7 +32,7 @@ import net.corda.node.services.network.NetworkMapService.Companion.SUBSCRIPTION_
import net.corda.node.utilities.AddOrRemove import net.corda.node.utilities.AddOrRemove
import net.corda.node.utilities.AddOrRemove.ADD import net.corda.node.utilities.AddOrRemove.ADD
import net.corda.node.utilities.AddOrRemove.REMOVE import net.corda.node.utilities.AddOrRemove.REMOVE
import java.security.PrivateKey import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
import java.time.Instant import java.time.Instant
import java.time.Period import java.time.Period
@ -322,9 +323,9 @@ data class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddO
/** /**
* Build a node registration in wire format. * Build a node registration in wire format.
*/ */
fun toWire(privateKey: PrivateKey): WireNodeRegistration { fun toWire(keyManager: KeyManagementService, publicKey: PublicKey): WireNodeRegistration {
val regSerialized = this.serialize() val regSerialized = this.serialize()
val regSig = privateKey.sign(regSerialized.bytes, node.legalIdentity.owningKey) val regSig = keyManager.sign(regSerialized.bytes, publicKey)
return WireNodeRegistration(regSerialized, regSig) return WireNodeRegistration(regSerialized, regSig)
} }

View File

@ -9,7 +9,10 @@ import bftsmart.tom.server.defaultservices.DefaultReplier
import bftsmart.tom.util.Extractor import bftsmart.tom.util.Extractor
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.Timestamp import net.corda.core.contracts.Timestamp
import net.corda.core.crypto.* import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.sign
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.TimestampChecker import net.corda.core.node.services.TimestampChecker
import net.corda.core.node.services.UniquenessProvider import net.corda.core.node.services.UniquenessProvider
@ -198,8 +201,7 @@ object BFTSMaRt {
} }
protected fun sign(bytes: ByteArray): DigitalSignature.WithKey { protected fun sign(bytes: ByteArray): DigitalSignature.WithKey {
val mySigningKey = db.transaction { services.notaryIdentityKey } return db.transaction { services.keyManagementService.sign(bytes, services.notaryIdentityKey) }
return mySigningKey.sign(bytes)
} }
// TODO: // TODO:

View File

@ -243,7 +243,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
} }
override fun notifyAll(txns: Iterable<WireTransaction>) { override fun notifyAll(txns: Iterable<WireTransaction>) {
val ourKeys = services.keyManagementService.keys.keys val ourKeys = services.keyManagementService.keys
val netDelta = txns.fold(Vault.NoUpdate) { netDelta, txn -> netDelta + makeUpdate(txn, ourKeys) } val netDelta = txns.fold(Vault.NoUpdate) { netDelta, txn -> netDelta + makeUpdate(txn, ourKeys) }
if (netDelta != Vault.NoUpdate) { if (netDelta != Vault.NoUpdate) {
recordUpdate(netDelta) recordUpdate(netDelta)

View File

@ -5,21 +5,24 @@ import net.corda.contracts.CommercialPaper
import net.corda.contracts.asset.* import net.corda.contracts.asset.*
import net.corda.contracts.testing.fillWithSomeTestCash import net.corda.contracts.testing.fillWithSomeTestCash
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.AnonymousParty import net.corda.core.crypto.DigitalSignature
import net.corda.core.identity.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sign
import net.corda.core.days import net.corda.core.days
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowStateMachine import net.corda.core.flows.FlowStateMachine
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.map import net.corda.core.map
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.* import net.corda.core.node.services.*
import net.corda.core.rootCause import net.corda.core.rootCause
import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
@ -87,8 +90,6 @@ class TwoPartyTradeFlowTests {
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name) val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name) val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name)
val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name) val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name)
val aliceKey = aliceNode.services.legalIdentityKey
val notaryKey = notaryNode.services.notaryIdentityKey
aliceNode.disableDBCloseOnStop() aliceNode.disableDBCloseOnStop()
bobNode.disableDBCloseOnStop() bobNode.disableDBCloseOnStop()
@ -102,7 +103,7 @@ class TwoPartyTradeFlowTests {
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
} }
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey, notaryKey) insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
val (bobStateMachine, aliceResult) = runBuyerAndSeller(notaryNode, aliceNode, bobNode, val (bobStateMachine, aliceResult) = runBuyerAndSeller(notaryNode, aliceNode, bobNode,
"alice's paper".outputStateAndRef()) "alice's paper".outputStateAndRef())
@ -133,8 +134,6 @@ class TwoPartyTradeFlowTests {
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name) val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name) val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name)
val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name) val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name)
val aliceKey = aliceNode.services.legalIdentityKey
val notaryKey = notaryNode.services.notaryIdentityKey
aliceNode.disableDBCloseOnStop() aliceNode.disableDBCloseOnStop()
bobNode.disableDBCloseOnStop() bobNode.disableDBCloseOnStop()
@ -149,7 +148,7 @@ class TwoPartyTradeFlowTests {
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
} }
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey, notaryKey) insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
val cashLockId = UUID.randomUUID() val cashLockId = UUID.randomUUID()
bobNode.database.transaction { bobNode.database.transaction {
@ -184,8 +183,6 @@ class TwoPartyTradeFlowTests {
var bobNode = net.createPartyNode(notaryNode.info.address, BOB.name) var bobNode = net.createPartyNode(notaryNode.info.address, BOB.name)
aliceNode.disableDBCloseOnStop() aliceNode.disableDBCloseOnStop()
bobNode.disableDBCloseOnStop() bobNode.disableDBCloseOnStop()
val aliceKey = aliceNode.services.legalIdentityKey
val notaryKey = notaryNode.services.notaryIdentityKey
val bobAddr = bobNode.net.myAddress as InMemoryMessagingNetwork.PeerHandle val bobAddr = bobNode.net.myAddress as InMemoryMessagingNetwork.PeerHandle
val networkMapAddr = notaryNode.info.address val networkMapAddr = notaryNode.info.address
@ -199,7 +196,7 @@ class TwoPartyTradeFlowTests {
fillUpForSeller(false, aliceNode.info.legalIdentity, fillUpForSeller(false, aliceNode.info.legalIdentity,
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
} }
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey, notaryKey) insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
val aliceFuture = runBuyerAndSeller(notaryNode, aliceNode, bobNode, "alice's paper".outputStateAndRef()).sellerResult val aliceFuture = runBuyerAndSeller(notaryNode, aliceNode, bobNode, "alice's paper".outputStateAndRef()).sellerResult
// Everything is on this thread so we can now step through the flow one step at a time. // Everything is on this thread so we can now step through the flow one step at a time.
@ -302,8 +299,6 @@ class TwoPartyTradeFlowTests {
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name) val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = makeNodeWithTracking(notaryNode.info.address, ALICE.name) val aliceNode = makeNodeWithTracking(notaryNode.info.address, ALICE.name)
val bobNode = makeNodeWithTracking(notaryNode.info.address, BOB.name) val bobNode = makeNodeWithTracking(notaryNode.info.address, BOB.name)
val alice = aliceNode.info.legalIdentity
val aliceKey = aliceNode.services.legalIdentityKey
ledger(aliceNode.services) { ledger(aliceNode.services) {
@ -319,16 +314,15 @@ class TwoPartyTradeFlowTests {
} }
val extraKey = bobNode.keyManagement.freshKey() val extraKey = bobNode.keyManagement.freshKey()
val extraPublicKey = extraKey.public val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(extraKey),
val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(extraPublicKey),
DUMMY_CASH_ISSUER.party, DUMMY_CASH_ISSUER.party,
notaryNode.info.notaryIdentity).second notaryNode.info.notaryIdentity).second
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bobNode.services.legalIdentityKey, extraKey) val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, extraKey, DUMMY_CASH_ISSUER_KEY.public, MEGA_CORP_PUBKEY)
val alicesFakePaper = aliceNode.database.transaction { val alicesFakePaper = aliceNode.database.transaction {
fillUpForSeller(false, alice, fillUpForSeller(false, aliceNode.info.legalIdentity,
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
} }
val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey) val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
net.runNetwork() // Clear network map registration messages net.runNetwork() // Clear network map registration messages
@ -404,7 +398,6 @@ class TwoPartyTradeFlowTests {
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name) val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = makeNodeWithTracking(notaryNode.info.address, ALICE.name) val aliceNode = makeNodeWithTracking(notaryNode.info.address, ALICE.name)
val bobNode = makeNodeWithTracking(notaryNode.info.address, BOB.name) val bobNode = makeNodeWithTracking(notaryNode.info.address, BOB.name)
val aliceKey = aliceNode.services.legalIdentityKey
ledger(aliceNode.services) { ledger(aliceNode.services) {
@ -419,18 +412,18 @@ class TwoPartyTradeFlowTests {
attachment(ByteArrayInputStream(stream.toByteArray())) attachment(ByteArrayInputStream(stream.toByteArray()))
} }
val bobsKey = bobNode.keyManagement.freshKey().public val bobsKey = bobNode.keyManagement.freshKey()
val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(bobsKey), val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(bobsKey),
DUMMY_CASH_ISSUER.party, DUMMY_CASH_ISSUER.party,
notaryNode.info.notaryIdentity).second notaryNode.info.notaryIdentity).second
insertFakeTransactions(bobsFakeCash, bobNode, notaryNode) insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, DUMMY_CASH_ISSUER_KEY.public, MEGA_CORP_PUBKEY)
val alicesFakePaper = aliceNode.database.transaction { val alicesFakePaper = aliceNode.database.transaction {
fillUpForSeller(false, aliceNode.info.legalIdentity, fillUpForSeller(false, aliceNode.info.legalIdentity,
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
} }
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey) insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
net.runNetwork() // Clear network map registration messages net.runNetwork() // Clear network map registration messages
@ -524,21 +517,19 @@ class TwoPartyTradeFlowTests {
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name) val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name) val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name)
val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name) val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name)
val alice = aliceNode.info.legalIdentity
val aliceKey = aliceNode.services.legalIdentityKey
val bob = bobNode.info.legalIdentity
val bobKey = bobNode.services.legalIdentityKey
val issuer = MEGA_CORP.ref(1, 2, 3) val issuer = MEGA_CORP.ref(1, 2, 3)
val bobsBadCash = fillUpForBuyer(bobError, bob, DUMMY_CASH_ISSUER.party, val bobsBadCash = bobNode.database.transaction {
notaryNode.info.notaryIdentity).second fillUpForBuyer(bobError, bobNode.info.legalIdentity, DUMMY_CASH_ISSUER.party,
notaryNode.info.notaryIdentity).second
}
val alicesFakePaper = aliceNode.database.transaction { val alicesFakePaper = aliceNode.database.transaction {
fillUpForSeller(aliceError, alice, fillUpForSeller(aliceError, aliceNode.info.legalIdentity,
1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second
} }
insertFakeTransactions(bobsBadCash, bobNode, notaryNode, bobKey) insertFakeTransactions(bobsBadCash, bobNode, notaryNode, DUMMY_CASH_ISSUER_KEY.public, MEGA_CORP_PUBKEY)
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey) insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
net.runNetwork() // Clear network map registration messages net.runNetwork() // Clear network map registration messages
@ -563,8 +554,25 @@ class TwoPartyTradeFlowTests {
wtxToSign: List<WireTransaction>, wtxToSign: List<WireTransaction>,
node: AbstractNode, node: AbstractNode,
notaryNode: MockNetwork.MockNode, notaryNode: MockNetwork.MockNode,
vararg extraKeys: KeyPair): Map<SecureHash, SignedTransaction> { vararg extraKeys: PublicKey): Map<SecureHash, SignedTransaction> {
val signed: List<SignedTransaction> = signAll(wtxToSign, extraKeys.toList() + notaryNode.services.notaryIdentityKey + DUMMY_CASH_ISSUER_KEY)
val signed = wtxToSign.map {
val bits = it.serialize()
val id = it.id
val sigs = mutableListOf<DigitalSignature.WithKey>()
sigs.add(node.services.keyManagementService.sign(id.bytes, node.services.legalIdentityKey))
sigs.add(notaryNode.services.keyManagementService.sign(id.bytes, notaryNode.services.notaryIdentityKey))
for (extraKey in extraKeys) {
if (extraKey == DUMMY_CASH_ISSUER_KEY.public) {
sigs.add(DUMMY_CASH_ISSUER_KEY.sign(id.bytes))
} else if (extraKey == MEGA_CORP_PUBKEY) {
sigs.add(MEGA_CORP_KEY.sign(id.bytes))
} else {
sigs.add(node.services.keyManagementService.sign(id.bytes, extraKey))
}
}
SignedTransaction(bits, sigs)
}
return node.database.transaction { return node.database.transaction {
node.services.recordTransactions(signed) node.services.recordTransactions(signed)
val validatedTransactions = node.services.storageService.validatedTransactions val validatedTransactions = node.services.storageService.validatedTransactions

View File

@ -133,9 +133,7 @@ class NotaryChangeTests {
addOutputState(stateC, notary) addOutputState(stateC, notary)
addOutputState(stateB, notary, encumbrance = 1) // Encumbered by stateC addOutputState(stateB, notary, encumbrance = 1) // Encumbered by stateC
} }
val nodeKey = node.services.legalIdentityKey val stx = node.services.signInitialTransaction(tx)
tx.signWith(nodeKey)
val stx = tx.toSignedTransaction()
node.services.recordTransactions(listOf(stx)) node.services.recordTransactions(listOf(stx))
return tx.toWireTransaction() return tx.toWireTransaction()
} }
@ -151,11 +149,8 @@ class NotaryChangeTests {
fun issueState(node: AbstractNode, notaryNode: AbstractNode): StateAndRef<*> { fun issueState(node: AbstractNode, notaryNode: AbstractNode): StateAndRef<*> {
val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0)) val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0))
val nodeKey = node.services.legalIdentityKey val signedByNode = node.services.signInitialTransaction(tx)
tx.signWith(nodeKey) val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey)
val notaryKeyPair = notaryNode.services.notaryIdentityKey
tx.signWith(notaryKeyPair)
val stx = tx.toSignedTransaction()
node.services.recordTransactions(listOf(stx)) node.services.recordTransactions(listOf(stx))
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0)) return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
} }
@ -164,13 +159,9 @@ fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: A
val state = TransactionState(DummyContract.MultiOwnerState(0, val state = TransactionState(DummyContract.MultiOwnerState(0,
listOf(nodeA.info.legalIdentity, nodeB.info.legalIdentity)), notaryNode.info.notaryIdentity) listOf(nodeA.info.legalIdentity, nodeB.info.legalIdentity)), notaryNode.info.notaryIdentity)
val tx = TransactionType.NotaryChange.Builder(notaryNode.info.notaryIdentity).withItems(state) val tx = TransactionType.NotaryChange.Builder(notaryNode.info.notaryIdentity).withItems(state)
val nodeAKey = nodeA.services.legalIdentityKey val signedByA = nodeA.services.signInitialTransaction(tx)
val nodeBKey = nodeB.services.legalIdentityKey val signedByAB = nodeB.services.addSignature(signedByA)
tx.signWith(nodeAKey) val stx = notaryNode.services.addSignature(signedByAB, notaryNode.services.notaryIdentityKey)
tx.signWith(nodeBKey)
val notaryKeyPair = notaryNode.services.notaryIdentityKey
tx.signWith(notaryKeyPair)
val stx = tx.toSignedTransaction()
nodeA.services.recordTransactions(listOf(stx)) nodeA.services.recordTransactions(listOf(stx))
nodeB.services.recordTransactions(listOf(stx)) nodeB.services.recordTransactions(listOf(stx))
val stateAndRef = StateAndRef(state, StateRef(stx.id, 0)) val stateAndRef = StateAndRef(state, StateRef(stx.id, 0))
@ -180,9 +171,7 @@ fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: A
fun issueInvalidState(node: AbstractNode, notary: Party): StateAndRef<*> { fun issueInvalidState(node: AbstractNode, notary: Party): StateAndRef<*> {
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0)) val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0))
tx.setTime(Instant.now(), 30.seconds) tx.setTime(Instant.now(), 30.seconds)
val nodeKey = node.services.legalIdentityKey val stx = node.services.signInitialTransaction(tx)
tx.signWith(nodeKey)
val stx = tx.toSignedTransaction(false)
node.services.recordTransactions(listOf(stx)) node.services.recordTransactions(listOf(stx))
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0)) return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
} }

View File

@ -271,11 +271,11 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
apply { apply {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val state = TestState(FlowLogicRefFactoryImpl.createForRPC(TestFlowLogic::class.java, increment), instant) val state = TestState(FlowLogicRefFactoryImpl.createForRPC(TestFlowLogic::class.java, increment), instant)
val usefulTX = TransactionType.General.Builder(null).apply { val builder = TransactionType.General.Builder(null).apply {
addOutputState(state, DUMMY_NOTARY) addOutputState(state, DUMMY_NOTARY)
addCommand(Command(), freshKey.public) addCommand(Command(), freshKey)
signWith(freshKey) }
}.toSignedTransaction() val usefulTX = services.signInitialTransaction(builder, freshKey)
val txHash = usefulTX.id val txHash = usefulTX.id
services.recordTransactions(usefulTX) services.recordTransactions(usefulTX)

View File

@ -62,8 +62,8 @@ class ScheduledFlowTests {
val notary = serviceHub.networkMapCache.getAnyNotary() val notary = serviceHub.networkMapCache.getAnyNotary()
val builder = TransactionType.General.Builder(notary) val builder = TransactionType.General.Builder(notary)
val tx = builder.withItems(scheduledState). builder.withItems(scheduledState)
signWith(serviceHub.legalIdentityKey).toSignedTransaction(false) val tx = serviceHub.signInitialTransaction(builder)
subFlow(FinalityFlow(tx, setOf(serviceHub.myInfo.legalIdentity))) subFlow(FinalityFlow(tx, setOf(serviceHub.myInfo.legalIdentity)))
} }
} }
@ -82,8 +82,8 @@ class ScheduledFlowTests {
val notary = state.state.notary val notary = state.state.notary
val newStateOutput = scheduledState.copy(processed = true) val newStateOutput = scheduledState.copy(processed = true)
val builder = TransactionType.General.Builder(notary) val builder = TransactionType.General.Builder(notary)
val tx = builder.withItems(state, newStateOutput). builder.withItems(state, newStateOutput)
signWith(serviceHub.legalIdentityKey).toSignedTransaction(false) val tx = serviceHub.signInitialTransaction(builder)
subFlow(FinalityFlow(tx, setOf(scheduledState.source, scheduledState.destination))) subFlow(FinalityFlow(tx, setOf(scheduledState.source, scheduledState.destination)))
} }
} }

View File

@ -216,7 +216,7 @@ abstract class AbstractNetworkMapServiceTest<out S : AbstractNetworkMapService>
} }
val expires = Instant.now() + NetworkMapService.DEFAULT_EXPIRATION_PERIOD val expires = Instant.now() + NetworkMapService.DEFAULT_EXPIRATION_PERIOD
val nodeRegistration = NodeRegistration(info, distinctSerial, addOrRemove, expires) val nodeRegistration = NodeRegistration(info, distinctSerial, addOrRemove, expires)
val request = RegistrationRequest(nodeRegistration.toWire(services.legalIdentityKey.private), info.address) val request = RegistrationRequest(nodeRegistration.toWire(services.keyManagementService, services.legalIdentityKey), info.address)
val response = services.networkService.sendRequest<RegistrationResponse>(REGISTER_TOPIC, request, mapServiceNode.info.address) val response = services.networkService.sendRequest<RegistrationResponse>(REGISTER_TOPIC, request, mapServiceNode.info.address)
network.runNetwork() network.runNetwork()
return response return response

View File

@ -46,9 +46,7 @@ class DataVendingServiceTests {
Cash().generateIssue(ptx, Amount(100, Issued(deposit, USD)), beneficiary, DUMMY_NOTARY) Cash().generateIssue(ptx, Amount(100, Issued(deposit, USD)), beneficiary, DUMMY_NOTARY)
// Complete the cash transaction, and then manually relay it // Complete the cash transaction, and then manually relay it
val registerKey = registerNode.services.legalIdentityKey val tx = registerNode.services.signInitialTransaction(ptx)
ptx.signWith(registerKey)
val tx = ptx.toSignedTransaction()
vaultServiceNode.database.transaction { vaultServiceNode.database.transaction {
assertThat(vaultServiceNode.services.vaultService.unconsumedStates<Cash.State>()).isEmpty() assertThat(vaultServiceNode.services.vaultService.unconsumedStates<Cash.State>()).isEmpty()
@ -76,9 +74,7 @@ class DataVendingServiceTests {
Cash().generateIssue(ptx, Amount(100, Issued(deposit, USD)), beneficiary, DUMMY_NOTARY) Cash().generateIssue(ptx, Amount(100, Issued(deposit, USD)), beneficiary, DUMMY_NOTARY)
// The transaction tries issuing MEGA_CORP cash, but we aren't the issuer, so it's invalid // The transaction tries issuing MEGA_CORP cash, but we aren't the issuer, so it's invalid
val registerKey = registerNode.services.legalIdentityKey val tx = registerNode.services.signInitialTransaction(ptx)
ptx.signWith(registerKey)
val tx = ptx.toSignedTransaction(false)
vaultServiceNode.database.transaction { vaultServiceNode.database.transaction {
assertThat(vaultServiceNode.services.vaultService.unconsumedStates<Cash.State>()).isEmpty() assertThat(vaultServiceNode.services.vaultService.unconsumedStates<Cash.State>()).isEmpty()

View File

@ -560,8 +560,7 @@ class FlowFrameworkTests {
fun `wait for transaction`() { fun `wait for transaction`() {
val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity)
ptx.addOutputState(DummyState()) ptx.addOutputState(DummyState())
ptx.signWith(node1.services.legalIdentityKey) val stx = node1.services.signInitialTransaction(ptx)
val stx = ptx.toSignedTransaction()
val committerFiber = node1 val committerFiber = node1
.initiateSingleShotFlow(WaitingFlows.Waiter::class) { WaitingFlows.Committer(it) } .initiateSingleShotFlow(WaitingFlows.Waiter::class) { WaitingFlows.Committer(it) }
@ -575,8 +574,7 @@ class FlowFrameworkTests {
fun `committer throws exception before calling the finality flow`() { fun `committer throws exception before calling the finality flow`() {
val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity)
ptx.addOutputState(DummyState()) ptx.addOutputState(DummyState())
ptx.signWith(node1.services.legalIdentityKey) val stx = node1.services.signInitialTransaction(ptx)
val stx = ptx.toSignedTransaction()
node1.registerServiceFlow(WaitingFlows.Waiter::class) { node1.registerServiceFlow(WaitingFlows.Waiter::class) {
WaitingFlows.Committer(it) { throw Exception("Error") } WaitingFlows.Committer(it) { throw Exception("Error") }

View File

@ -6,7 +6,6 @@ import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.keys
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.seconds import net.corda.core.seconds
@ -21,7 +20,6 @@ import net.corda.testing.node.MockNetwork
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.KeyPair
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -31,7 +29,6 @@ class NotaryServiceTests {
lateinit var net: MockNetwork lateinit var net: MockNetwork
lateinit var notaryNode: MockNetwork.MockNode lateinit var notaryNode: MockNetwork.MockNode
lateinit var clientNode: MockNetwork.MockNode lateinit var clientNode: MockNetwork.MockNode
lateinit var clientKeyPair: KeyPair
@Before fun setup() { @Before fun setup() {
net = MockNetwork() net = MockNetwork()
@ -39,7 +36,6 @@ class NotaryServiceTests {
legalName = DUMMY_NOTARY.name, legalName = DUMMY_NOTARY.name,
advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), ServiceInfo(SimpleNotaryService.type))) advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), ServiceInfo(SimpleNotaryService.type)))
clientNode = net.createNode(networkMapAddress = notaryNode.info.address) clientNode = net.createNode(networkMapAddress = notaryNode.info.address)
clientKeyPair = clientNode.keyManagement.toKeyPair(clientNode.info.legalIdentity.owningKey.keys.single())
net.runNetwork() // Clear network map registration messages net.runNetwork() // Clear network map registration messages
} }
@ -48,8 +44,7 @@ class NotaryServiceTests {
val inputState = issueState(clientNode) val inputState = issueState(clientNode)
val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState) val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState)
tx.setTime(Instant.now(), 30.seconds) tx.setTime(Instant.now(), 30.seconds)
tx.signWith(clientKeyPair) clientNode.services.signInitialTransaction(tx)
tx.toSignedTransaction(false)
} }
val future = runNotaryClient(stx) val future = runNotaryClient(stx)
@ -61,8 +56,7 @@ class NotaryServiceTests {
val stx = run { val stx = run {
val inputState = issueState(clientNode) val inputState = issueState(clientNode)
val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState) val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState)
tx.signWith(clientKeyPair) clientNode.services.signInitialTransaction(tx)
tx.toSignedTransaction(false)
} }
val future = runNotaryClient(stx) val future = runNotaryClient(stx)
@ -75,8 +69,7 @@ class NotaryServiceTests {
val inputState = issueState(clientNode) val inputState = issueState(clientNode)
val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState) val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState)
tx.setTime(Instant.now().plusSeconds(3600), 30.seconds) tx.setTime(Instant.now().plusSeconds(3600), 30.seconds)
tx.signWith(clientKeyPair) clientNode.services.signInitialTransaction(tx)
tx.toSignedTransaction(false)
} }
val future = runNotaryClient(stx) val future = runNotaryClient(stx)
@ -89,8 +82,7 @@ class NotaryServiceTests {
val stx = run { val stx = run {
val inputState = issueState(clientNode) val inputState = issueState(clientNode)
val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState) val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState)
tx.signWith(clientKeyPair) clientNode.services.signInitialTransaction(tx)
tx.toSignedTransaction(false)
} }
val firstAttempt = NotaryFlow.Client(stx) val firstAttempt = NotaryFlow.Client(stx)
@ -107,14 +99,12 @@ class NotaryServiceTests {
val inputState = issueState(clientNode) val inputState = issueState(clientNode)
val stx = run { val stx = run {
val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState) val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState)
tx.signWith(clientKeyPair) clientNode.services.signInitialTransaction(tx)
tx.toSignedTransaction(false)
} }
val stx2 = run { val stx2 = run {
val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState) val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState)
tx.addInputState(issueState(clientNode)) tx.addInputState(issueState(clientNode))
tx.signWith(clientKeyPair) clientNode.services.signInitialTransaction(tx)
tx.toSignedTransaction(false)
} }
val firstSpend = NotaryFlow.Client(stx) val firstSpend = NotaryFlow.Client(stx)
@ -139,11 +129,8 @@ class NotaryServiceTests {
fun issueState(node: AbstractNode): StateAndRef<*> { fun issueState(node: AbstractNode): StateAndRef<*> {
val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0)) val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0))
val nodeKey = node.services.legalIdentityKey val signedByNode = node.services.signInitialTransaction(tx)
tx.signWith(nodeKey) val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey)
val notaryKeyPair = notaryNode.services.notaryIdentityKey
tx.signWith(notaryKeyPair)
val stx = tx.toSignedTransaction()
node.services.recordTransactions(listOf(stx)) node.services.recordTransactions(listOf(stx))
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0)) return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
} }

View File

@ -3,7 +3,6 @@ package net.corda.node.services.transactions
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.keys
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -42,9 +41,7 @@ class ValidatingNotaryServiceTests {
val stx = run { val stx = run {
val inputState = issueInvalidState(clientNode, notaryNode.info.notaryIdentity) val inputState = issueInvalidState(clientNode, notaryNode.info.notaryIdentity)
val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState) val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState)
val keyPair = clientNode.services.keyManagementService.toKeyPair(clientNode.info.legalIdentity.owningKey.keys.single()) clientNode.services.signInitialTransaction(tx)
tx.signWith(keyPair)
tx.toSignedTransaction(false)
} }
val future = runClient(stx) val future = runClient(stx)
@ -60,9 +57,7 @@ class ValidatingNotaryServiceTests {
val command = Command(DummyContract.Commands.Move(), expectedMissingKey) val command = Command(DummyContract.Commands.Move(), expectedMissingKey)
val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState, command) val tx = TransactionType.General.Builder(notaryNode.info.notaryIdentity).withItems(inputState, command)
val keyPair = clientNode.services.keyManagementService.toKeyPair(clientNode.info.legalIdentity.owningKey.keys.single()) clientNode.services.signInitialTransaction(tx)
tx.signWith(keyPair)
tx.toSignedTransaction(false)
} }
val ex = assertFailsWith(NotaryException::class) { val ex = assertFailsWith(NotaryException::class) {
@ -85,11 +80,8 @@ class ValidatingNotaryServiceTests {
fun issueState(node: AbstractNode): StateAndRef<*> { fun issueState(node: AbstractNode): StateAndRef<*> {
val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0)) val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0))
val nodeKey = node.services.legalIdentityKey val signedByNode = node.services.signInitialTransaction(tx)
tx.signWith(nodeKey) val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey)
val notaryKeyPair = notaryNode.services.notaryIdentityKey
tx.signWith(notaryKeyPair)
val stx = tx.toSignedTransaction()
node.services.recordTransactions(listOf(stx)) node.services.recordTransactions(listOf(stx))
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0)) return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
} }

View File

@ -402,7 +402,7 @@ class NodeVaultServiceTest {
fun addNoteToTransaction() { fun addNoteToTransaction() {
database.transaction { database.transaction {
val freshKey = services.legalIdentityKey.public val freshKey = services.legalIdentityKey
// Issue a txn to Send us some Money // Issue a txn to Send us some Money
val usefulTX = TransactionType.General.Builder(null).apply { val usefulTX = TransactionType.General.Builder(null).apply {

View File

@ -91,9 +91,8 @@ class VaultWithCashTest {
database.transaction { database.transaction {
// A tx that sends us money. // A tx that sends us money.
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val usefulTX = TransactionType.General.Builder(null).apply { val usefulTX = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshPublicKey), DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshKey), DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -101,11 +100,11 @@ class VaultWithCashTest {
services.recordTransactions(usefulTX) services.recordTransactions(usefulTX)
// A tx that spends our money. // A tx that spends our money.
val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply { val spendTXBuilder = TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 80.DOLLARS, BOB) vault.generateSpend(this, 80.DOLLARS, BOB)
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }
val spendTX = services.signInitialTransaction(spendTXBuilder, freshKey)
assertEquals(100.DOLLARS, vault.cashBalances[USD]) assertEquals(100.DOLLARS, vault.cashBalances[USD])
@ -129,14 +128,13 @@ class VaultWithCashTest {
@Test @Test
fun `issue and attempt double spend`() { fun `issue and attempt double spend`() {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
database.transaction { database.transaction {
// A tx that sends us money. // A tx that sends us money.
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 10, 10, Random(0L), services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 10, 10, Random(0L),
issuedBy = MEGA_CORP.ref(1), issuedBy = MEGA_CORP.ref(1),
issuerKey = MEGA_CORP_KEY, issuerKey = MEGA_CORP_KEY,
ownedBy = AnonymousParty(freshPublicKey)) ownedBy = AnonymousParty(freshKey))
println("Cash balance: ${vault.cashBalances[USD]}") println("Cash balance: ${vault.cashBalances[USD]}")
assertThat(vault.unconsumedStates<Cash.State>()).hasSize(10) assertThat(vault.unconsumedStates<Cash.State>()).hasSize(10)
@ -149,12 +147,12 @@ class VaultWithCashTest {
backgroundExecutor.submit { backgroundExecutor.submit {
database.transaction { database.transaction {
try { try {
val txn1 = val txn1Builder =
TransactionType.General.Builder(DUMMY_NOTARY).apply { TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 60.DOLLARS, BOB) vault.generateSpend(this, 60.DOLLARS, BOB)
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }
val txn1 = services.signInitialTransaction(txn1Builder, freshKey)
println("txn1: ${txn1.id} spent ${((txn1.tx.outputs[0].data) as Cash.State).amount}") println("txn1: ${txn1.id} spent ${((txn1.tx.outputs[0].data) as Cash.State).amount}")
println("""txn1 states: println("""txn1 states:
UNCONSUMED: ${vault.unconsumedStates<Cash.State>().count()} : ${vault.unconsumedStates<Cash.State>()}, UNCONSUMED: ${vault.unconsumedStates<Cash.State>().count()} : ${vault.unconsumedStates<Cash.State>()},
@ -181,12 +179,12 @@ class VaultWithCashTest {
backgroundExecutor.submit { backgroundExecutor.submit {
database.transaction { database.transaction {
try { try {
val txn2 = val txn2Builder =
TransactionType.General.Builder(DUMMY_NOTARY).apply { TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 80.DOLLARS, BOB) vault.generateSpend(this, 80.DOLLARS, BOB)
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }
val txn2 = services.signInitialTransaction(txn2Builder, freshKey)
println("txn2: ${txn2.id} spent ${((txn2.tx.outputs[0].data) as Cash.State).amount}") println("txn2: ${txn2.id} spent ${((txn2.tx.outputs[0].data) as Cash.State).amount}")
println("""txn2 states: println("""txn2 states:
UNCONSUMED: ${vault.unconsumedStates<Cash.State>().count()} : ${vault.unconsumedStates<Cash.State>()}, UNCONSUMED: ${vault.unconsumedStates<Cash.State>().count()} : ${vault.unconsumedStates<Cash.State>()},
@ -221,17 +219,16 @@ class VaultWithCashTest {
fun `branching LinearStates fails to verify`() { fun `branching LinearStates fails to verify`() {
database.transaction { database.transaction {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public val freshIdentity = AnonymousParty(freshKey)
val freshIdentity = AnonymousParty(freshPublicKey)
val linearId = UniqueIdentifier() val linearId = UniqueIdentifier()
// Issue a linear state // Issue a linear state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssueBuilder = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }
val dummyIssue = services.signInitialTransaction(dummyIssueBuilder)
assertThatThrownBy { assertThatThrownBy {
dummyIssue.toLedgerTransaction(services).verify() dummyIssue.toLedgerTransaction(services).verify()
@ -243,17 +240,16 @@ class VaultWithCashTest {
fun `sequencing LinearStates works`() { fun `sequencing LinearStates works`() {
database.transaction { database.transaction {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public val freshIdentity = AnonymousParty(freshKey)
val freshIdentity = AnonymousParty(freshPublicKey)
val linearId = UniqueIdentifier() val linearId = UniqueIdentifier()
// Issue a linear state // Issue a linear state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssueBuilder = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }
val dummyIssue = services.signInitialTransaction(dummyIssueBuilder, services.legalIdentityKey)
dummyIssue.toLedgerTransaction(services).verify() dummyIssue.toLedgerTransaction(services).verify()
@ -278,9 +274,8 @@ class VaultWithCashTest {
fun `spending cash in vault of mixed state types works`() { fun `spending cash in vault of mixed state types works`() {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
database.transaction { database.transaction {
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L), ownedBy = AnonymousParty(freshPublicKey)) services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L), ownedBy = AnonymousParty(freshKey))
services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 2, 2, Random(0L)) services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 2, 2, Random(0L))
services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L)) services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L))
val cash = vault.unconsumedStates<Cash.State>() val cash = vault.unconsumedStates<Cash.State>()
@ -293,11 +288,11 @@ class VaultWithCashTest {
database.transaction { database.transaction {
// A tx that spends our money. // A tx that spends our money.
val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply { val spendTXBuilder = TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 80.DOLLARS, BOB) vault.generateSpend(this, 80.DOLLARS, BOB)
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }
val spendTX = services.signInitialTransaction(spendTXBuilder, freshKey)
services.recordTransactions(spendTX) services.recordTransactions(spendTX)
val consumedStates = vault.consumedStates<ContractState>() val consumedStates = vault.consumedStates<ContractState>()
@ -312,8 +307,7 @@ class VaultWithCashTest {
fun `consuming multiple contract state types in same transaction`() { fun `consuming multiple contract state types in same transaction`() {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public val freshIdentity = AnonymousParty(freshKey)
val freshIdentity = AnonymousParty(freshPublicKey)
database.transaction { database.transaction {
services.fillWithSomeTestDeals(listOf("123", "456", "789")) services.fillWithSomeTestDeals(listOf("123", "456", "789"))

View File

@ -15,6 +15,7 @@ import net.corda.core.math.Interpolator
import net.corda.core.math.InterpolatorFactory import net.corda.core.math.InterpolatorFactory
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.transactions.FilteredTransaction import net.corda.core.transactions.FilteredTransaction
@ -32,6 +33,8 @@ import java.io.InputStream
import java.math.BigDecimal import java.math.BigDecimal
import java.security.KeyPair import java.security.KeyPair
import java.time.Clock import java.time.Clock
import java.security.PublicKey
import java.time.Duration
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
@ -68,8 +71,8 @@ object NodeInterestRates {
val oracle: Oracle by lazy { val oracle: Oracle by lazy {
val myNodeInfo = services.myInfo val myNodeInfo = services.myInfo
val myIdentity = myNodeInfo.serviceIdentities(type).first() val myIdentity = myNodeInfo.serviceIdentities(type).first()
val mySigningKey = services.keyManagementService.toKeyPair(myIdentity.owningKey.keys) val mySigningKey = myIdentity.owningKey.keys.first { services.keyManagementService.keys.contains(it) }
Oracle(myIdentity, mySigningKey, services.clock) Oracle(myIdentity, mySigningKey, services)
} }
init { init {
@ -129,7 +132,7 @@ object NodeInterestRates {
* The oracle will try to interpolate the missing value of a tenor for the given fix name and date. * The oracle will try to interpolate the missing value of a tenor for the given fix name and date.
*/ */
@ThreadSafe @ThreadSafe
class Oracle(val identity: Party, private val signingKey: KeyPair, val clock: Clock) { class Oracle(val identity: Party, private val signingKey: PublicKey, val services: ServiceHub) {
private object Table : JDBCHashedTable("demo_interest_rate_fixes") { private object Table : JDBCHashedTable("demo_interest_rate_fixes") {
val name = varchar("index_name", length = 255) val name = varchar("index_name", length = 255)
val forDay = localDate("for_day") val forDay = localDate("for_day")
@ -168,7 +171,7 @@ object NodeInterestRates {
// Make this the last bit of initialisation logic so fully constructed when entered into instances map // Make this the last bit of initialisation logic so fully constructed when entered into instances map
init { init {
require(signingKey.public in identity.owningKey.keys) require(signingKey in identity.owningKey.keys)
} }
/** /**
@ -179,7 +182,7 @@ object NodeInterestRates {
@Suspendable @Suspendable
fun query(queries: List<FixOf>, deadline: Instant): List<Fix> { fun query(queries: List<FixOf>, deadline: Instant): List<Fix> {
require(queries.isNotEmpty()) require(queries.isNotEmpty())
return mutex.readWithDeadline(clock, deadline) { return mutex.readWithDeadline(services.clock, deadline) {
val answers: List<Fix?> = queries.map { container[it] } val answers: List<Fix?> = queries.map { container[it] }
val firstNull = answers.indexOf(null) val firstNull = answers.indexOf(null)
if (firstNull != -1) { if (firstNull != -1) {
@ -225,7 +228,8 @@ object NodeInterestRates {
// Note that we will happily sign an invalid transaction, as we are only being presented with a filtered // Note that we will happily sign an invalid transaction, as we are only being presented with a filtered
// version so we can't resolve or check it ourselves. However, that doesn't matter much, as if we sign // version so we can't resolve or check it ourselves. However, that doesn't matter much, as if we sign
// an invalid transaction the signature is worthless. // an invalid transaction the signature is worthless.
return signingKey.sign(ftx.rootHash.bytes, identity) val signature = services.keyManagementService.sign(ftx.rootHash.bytes, signingKey)
return DigitalSignature.LegallyIdentifiable(identity, signature.bytes)
} }
// DOCEND 1 // DOCEND 1
} }

View File

@ -2,10 +2,10 @@ package net.corda.irs.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.DealState import net.corda.core.contracts.DealState
import net.corda.core.identity.AbstractParty
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.AbstractParty
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken

View File

@ -3,7 +3,6 @@ package net.corda.irs.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.TransientProperty import net.corda.core.TransientProperty
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.keys
import net.corda.core.crypto.toBase58String import net.corda.core.crypto.toBase58String
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
@ -20,7 +19,6 @@ import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import net.corda.flows.TwoPartyDealFlow import net.corda.flows.TwoPartyDealFlow
import java.math.BigDecimal import java.math.BigDecimal
import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
object FixingFlow { object FixingFlow {
@ -115,10 +113,9 @@ object FixingFlow {
StateAndRef(state, payload.ref) StateAndRef(state, payload.ref)
} }
override val myKeyPair: KeyPair get() { override val myKey: PublicKey get() {
val myPublicKey = serviceHub.myInfo.legalIdentity.owningKey dealToFix.state.data.parties.single { it.owningKey == serviceHub.myInfo.legalIdentity.owningKey }
val myKeys = dealToFix.state.data.parties.single { it.owningKey == myPublicKey }.owningKey.keys return serviceHub.legalIdentityKey
return serviceHub.keyManagementService.toKeyPair(myKeys)
} }
override val notaryNode: NodeInfo get() { override val notaryNode: NodeInfo get() {

View File

@ -13,6 +13,7 @@ import net.corda.core.flatMap
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowStateMachine import net.corda.core.flows.FlowStateMachine
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.map import net.corda.core.map
import net.corda.core.node.services.linearHeadsOfType import net.corda.core.node.services.linearHeadsOfType
@ -27,7 +28,7 @@ import net.corda.node.utilities.transaction
import net.corda.testing.initiateSingleShotFlow import net.corda.testing.initiateSingleShotFlow
import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockIdentityService import net.corda.testing.node.MockIdentityService
import java.security.KeyPair import java.security.PublicKey
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
@ -127,9 +128,9 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
@InitiatingFlow @InitiatingFlow
class StartDealFlow(val otherParty: Party, class StartDealFlow(val otherParty: Party,
val payload: AutoOffer, val payload: AutoOffer,
val myKeyPair: KeyPair) : FlowLogic<SignedTransaction>() { val myKey: PublicKey) : FlowLogic<SignedTransaction>() {
@Suspendable @Suspendable
override fun call(): SignedTransaction = subFlow(Instigator(otherParty, payload, myKeyPair)) override fun call(): SignedTransaction = subFlow(Instigator(otherParty, payload, myKey))
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")

View File

@ -7,10 +7,9 @@ import net.corda.contracts.asset.`owned by`
import net.corda.core.bd import net.corda.core.bd
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.MerkleTreeException import net.corda.core.crypto.MerkleTreeException
import net.corda.core.identity.Party
import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.identity.Party
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ALICE import net.corda.core.utilities.ALICE
@ -25,6 +24,7 @@ import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
@ -34,7 +34,6 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import java.io.Closeable import java.io.Closeable
import java.math.BigDecimal import java.math.BigDecimal
import java.time.Clock
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertFalse import kotlin.test.assertFalse
@ -52,7 +51,8 @@ class NodeInterestRatesTest {
val DUMMY_CASH_ISSUER_KEY = generateKeyPair() val DUMMY_CASH_ISSUER_KEY = generateKeyPair()
val DUMMY_CASH_ISSUER = Party(X500Name("CN=Cash issuer,O=R3,OU=corda,L=London,C=UK"), DUMMY_CASH_ISSUER_KEY.public) val DUMMY_CASH_ISSUER = Party(X500Name("CN=Cash issuer,O=R3,OU=corda,L=London,C=UK"), DUMMY_CASH_ISSUER_KEY.public)
val clock = Clock.systemUTC() val dummyServices = MockServices(DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY)
val clock get() = dummyServices.clock
lateinit var oracle: NodeInterestRates.Oracle lateinit var oracle: NodeInterestRates.Oracle
lateinit var dataSource: Closeable lateinit var dataSource: Closeable
lateinit var database: Database lateinit var database: Database
@ -72,7 +72,7 @@ class NodeInterestRatesTest {
dataSource = dataSourceAndDatabase.first dataSource = dataSourceAndDatabase.first
database = dataSourceAndDatabase.second database = dataSourceAndDatabase.second
database.transaction { database.transaction {
oracle = NodeInterestRates.Oracle(MEGA_CORP, MEGA_CORP_KEY, clock).apply { knownFixes = TEST_DATA } oracle = NodeInterestRates.Oracle(MEGA_CORP, MEGA_CORP_KEY.public, dummyServices).apply { knownFixes = TEST_DATA }
} }
} }

View File

@ -13,18 +13,16 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode:
@Suspendable @Suspendable
override fun call(): SignedTransaction { override fun call(): SignedTransaction {
val random = Random() val random = Random()
val myKeyPair = serviceHub.legalIdentityKey
// Self issue an asset // Self issue an asset
val issueTx = DummyContract.generateInitial(random.nextInt(), notary, serviceHub.myInfo.legalIdentity.ref(0)).apply { val issueTxBuilder = DummyContract.generateInitial(random.nextInt(), notary, serviceHub.myInfo.legalIdentity.ref(0))
signWith(myKeyPair) val issueTx = serviceHub.signInitialTransaction(issueTxBuilder)
} serviceHub.recordTransactions(issueTx)
serviceHub.recordTransactions(issueTx.toSignedTransaction())
// Move ownership of the asset to the counterparty // Move ownership of the asset to the counterparty
val asset = issueTx.toWireTransaction().outRef<DummyContract.SingleOwnerState>(0) val counterPartyKey = counterpartyNode.owningKey
val moveTx = DummyContract.move(asset, counterpartyNode).apply { val asset = issueTx.tx.outRef<DummyContract.SingleOwnerState>(0)
signWith(myKeyPair) val moveTxBuilder = DummyContract.move(asset, counterpartyNode)
} val moveTx = serviceHub.signInitialTransaction(moveTxBuilder)
// We don't check signatures because we know that the notary's signature is missing // We don't check signatures because we know that the notary's signature is missing
return moveTx.toSignedTransaction(checkSufficientSignatures = false) return moveTx
} }
} }

View File

@ -21,9 +21,8 @@ object StateRevisionFlow {
val state = originalState.state.data val state = originalState.state.data
val tx = state.generateRevision(originalState.state.notary, originalState, modification) val tx = state.generateRevision(originalState.state.notary, originalState, modification)
tx.setTime(serviceHub.clock.instant(), 30.seconds) tx.setTime(serviceHub.clock.instant(), 30.seconds)
tx.signWith(serviceHub.legalIdentityKey)
val stx = tx.toSignedTransaction(false) val stx = serviceHub.signInitialTransaction(tx)
return Pair(stx, state.participants) return Pair(stx, state.participants)
} }
} }

View File

@ -4,13 +4,13 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.CommercialPaper import net.corda.contracts.CommercialPaper
import net.corda.contracts.asset.DUMMY_CASH_ISSUER import net.corda.contracts.asset.DUMMY_CASH_ISSUER
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.days import net.corda.core.days
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.seconds import net.corda.core.seconds

View File

@ -47,7 +47,11 @@ import javax.annotation.concurrent.ThreadSafe
* A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for * A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for
* building chains of transactions and verifying them. It isn't sufficient for testing flows however. * building chains of transactions and verifying them. It isn't sufficient for testing flows however.
*/ */
open class MockServices(val key: KeyPair = generateKeyPair()) : ServiceHub { open class MockServices(vararg val keys: KeyPair) : ServiceHub {
constructor() : this(generateKeyPair())
val key: KeyPair get() = keys.first()
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
txs.forEach { txs.forEach {
storageService.stateMachineRecordedTransactionMapping.addMapping(StateMachineRunId.createRandom(), it.id) storageService.stateMachineRecordedTransactionMapping.addMapping(StateMachineRunId.createRandom(), it.id)
@ -59,7 +63,7 @@ open class MockServices(val key: KeyPair = generateKeyPair()) : ServiceHub {
override val storageService: TxWritableStorageService = MockStorageService() override val storageService: TxWritableStorageService = MockStorageService()
override val identityService: MockIdentityService = MockIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY)) override val identityService: MockIdentityService = MockIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY))
override val keyManagementService: MockKeyManagementService = MockKeyManagementService(key) override val keyManagementService: KeyManagementService = MockKeyManagementService(*keys)
override val vaultService: VaultService get() = throw UnsupportedOperationException() override val vaultService: VaultService get() = throw UnsupportedOperationException()
override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException() override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException()
@ -106,14 +110,27 @@ class MockIdentityService(val identities: List<Party>,
class MockKeyManagementService(vararg initialKeys: KeyPair) : SingletonSerializeAsToken(), KeyManagementService { class MockKeyManagementService(vararg initialKeys: KeyPair) : SingletonSerializeAsToken(), KeyManagementService {
override val keys: MutableMap<PublicKey, PrivateKey> = initialKeys.associateByTo(HashMap(), { it.public }, { it.private }) private val keyStore: MutableMap<PublicKey, PrivateKey> = initialKeys.associateByTo(HashMap(), { it.public }, { it.private })
override val keys: Set<PublicKey> get() = keyStore.keys
val nextKeys = LinkedList<KeyPair>() val nextKeys = LinkedList<KeyPair>()
override fun freshKey(): KeyPair { override fun freshKey(): PublicKey {
val k = nextKeys.poll() ?: generateKeyPair() val k = nextKeys.poll() ?: generateKeyPair()
keys[k.public] = k.private keyStore[k.public] = k.private
return k return k.public
}
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
val pk = publicKey.keys.first { keyStore.containsKey(it) }
return KeyPair(pk, keyStore[pk]!!)
}
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
val keyPair = getSigningKeyPair(publicKey)
val signature = keyPair.sign(bytes)
return signature
} }
} }

View File

@ -6,18 +6,19 @@ import com.google.common.util.concurrent.SettableFuture
import net.corda.core.crypto.commonName import net.corda.core.crypto.commonName
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.testing.MOCK_VERSION_INFO import net.corda.core.node.services.KeyManagementService
import net.corda.node.services.RPCUserServiceImpl import net.corda.node.services.RPCUserServiceImpl
import net.corda.node.services.api.MonitoringService import net.corda.node.services.api.MonitoringService
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.keys.E2ETestKeyManagementService
import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.node.services.messaging.ArtemisMessagingServer
import net.corda.node.services.messaging.NodeMessagingClient import net.corda.node.services.messaging.NodeMessagingClient
import net.corda.node.services.network.InMemoryNetworkMapCache import net.corda.node.services.network.InMemoryNetworkMapCache
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.MOCK_VERSION_INFO
import net.corda.testing.freeLocalHostAndPort import net.corda.testing.freeLocalHostAndPort
import org.bouncycastle.asn1.x500.X500Name
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import java.io.Closeable import java.io.Closeable
import java.security.KeyPair import java.security.KeyPair
@ -34,6 +35,7 @@ class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeL
val userService = RPCUserServiceImpl(config.rpcUsers) val userService = RPCUserServiceImpl(config.rpcUsers)
val monitoringService = MonitoringService(MetricRegistry()) val monitoringService = MonitoringService(MetricRegistry())
val identity: KeyPair = generateKeyPair() val identity: KeyPair = generateKeyPair()
val keyService: KeyManagementService = E2ETestKeyManagementService(setOf(identity))
val executor = ServiceAffinityExecutor(config.myLegalName.commonName, 1) val executor = ServiceAffinityExecutor(config.myLegalName.commonName, 1)
val broker = ArtemisMessagingServer(config, address, rpcAddress, InMemoryNetworkMapCache(), userService) val broker = ArtemisMessagingServer(config, address, rpcAddress, InMemoryNetworkMapCache(), userService)
val networkMapRegistrationFuture: SettableFuture<Unit> = SettableFuture.create<Unit>() val networkMapRegistrationFuture: SettableFuture<Unit> = SettableFuture.create<Unit>()