mirror of
https://github.com/corda/corda.git
synced 2025-06-16 14:18:20 +00:00
Remove notaryIdentityKey from ServiceHub (#1541)
* Remove notaryIdentityKey from ServiceHub It was redundant, as we have notary field on a transaction. Notaries can use this field to check if the transaction was meant for them and then use that information while choosing a key to sign a transaction. * Move notaryIdentityKey to NotaryService * Address comments * Fixes after rebase
This commit is contained in:
committed by
josecoll
parent
adb8c5ead2
commit
002c6c4687
@ -13,6 +13,7 @@ import net.corda.core.node.services.NotaryService
|
|||||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||||
import net.corda.core.node.services.UniquenessProvider
|
import net.corda.core.node.services.UniquenessProvider
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
|
import net.corda.core.transactions.FilteredTransaction
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.UntrustworthyData
|
import net.corda.core.utilities.UntrustworthyData
|
||||||
@ -71,7 +72,7 @@ class NotaryFlow {
|
|||||||
val tx: Any = if (stx.isNotaryChangeTransaction()) {
|
val tx: Any = if (stx.isNotaryChangeTransaction()) {
|
||||||
stx.notaryChangeTx
|
stx.notaryChangeTx
|
||||||
} else {
|
} else {
|
||||||
stx.buildFilteredTransaction(Predicate { it is StateRef || it is TimeWindow })
|
stx.buildFilteredTransaction(Predicate { it is StateRef || it is TimeWindow || it == notaryParty })
|
||||||
}
|
}
|
||||||
sendAndReceiveWithRetry(notaryParty, tx)
|
sendAndReceiveWithRetry(notaryParty, tx)
|
||||||
}
|
}
|
||||||
@ -118,7 +119,8 @@ class NotaryFlow {
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): Void? {
|
override fun call(): Void? {
|
||||||
val (id, inputs, timeWindow) = receiveAndVerifyTx()
|
val (id, inputs, timeWindow, notary) = receiveAndVerifyTx()
|
||||||
|
checkNotary(notary)
|
||||||
service.validateTimeWindow(timeWindow)
|
service.validateTimeWindow(timeWindow)
|
||||||
service.commitInputStates(inputs, id, otherSide)
|
service.commitInputStates(inputs, id, otherSide)
|
||||||
signAndSendResponse(id)
|
signAndSendResponse(id)
|
||||||
@ -132,6 +134,13 @@ class NotaryFlow {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
abstract fun receiveAndVerifyTx(): TransactionParts
|
abstract fun receiveAndVerifyTx(): TransactionParts
|
||||||
|
|
||||||
|
// Check if transaction is intended to be signed by this notary.
|
||||||
|
@Suspendable
|
||||||
|
protected fun checkNotary(notary: Party?) {
|
||||||
|
if (notary !in serviceHub.myInfo.legalIdentities)
|
||||||
|
throw NotaryException(NotaryError.WrongNotary)
|
||||||
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun signAndSendResponse(txId: SecureHash) {
|
private fun signAndSendResponse(txId: SecureHash) {
|
||||||
val signature = service.sign(txId)
|
val signature = service.sign(txId)
|
||||||
@ -144,7 +153,7 @@ class NotaryFlow {
|
|||||||
* The minimum amount of information needed to notarise a transaction. Note that this does not include
|
* The minimum amount of information needed to notarise a transaction. Note that this does not include
|
||||||
* any sensitive transaction details.
|
* any sensitive transaction details.
|
||||||
*/
|
*/
|
||||||
data class TransactionParts(val id: SecureHash, val inputs: List<StateRef>, val timestamp: TimeWindow?)
|
data class TransactionParts(val id: SecureHash, val inputs: List<StateRef>, val timestamp: TimeWindow?, val notary: Party?)
|
||||||
|
|
||||||
class NotaryException(val error: NotaryError) : FlowException("Error response from Notary - $error")
|
class NotaryException(val error: NotaryError) : FlowException("Error response from Notary - $error")
|
||||||
|
|
||||||
@ -160,4 +169,6 @@ sealed class NotaryError {
|
|||||||
data class TransactionInvalid(val cause: Throwable) : NotaryError() {
|
data class TransactionInvalid(val cause: Throwable) : NotaryError() {
|
||||||
override fun toString() = cause.toString()
|
override fun toString() = cause.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object WrongNotary: NotaryError()
|
||||||
}
|
}
|
||||||
|
@ -133,20 +133,6 @@ interface ServiceHub : ServicesForResolution {
|
|||||||
|
|
||||||
private val legalIdentityKey: PublicKey get() = this.myInfo.legalIdentitiesAndCerts.first().owningKey
|
private val legalIdentityKey: PublicKey get() = this.myInfo.legalIdentitiesAndCerts.first().owningKey
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper property to shorten code for fetching the the [PublicKey] portion of the
|
|
||||||
* Node's Notary signing identity. It is required that the Node hosts a notary service,
|
|
||||||
* otherwise an [IllegalArgumentException] will be thrown.
|
|
||||||
* 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
|
|
||||||
* the matching [java.security.PrivateKey] will be looked up internally and used to sign.
|
|
||||||
* If the key is actually a [net.corda.core.crypto.CompositeKey], the first leaf key hosted on this node
|
|
||||||
* will be used to create the signature.
|
|
||||||
*/
|
|
||||||
// TODO Remove that from ServiceHub, we could take that information from a transaction notary field and figure out what key to use from that.
|
|
||||||
// But, it's separate PR.
|
|
||||||
val notaryIdentityKey: PublicKey
|
|
||||||
|
|
||||||
// Helper method to construct an initial partially signed transaction from a [TransactionBuilder].
|
// Helper method to construct an initial partially signed transaction from a [TransactionBuilder].
|
||||||
private fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey, signatureMetadata: SignatureMetadata): SignedTransaction {
|
private fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey, signatureMetadata: SignatureMetadata): SignedTransaction {
|
||||||
return builder.toSignedTransaction(keyManagementService, publicKey, signatureMetadata)
|
return builder.toSignedTransaction(keyManagementService, publicKey, signatureMetadata)
|
||||||
|
@ -13,9 +13,11 @@ import net.corda.core.serialization.SingletonSerializeAsToken
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
|
import java.security.PublicKey
|
||||||
|
|
||||||
abstract class NotaryService : SingletonSerializeAsToken() {
|
abstract class NotaryService : SingletonSerializeAsToken() {
|
||||||
abstract val services: ServiceHub
|
abstract val services: ServiceHub
|
||||||
|
abstract val notaryIdentityKey: PublicKey
|
||||||
|
|
||||||
abstract fun start()
|
abstract fun start()
|
||||||
abstract fun stop()
|
abstract fun stop()
|
||||||
@ -70,11 +72,11 @@ abstract class TrustedAuthorityNotaryService : NotaryService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun sign(bits: ByteArray): DigitalSignature.WithKey {
|
fun sign(bits: ByteArray): DigitalSignature.WithKey {
|
||||||
return services.keyManagementService.sign(bits, services.notaryIdentityKey)
|
return services.keyManagementService.sign(bits, notaryIdentityKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sign(txId: SecureHash): TransactionSignature {
|
fun sign(txId: SecureHash): TransactionSignature {
|
||||||
val signableData = SignableData(txId, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(services.notaryIdentityKey).schemeNumberID))
|
val signableData = SignableData(txId, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(notaryIdentityKey).schemeNumberID))
|
||||||
return services.keyManagementService.sign(signableData, services.notaryIdentityKey)
|
return services.keyManagementService.sign(signableData, notaryIdentityKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,11 +10,12 @@ import net.corda.core.node.services.TimeWindowChecker
|
|||||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
|
import java.security.PublicKey
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
|
|
||||||
// START 1
|
// START 1
|
||||||
@CordaService
|
@CordaService
|
||||||
class MyCustomValidatingNotaryService(override val services: ServiceHub) : TrustedAuthorityNotaryService() {
|
class MyCustomValidatingNotaryService(override val services: ServiceHub, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||||
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
||||||
override val uniquenessProvider = PersistentUniquenessProvider()
|
override val uniquenessProvider = PersistentUniquenessProvider()
|
||||||
|
|
||||||
@ -35,9 +36,10 @@ class MyValidatingNotaryFlow(otherSide: Party, service: MyCustomValidatingNotary
|
|||||||
override fun receiveAndVerifyTx(): TransactionParts {
|
override fun receiveAndVerifyTx(): TransactionParts {
|
||||||
try {
|
try {
|
||||||
val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false))
|
val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false))
|
||||||
|
checkNotary(stx.notary)
|
||||||
checkSignatures(stx)
|
checkSignatures(stx)
|
||||||
val wtx = stx.tx
|
val wtx = stx.tx
|
||||||
return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow)
|
return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow, wtx.notary)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw when (e) {
|
throw when (e) {
|
||||||
is TransactionVerificationException,
|
is TransactionVerificationException,
|
||||||
@ -49,7 +51,7 @@ class MyValidatingNotaryFlow(otherSide: Party, service: MyCustomValidatingNotary
|
|||||||
|
|
||||||
private fun checkSignatures(stx: SignedTransaction) {
|
private fun checkSignatures(stx: SignedTransaction) {
|
||||||
try {
|
try {
|
||||||
stx.verifySignaturesExcept(serviceHub.notaryIdentityKey)
|
stx.verifySignaturesExcept(service.notaryIdentityKey)
|
||||||
} catch (e: SignatureException) {
|
} catch (e: SignatureException) {
|
||||||
throw NotaryException(NotaryError.TransactionInvalid(e))
|
throw NotaryException(NotaryError.TransactionInvalid(e))
|
||||||
}
|
}
|
||||||
|
@ -255,9 +255,16 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
*/
|
*/
|
||||||
fun <T : SerializeAsToken> installCordaService(serviceClass: Class<T>): T {
|
fun <T : SerializeAsToken> installCordaService(serviceClass: Class<T>): T {
|
||||||
serviceClass.requireAnnotation<CordaService>()
|
serviceClass.requireAnnotation<CordaService>()
|
||||||
val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java).apply { isAccessible = true }
|
|
||||||
val service = try {
|
val service = try {
|
||||||
constructor.newInstance(services)
|
if (NotaryService::class.java.isAssignableFrom(serviceClass)) {
|
||||||
|
check(myNotaryIdentity != null) { "Trying to install a notary service but no notary identity specified" }
|
||||||
|
val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java, PublicKey::class.java).apply { isAccessible = true }
|
||||||
|
constructor.newInstance(services, myNotaryIdentity!!.owningKey)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java).apply { isAccessible = true }
|
||||||
|
constructor.newInstance(services)
|
||||||
|
}
|
||||||
} catch (e: InvocationTargetException) {
|
} catch (e: InvocationTargetException) {
|
||||||
throw ServiceInstantiationException(e.cause)
|
throw ServiceInstantiationException(e.cause)
|
||||||
}
|
}
|
||||||
@ -576,12 +583,13 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
open protected fun makeCoreNotaryService(type: ServiceType): NotaryService? {
|
open protected fun makeCoreNotaryService(type: ServiceType): NotaryService? {
|
||||||
|
check(myNotaryIdentity != null) { "No notary identity initialized when creating a notary service" }
|
||||||
return when (type) {
|
return when (type) {
|
||||||
SimpleNotaryService.type -> SimpleNotaryService(services)
|
SimpleNotaryService.type -> SimpleNotaryService(services, myNotaryIdentity!!.owningKey)
|
||||||
ValidatingNotaryService.type -> ValidatingNotaryService(services)
|
ValidatingNotaryService.type -> ValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
|
||||||
RaftNonValidatingNotaryService.type -> RaftNonValidatingNotaryService(services)
|
RaftNonValidatingNotaryService.type -> RaftNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
|
||||||
RaftValidatingNotaryService.type -> RaftValidatingNotaryService(services)
|
RaftValidatingNotaryService.type -> RaftValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
|
||||||
BFTNonValidatingNotaryService.type -> BFTNonValidatingNotaryService(services)
|
BFTNonValidatingNotaryService.type -> BFTNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -713,7 +721,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
override val myInfo: NodeInfo get() = info
|
override val myInfo: NodeInfo get() = info
|
||||||
override val database: CordaPersistence get() = this@AbstractNode.database
|
override val database: CordaPersistence get() = this@AbstractNode.database
|
||||||
override val configuration: NodeConfiguration get() = this@AbstractNode.configuration
|
override val configuration: NodeConfiguration get() = this@AbstractNode.configuration
|
||||||
override val notaryIdentityKey: PublicKey get() = myNotaryIdentity?.owningKey ?: throw IllegalArgumentException("Node doesn't have notary identity key")
|
|
||||||
|
|
||||||
override fun <T : SerializeAsToken> cordaService(type: Class<T>): T {
|
override fun <T : SerializeAsToken> cordaService(type: Class<T>): T {
|
||||||
require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" }
|
require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" }
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.contracts.StateRef
|
|||||||
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.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.flows.NotaryError
|
||||||
import net.corda.core.flows.NotaryException
|
import net.corda.core.flows.NotaryException
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -20,6 +21,7 @@ import net.corda.core.utilities.*
|
|||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||||
import net.corda.node.utilities.NODE_DATABASE_PREFIX
|
import net.corda.node.utilities.NODE_DATABASE_PREFIX
|
||||||
|
import java.security.PublicKey
|
||||||
import javax.persistence.Entity
|
import javax.persistence.Entity
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
@ -28,7 +30,9 @@ import kotlin.concurrent.thread
|
|||||||
*
|
*
|
||||||
* A transaction is notarised when the consensus is reached by the cluster on its uniqueness, and time-window validity.
|
* A transaction is notarised when the consensus is reached by the cluster on its uniqueness, and time-window validity.
|
||||||
*/
|
*/
|
||||||
class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, cluster: BFTSMaRt.Cluster = distributedCluster) : NotaryService() {
|
class BFTNonValidatingNotaryService(override val services: ServiceHubInternal,
|
||||||
|
override val notaryIdentityKey: PublicKey,
|
||||||
|
cluster: BFTSMaRt.Cluster = distributedCluster) : NotaryService() {
|
||||||
companion object {
|
companion object {
|
||||||
val type = SimpleNotaryService.type.getSubType("bft")
|
val type = SimpleNotaryService.type.getSubType("bft")
|
||||||
private val log = loggerFor<BFTNonValidatingNotaryService>()
|
private val log = loggerFor<BFTNonValidatingNotaryService>()
|
||||||
@ -51,7 +55,7 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c
|
|||||||
thread(name = "BFT SMaRt replica $replicaId init", isDaemon = true) {
|
thread(name = "BFT SMaRt replica $replicaId init", isDaemon = true) {
|
||||||
configHandle.use {
|
configHandle.use {
|
||||||
val timeWindowChecker = TimeWindowChecker(services.clock)
|
val timeWindowChecker = TimeWindowChecker(services.clock)
|
||||||
val replica = Replica(it, replicaId, { createMap() }, services, timeWindowChecker)
|
val replica = Replica(it, replicaId, { createMap() }, services, notaryIdentityKey, timeWindowChecker)
|
||||||
replicaHolder.set(replica)
|
replicaHolder.set(replica)
|
||||||
log.info("BFT SMaRt replica $replicaId is running.")
|
log.info("BFT SMaRt replica $replicaId is running.")
|
||||||
}
|
}
|
||||||
@ -100,8 +104,8 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c
|
|||||||
toPersistentEntityKey = { PersistentStateRef(it.txhash.toString(), it.index) },
|
toPersistentEntityKey = { PersistentStateRef(it.txhash.toString(), it.index) },
|
||||||
fromPersistentEntity = {
|
fromPersistentEntity = {
|
||||||
//TODO null check will become obsolete after making DB/JPA columns not nullable
|
//TODO null check will become obsolete after making DB/JPA columns not nullable
|
||||||
var txId = it.id.txId ?: throw IllegalStateException("DB returned null SecureHash transactionId")
|
val txId = it.id.txId ?: throw IllegalStateException("DB returned null SecureHash transactionId")
|
||||||
var index = it.id.index ?: throw IllegalStateException("DB returned null SecureHash index")
|
val index = it.id.index ?: throw IllegalStateException("DB returned null SecureHash index")
|
||||||
Pair(StateRef(txhash = SecureHash.parse(txId), index = index),
|
Pair(StateRef(txhash = SecureHash.parse(txId), index = index),
|
||||||
UniquenessProvider.ConsumingTx(
|
UniquenessProvider.ConsumingTx(
|
||||||
id = SecureHash.parse(it.consumingTxHash),
|
id = SecureHash.parse(it.consumingTxHash),
|
||||||
@ -126,7 +130,8 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c
|
|||||||
replicaId: Int,
|
replicaId: Int,
|
||||||
createMap: () -> AppendOnlyPersistentMap<StateRef, UniquenessProvider.ConsumingTx, PersistedCommittedState, PersistentStateRef>,
|
createMap: () -> AppendOnlyPersistentMap<StateRef, UniquenessProvider.ConsumingTx, PersistedCommittedState, PersistentStateRef>,
|
||||||
services: ServiceHubInternal,
|
services: ServiceHubInternal,
|
||||||
timeWindowChecker: TimeWindowChecker) : BFTSMaRt.Replica(config, replicaId, createMap, services, timeWindowChecker) {
|
notaryIdentityKey: PublicKey,
|
||||||
|
timeWindowChecker: TimeWindowChecker) : BFTSMaRt.Replica(config, replicaId, createMap, services, notaryIdentityKey, timeWindowChecker) {
|
||||||
|
|
||||||
override fun executeCommand(command: ByteArray): ByteArray {
|
override fun executeCommand(command: ByteArray): ByteArray {
|
||||||
val request = command.deserialize<BFTSMaRt.CommitRequest>()
|
val request = command.deserialize<BFTSMaRt.CommitRequest>()
|
||||||
@ -139,8 +144,9 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c
|
|||||||
return try {
|
return try {
|
||||||
val id = ftx.id
|
val id = ftx.id
|
||||||
val inputs = ftx.inputs
|
val inputs = ftx.inputs
|
||||||
|
val notary = ftx.notary
|
||||||
validateTimeWindow(ftx.timeWindow)
|
validateTimeWindow(ftx.timeWindow)
|
||||||
|
if (notary !in services.myInfo.legalIdentities) throw NotaryException(NotaryError.WrongNotary)
|
||||||
commitInputStates(inputs, id, callerIdentity)
|
commitInputStates(inputs, id, callerIdentity)
|
||||||
log.debug { "Inputs committed successfully, signing $id" }
|
log.debug { "Inputs committed successfully, signing $id" }
|
||||||
BFTSMaRt.ReplicaResponse.Signature(sign(ftx))
|
BFTSMaRt.ReplicaResponse.Signature(sign(ftx))
|
||||||
|
@ -36,6 +36,7 @@ import net.corda.node.services.transactions.BFTSMaRt.Client
|
|||||||
import net.corda.node.services.transactions.BFTSMaRt.Replica
|
import net.corda.node.services.transactions.BFTSMaRt.Replica
|
||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import java.security.PublicKey
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -176,6 +177,7 @@ object BFTSMaRt {
|
|||||||
createMap: () -> AppendOnlyPersistentMap<StateRef, UniquenessProvider.ConsumingTx,
|
createMap: () -> AppendOnlyPersistentMap<StateRef, UniquenessProvider.ConsumingTx,
|
||||||
BFTNonValidatingNotaryService.PersistedCommittedState, PersistentStateRef>,
|
BFTNonValidatingNotaryService.PersistedCommittedState, PersistentStateRef>,
|
||||||
protected val services: ServiceHubInternal,
|
protected val services: ServiceHubInternal,
|
||||||
|
protected val notaryIdentityKey: PublicKey,
|
||||||
private val timeWindowChecker: TimeWindowChecker) : DefaultRecoverable() {
|
private val timeWindowChecker: TimeWindowChecker) : DefaultRecoverable() {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = loggerFor<Replica>()
|
private val log = loggerFor<Replica>()
|
||||||
@ -248,11 +250,11 @@ object BFTSMaRt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected fun sign(bytes: ByteArray): DigitalSignature.WithKey {
|
protected fun sign(bytes: ByteArray): DigitalSignature.WithKey {
|
||||||
return services.database.transaction { services.keyManagementService.sign(bytes, services.notaryIdentityKey) }
|
return services.database.transaction { services.keyManagementService.sign(bytes, notaryIdentityKey) }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun sign(filteredTransaction: FilteredTransaction): TransactionSignature {
|
protected fun sign(filteredTransaction: FilteredTransaction): TransactionSignature {
|
||||||
return services.database.transaction { services.createSignature(filteredTransaction, services.notaryIdentityKey) }
|
return services.database.transaction { services.createSignature(filteredTransaction, notaryIdentityKey) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
|
@ -27,9 +27,10 @@ class NonValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryS
|
|||||||
it.verify()
|
it.verify()
|
||||||
it.checkAllComponentsVisible(ComponentGroupEnum.INPUTS_GROUP)
|
it.checkAllComponentsVisible(ComponentGroupEnum.INPUTS_GROUP)
|
||||||
it.checkAllComponentsVisible(ComponentGroupEnum.TIMEWINDOW_GROUP)
|
it.checkAllComponentsVisible(ComponentGroupEnum.TIMEWINDOW_GROUP)
|
||||||
TransactionParts(it.id, it.inputs, it.timeWindow)
|
val notary = it.notary
|
||||||
|
TransactionParts(it.id, it.inputs, it.timeWindow, notary)
|
||||||
}
|
}
|
||||||
is NotaryChangeWireTransaction -> TransactionParts(it.id, it.inputs, null)
|
is NotaryChangeWireTransaction -> TransactionParts(it.id, it.inputs, null, it.notary)
|
||||||
else -> {
|
else -> {
|
||||||
throw IllegalArgumentException("Received unexpected transaction type: ${it::class.java.simpleName}," +
|
throw IllegalArgumentException("Received unexpected transaction type: ${it::class.java.simpleName}," +
|
||||||
"expected either ${FilteredTransaction::class.java.simpleName} or ${NotaryChangeWireTransaction::class.java.simpleName}")
|
"expected either ${FilteredTransaction::class.java.simpleName} or ${NotaryChangeWireTransaction::class.java.simpleName}")
|
||||||
|
@ -5,9 +5,10 @@ import net.corda.core.identity.Party
|
|||||||
import net.corda.core.node.services.TimeWindowChecker
|
import net.corda.core.node.services.TimeWindowChecker
|
||||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
|
import java.security.PublicKey
|
||||||
|
|
||||||
/** A non-validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
/** A non-validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
||||||
class RaftNonValidatingNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() {
|
class RaftNonValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||||
companion object {
|
companion object {
|
||||||
val type = SimpleNotaryService.type.getSubType("raft")
|
val type = SimpleNotaryService.type.getSubType("raft")
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,10 @@ import net.corda.core.identity.Party
|
|||||||
import net.corda.core.node.services.TimeWindowChecker
|
import net.corda.core.node.services.TimeWindowChecker
|
||||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
|
import java.security.PublicKey
|
||||||
|
|
||||||
/** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
/** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
||||||
class RaftValidatingNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() {
|
class RaftValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||||
companion object {
|
companion object {
|
||||||
val type = ValidatingNotaryService.type.getSubType("raft")
|
val type = ValidatingNotaryService.type.getSubType("raft")
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,10 @@ import net.corda.core.node.services.TimeWindowChecker
|
|||||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||||
import net.corda.nodeapi.ServiceType
|
import net.corda.nodeapi.ServiceType
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
|
import java.security.PublicKey
|
||||||
|
|
||||||
/** A simple Notary service that does not perform transaction validation */
|
/** A simple Notary service that does not perform transaction validation */
|
||||||
class SimpleNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() {
|
class SimpleNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||||
companion object {
|
companion object {
|
||||||
val type = ServiceType.notary.getSubType("simple")
|
val type = ServiceType.notary.getSubType("simple")
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,11 @@ class ValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryServ
|
|||||||
override fun receiveAndVerifyTx(): TransactionParts {
|
override fun receiveAndVerifyTx(): TransactionParts {
|
||||||
try {
|
try {
|
||||||
val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false))
|
val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false))
|
||||||
|
val notary = stx.notary
|
||||||
|
checkNotary(notary)
|
||||||
checkSignatures(stx)
|
checkSignatures(stx)
|
||||||
val wtx = stx.tx
|
val wtx = stx.tx
|
||||||
return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow)
|
return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow, notary!!)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw when (e) {
|
throw when (e) {
|
||||||
is TransactionVerificationException,
|
is TransactionVerificationException,
|
||||||
@ -38,7 +40,7 @@ class ValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryServ
|
|||||||
|
|
||||||
private fun checkSignatures(stx: SignedTransaction) {
|
private fun checkSignatures(stx: SignedTransaction) {
|
||||||
try {
|
try {
|
||||||
stx.verifySignaturesExcept(serviceHub.notaryIdentityKey)
|
stx.verifySignaturesExcept(service.notaryIdentityKey)
|
||||||
} catch(e: SignatureException) {
|
} catch(e: SignatureException) {
|
||||||
throw NotaryException(NotaryError.TransactionInvalid(e))
|
throw NotaryException(NotaryError.TransactionInvalid(e))
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,10 @@ import net.corda.core.node.services.TimeWindowChecker
|
|||||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||||
import net.corda.nodeapi.ServiceType
|
import net.corda.nodeapi.ServiceType
|
||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
|
import java.security.PublicKey
|
||||||
|
|
||||||
/** A Notary service that validates the transaction chain of the submitted transaction before committing it */
|
/** A Notary service that validates the transaction chain of the submitted transaction before committing it */
|
||||||
class ValidatingNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() {
|
class ValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||||
companion object {
|
companion object {
|
||||||
val type = ServiceType.notary.getSubType("validating")
|
val type = ServiceType.notary.getSubType("validating")
|
||||||
}
|
}
|
||||||
|
@ -646,7 +646,8 @@ class TwoPartyTradeFlowTests {
|
|||||||
val sigs = mutableListOf<TransactionSignature>()
|
val sigs = mutableListOf<TransactionSignature>()
|
||||||
val nodeKey = node.info.chooseIdentity().owningKey
|
val nodeKey = node.info.chooseIdentity().owningKey
|
||||||
sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey))
|
sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey))
|
||||||
sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(notaryNode.services.notaryIdentityKey).schemeNumberID)), notaryNode.services.notaryIdentityKey))
|
sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1,
|
||||||
|
Crypto.findSignatureScheme(notaryNode.info.legalIdentities[1].owningKey).schemeNumberID)), notaryNode.info.legalIdentities[1].owningKey))
|
||||||
extraSigningNodes.forEach { currentNode ->
|
extraSigningNodes.forEach { currentNode ->
|
||||||
sigs.add(currentNode.services.keyManagementService.sign(
|
sigs.add(currentNode.services.keyManagementService.sign(
|
||||||
SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)),
|
SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)),
|
||||||
|
@ -161,7 +161,7 @@ class NotaryServiceTests {
|
|||||||
fun issueState(node: StartedNode<*>): StateAndRef<*> {
|
fun issueState(node: StartedNode<*>): StateAndRef<*> {
|
||||||
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0))
|
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0))
|
||||||
val signedByNode = node.services.signInitialTransaction(tx)
|
val signedByNode = node.services.signInitialTransaction(tx)
|
||||||
val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey)
|
val stx = notaryNode.services.addSignature(signedByNode, notary.owningKey)
|
||||||
node.services.recordTransactions(stx)
|
node.services.recordTransactions(stx)
|
||||||
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
|
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ class ValidatingNotaryServiceTests {
|
|||||||
fun issueState(node: StartedNode<*>): StateAndRef<*> {
|
fun issueState(node: StartedNode<*>): StateAndRef<*> {
|
||||||
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0))
|
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0))
|
||||||
val signedByNode = node.services.signInitialTransaction(tx)
|
val signedByNode = node.services.signInitialTransaction(tx)
|
||||||
val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey)
|
val stx = notaryNode.services.addSignature(signedByNode, notary.owningKey)
|
||||||
node.services.recordTransactions(stx)
|
node.services.recordTransactions(stx)
|
||||||
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
|
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
|
||||||
}
|
}
|
||||||
|
@ -68,8 +68,6 @@ open class MockServiceHubInternal(
|
|||||||
get() = overrideClock ?: throw UnsupportedOperationException()
|
get() = overrideClock ?: throw UnsupportedOperationException()
|
||||||
override val myInfo: NodeInfo
|
override val myInfo: NodeInfo
|
||||||
get() = NodeInfo(listOf(MOCK_HOST_AND_PORT), listOf(DUMMY_IDENTITY_1), 1, serial = 1L) // Required to get a dummy platformVersion when required for tests.
|
get() = NodeInfo(listOf(MOCK_HOST_AND_PORT), listOf(DUMMY_IDENTITY_1), 1, serial = 1L) // Required to get a dummy platformVersion when required for tests.
|
||||||
override val notaryIdentityKey: PublicKey
|
|
||||||
get() = throw IllegalStateException("No notary identity in MockServiceHubInternal")
|
|
||||||
override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())
|
override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())
|
||||||
override val rpcFlows: List<Class<out FlowLogic<*>>>
|
override val rpcFlows: List<Class<out FlowLogic<*>>>
|
||||||
get() = throw UnsupportedOperationException()
|
get() = throw UnsupportedOperationException()
|
||||||
|
@ -257,11 +257,9 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
|
|||||||
|
|
||||||
override fun makeCoreNotaryService(type: ServiceType): NotaryService? {
|
override fun makeCoreNotaryService(type: ServiceType): NotaryService? {
|
||||||
if (type != BFTNonValidatingNotaryService.type) return super.makeCoreNotaryService(type)
|
if (type != BFTNonValidatingNotaryService.type) return super.makeCoreNotaryService(type)
|
||||||
return BFTNonValidatingNotaryService(services, object : BFTSMaRt.Cluster {
|
return BFTNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey, object : BFTSMaRt.Cluster {
|
||||||
override fun waitUntilAllReplicasHaveInitialized() {
|
override fun waitUntilAllReplicasHaveInitialized() {
|
||||||
val clusterNodes = mockNet.nodes.filter {
|
val clusterNodes = mockNet.nodes.filter { myNotaryIdentity!!.owningKey in it.started!!.info.legalIdentities.map { it.owningKey } }
|
||||||
services.notaryIdentityKey in it.info.legalIdentitiesAndCerts.map { it.owningKey }
|
|
||||||
}
|
|
||||||
if (clusterNodes.size != configuration.notaryClusterAddresses.size) {
|
if (clusterNodes.size != configuration.notaryClusterAddresses.size) {
|
||||||
throw IllegalStateException("Unable to enumerate all nodes in BFT cluster.")
|
throw IllegalStateException("Unable to enumerate all nodes in BFT cluster.")
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,6 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub {
|
|||||||
val identity = getTestPartyAndCertificate(MEGA_CORP.name, key.public)
|
val identity = getTestPartyAndCertificate(MEGA_CORP.name, key.public)
|
||||||
return NodeInfo(emptyList(), listOf(identity), 1, serial = 1L)
|
return NodeInfo(emptyList(), listOf(identity), 1, serial = 1L)
|
||||||
}
|
}
|
||||||
override val notaryIdentityKey: PublicKey get() = throw UnsupportedOperationException()
|
|
||||||
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)
|
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)
|
||||||
|
|
||||||
lateinit var hibernatePersister: HibernateObserver
|
lateinit var hibernatePersister: HibernateObserver
|
||||||
|
Reference in New Issue
Block a user