mirror of
https://github.com/corda/corda.git
synced 2025-06-13 04:38:19 +00:00
ENT-1858: Notary cleanup (#4134)
* Migrated all non-BFT notary implementations to use async commits. * Mock network: await for async operation completion. When calling runNetwork() it keeps "pumping" messages between participants until no more messages are generated. The problem comes in when a flow suspends on an async operation: the mock network thinks the flow finished the work for the current step, and since no more messages are generated, completes the runNetwork() function. The message that the flow generates once it resumes after async operation completion never gets processed. This change makes runNetwork() wait until all flow async operations finish, and only then check whether no more messages can be transferred.
This commit is contained in:
@ -10,7 +10,7 @@ import net.corda.core.internal.*
|
||||
import net.corda.core.internal.cordapp.CordappImpl
|
||||
import net.corda.core.internal.cordapp.CordappInfoResolver
|
||||
import net.corda.core.internal.notary.NotaryService
|
||||
import net.corda.core.internal.notary.TrustedAuthorityNotaryService
|
||||
import net.corda.core.internal.notary.SinglePartyNotaryService
|
||||
import net.corda.core.node.services.CordaService
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.SerializationCustomSerializer
|
||||
@ -152,7 +152,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
||||
// the scanner won't find subclasses deeper down the hierarchy if any intermediate class is not
|
||||
// present in the CorDapp.
|
||||
val result = scanResult.getClassesWithSuperclass(NotaryService::class) +
|
||||
scanResult.getClassesWithSuperclass(TrustedAuthorityNotaryService::class)
|
||||
scanResult.getClassesWithSuperclass(SinglePartyNotaryService::class)
|
||||
logger.info("Found notary service CorDapp implementations: " + result.joinToString(", "))
|
||||
return result.firstOrNull()
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ import net.corda.core.contracts.ComponentGroupEnum
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.NotarisationPayload
|
||||
import net.corda.core.flows.NotarisationRequest
|
||||
import net.corda.core.internal.notary.SinglePartyNotaryService
|
||||
import net.corda.core.internal.notary.NotaryServiceFlow
|
||||
import net.corda.core.internal.notary.TrustedAuthorityNotaryService
|
||||
import net.corda.core.transactions.ContractUpgradeFilteredTransaction
|
||||
import net.corda.core.transactions.CoreTransaction
|
||||
import net.corda.core.transactions.FilteredTransaction
|
||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||
|
||||
class NonValidatingNotaryFlow(otherSideSession: FlowSession, service: TrustedAuthorityNotaryService) : NotaryServiceFlow(otherSideSession, service) {
|
||||
class NonValidatingNotaryFlow(otherSideSession: FlowSession, service: SinglePartyNotaryService) : NotaryServiceFlow(otherSideSession, service) {
|
||||
/**
|
||||
* The received transaction is not checked for contract-validity, as that would require fully
|
||||
* resolving it into a [TransactionForVerification], for which the caller would have to reveal the whole transaction
|
||||
|
@ -12,7 +12,7 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.NamedCacheFactory
|
||||
import net.corda.core.internal.concurrent.OpenFuture
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.notary.AsyncUniquenessProvider
|
||||
import net.corda.core.internal.notary.UniquenessProvider
|
||||
import net.corda.core.internal.notary.NotaryInternalException
|
||||
import net.corda.core.internal.notary.isConsumedByTheSameTx
|
||||
import net.corda.core.internal.notary.validateTimeWindow
|
||||
@ -36,7 +36,7 @@ import kotlin.concurrent.thread
|
||||
|
||||
/** A RDBMS backed Uniqueness provider */
|
||||
@ThreadSafe
|
||||
class PersistentUniquenessProvider(val clock: Clock, val database: CordaPersistence, cacheFactory: NamedCacheFactory) : AsyncUniquenessProvider, SingletonSerializeAsToken() {
|
||||
class PersistentUniquenessProvider(val clock: Clock, val database: CordaPersistence, cacheFactory: NamedCacheFactory) : UniquenessProvider, SingletonSerializeAsToken() {
|
||||
|
||||
@MappedSuperclass
|
||||
class BaseComittedState(
|
||||
@ -77,7 +77,7 @@ class PersistentUniquenessProvider(val clock: Clock, val database: CordaPersiste
|
||||
val requestSignature: NotarisationRequestSignature,
|
||||
val timeWindow: TimeWindow?,
|
||||
val references: List<StateRef>,
|
||||
val future: OpenFuture<AsyncUniquenessProvider.Result>)
|
||||
val future: OpenFuture<UniquenessProvider.Result>)
|
||||
|
||||
@Entity
|
||||
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}notary_committed_states")
|
||||
@ -133,15 +133,15 @@ class PersistentUniquenessProvider(val clock: Clock, val database: CordaPersiste
|
||||
*
|
||||
* Returns a future that will complete once the request is processed, containing the commit [Result].
|
||||
*/
|
||||
override fun commitAsync(
|
||||
override fun commit(
|
||||
states: List<StateRef>,
|
||||
txId: SecureHash,
|
||||
callerIdentity: Party,
|
||||
requestSignature: NotarisationRequestSignature,
|
||||
timeWindow: TimeWindow?,
|
||||
references: List<StateRef>
|
||||
): CordaFuture<AsyncUniquenessProvider.Result> {
|
||||
val future = openFuture<AsyncUniquenessProvider.Result>()
|
||||
): CordaFuture<UniquenessProvider.Result> {
|
||||
val future = openFuture<UniquenessProvider.Result>()
|
||||
val request = CommitRequest(states, txId, callerIdentity, requestSignature, timeWindow, references, future)
|
||||
requestQueue.put(request)
|
||||
return future
|
||||
@ -232,13 +232,13 @@ class PersistentUniquenessProvider(val clock: Clock, val database: CordaPersiste
|
||||
|
||||
private fun respondWithError(request: CommitRequest, exception: Exception) {
|
||||
if (exception is NotaryInternalException) {
|
||||
request.future.set(AsyncUniquenessProvider.Result.Failure(exception.error))
|
||||
request.future.set(UniquenessProvider.Result.Failure(exception.error))
|
||||
} else {
|
||||
request.future.setException(NotaryInternalException(NotaryError.General(Exception("Internal service error."))))
|
||||
}
|
||||
}
|
||||
|
||||
private fun respondWithSuccess(request: CommitRequest) {
|
||||
request.future.set(AsyncUniquenessProvider.Result.Success)
|
||||
request.future.set(UniquenessProvider.Result.Success)
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package net.corda.node.services.transactions
|
||||
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.internal.notary.SinglePartyNotaryService
|
||||
import net.corda.core.internal.notary.NotaryServiceFlow
|
||||
import net.corda.core.internal.notary.TrustedAuthorityNotaryService
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import java.security.PublicKey
|
||||
|
||||
/** An embedded notary service that uses the node's database to store committed states. */
|
||||
class SimpleNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||
class SimpleNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : SinglePartyNotaryService() {
|
||||
private val notaryConfig = services.configuration.notary
|
||||
?: throw IllegalArgumentException("Failed to register ${this::class.java}: notary configuration not present")
|
||||
|
||||
|
@ -8,9 +8,9 @@ import net.corda.core.flows.NotarisationPayload
|
||||
import net.corda.core.flows.NotarisationRequest
|
||||
import net.corda.core.flows.NotaryError
|
||||
import net.corda.core.internal.ResolveTransactionsFlow
|
||||
import net.corda.core.internal.notary.SinglePartyNotaryService
|
||||
import net.corda.core.internal.notary.NotaryInternalException
|
||||
import net.corda.core.internal.notary.NotaryServiceFlow
|
||||
import net.corda.core.internal.notary.TrustedAuthorityNotaryService
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionWithSignatures
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
@ -22,7 +22,7 @@ import java.security.SignatureException
|
||||
* has its input states "blocked" by a transaction from another party, and needs to establish whether that transaction was
|
||||
* indeed valid.
|
||||
*/
|
||||
class ValidatingNotaryFlow(otherSideSession: FlowSession, service: TrustedAuthorityNotaryService) : NotaryServiceFlow(otherSideSession, service) {
|
||||
class ValidatingNotaryFlow(otherSideSession: FlowSession, service: SinglePartyNotaryService) : NotaryServiceFlow(otherSideSession, service) {
|
||||
/**
|
||||
* Fully resolves the received transaction and its dependencies, runs contract verification logic and checks that
|
||||
* the transaction in question has all required signatures apart from the notary's.
|
||||
|
@ -4,7 +4,10 @@ import co.paralleluniverse.fibers.Suspendable
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -12,8 +15,9 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FlowIORequest
|
||||
import net.corda.core.internal.ResolveTransactionsFlow
|
||||
import net.corda.core.internal.bufferUntilSubscribed
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.notary.NotaryServiceFlow
|
||||
import net.corda.core.internal.notary.TrustedAuthorityNotaryService
|
||||
import net.corda.core.internal.notary.SinglePartyNotaryService
|
||||
import net.corda.core.internal.notary.UniquenessProvider
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -176,8 +180,15 @@ class TimedFlowTests {
|
||||
}.bufferUntilSubscribed().toBlocking().toFuture()
|
||||
}
|
||||
|
||||
private class TestNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||
override val uniquenessProvider = mock<UniquenessProvider>()
|
||||
private class TestNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : SinglePartyNotaryService() {
|
||||
override val uniquenessProvider = object : UniquenessProvider {
|
||||
/** A dummy commit method that immediately returns a success message. */
|
||||
override fun commit(states: List<StateRef>, txId: SecureHash, callerIdentity: Party, requestSignature: NotarisationRequestSignature, timeWindow: TimeWindow?, references: List<StateRef>): CordaFuture<UniquenessProvider.Result> {
|
||||
return openFuture<UniquenessProvider.Result>(). apply {
|
||||
set(UniquenessProvider.Result.Success)
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = TestNotaryFlow(otherPartySession, this)
|
||||
override fun start() {}
|
||||
override fun stop() {}
|
||||
|
@ -8,14 +8,15 @@ import net.corda.core.flows.NotarisationRequestSignature
|
||||
import net.corda.core.flows.NotaryError
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.notary.NotaryInternalException
|
||||
import net.corda.core.internal.notary.UniquenessProvider
|
||||
import net.corda.node.services.schema.NodeSchemaService
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.core.generateStateRef
|
||||
import net.corda.testing.internal.LogHelper
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.testing.internal.configureDatabase
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import org.junit.After
|
||||
@ -51,27 +52,26 @@ class PersistentUniquenessProviderTests {
|
||||
@Test
|
||||
fun `should commit a transaction with unused inputs without exception`() {
|
||||
val provider = PersistentUniquenessProvider(Clock.systemUTC(), database, TestingNamedCacheFactory())
|
||||
val inputState = generateStateRef()
|
||||
val inputState = generateStateRef()
|
||||
|
||||
provider.commit(listOf(inputState), txID, identity, requestSignature)
|
||||
provider.commit(listOf(inputState), txID, identity, requestSignature).get()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should report a conflict for a transaction with previously used inputs`() {
|
||||
val provider = PersistentUniquenessProvider(Clock.systemUTC(), database, TestingNamedCacheFactory())
|
||||
val inputState = generateStateRef()
|
||||
val inputState = generateStateRef()
|
||||
|
||||
val inputs = listOf(inputState)
|
||||
val firstTxId = txID
|
||||
provider.commit(inputs, firstTxId, identity, requestSignature)
|
||||
val inputs = listOf(inputState)
|
||||
val firstTxId = txID
|
||||
provider.commit(inputs, firstTxId, identity, requestSignature).get()
|
||||
|
||||
val secondTxId = SecureHash.randomSHA256()
|
||||
val ex = assertFailsWith<NotaryInternalException> {
|
||||
provider.commit(inputs, secondTxId, identity, requestSignature)
|
||||
}
|
||||
val error = ex.error as NotaryError.Conflict
|
||||
val secondTxId = SecureHash.randomSHA256()
|
||||
|
||||
val conflictCause = error.consumedStates[inputState]!!
|
||||
assertEquals(conflictCause.hashOfTransactionId, firstTxId.sha256())
|
||||
}
|
||||
val response:UniquenessProvider.Result = provider.commit(inputs, secondTxId, identity, requestSignature).get()
|
||||
val error = (response as UniquenessProvider.Result.Failure).error as NotaryError.Conflict
|
||||
|
||||
val conflictCause = error.consumedStates[inputState]!!
|
||||
assertEquals(conflictCause.hashOfTransactionId, firstTxId.sha256())
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user