mirror of
https://github.com/corda/corda.git
synced 2025-01-21 20:08:27 +00:00
Deprecate TimeWindowChecker, make TimeWindowInvalid report exact current time and transaction time window (#2280)
* Make notary service return the current time and the transaction time window along with the TimeWindowInvalid error. Deprecate TimeWindowChecker. Add a static method for validating transaction time window to reduce code duplication.
This commit is contained in:
parent
0ff9c9e2e3
commit
e357a88181
@ -1304,7 +1304,7 @@ public @interface net.corda.core.flows.InitiatingFlow
|
||||
@org.jetbrains.annotations.NotNull public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.flows.NotaryError$TimeWindowInvalid extends net.corda.core.flows.NotaryError
|
||||
public static final net.corda.core.flows.NotaryError$TimeWindowInvalid INSTANCE
|
||||
@kotlin.jvm.JvmField @org.jetbrains.annotations.NotNull public static final net.corda.core.flows.NotaryError$TimeWindowInvalid INSTANCE
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.flows.NotaryError$TransactionInvalid extends net.corda.core.flows.NotaryError
|
||||
public <init>(Throwable)
|
||||
@ -1885,7 +1885,7 @@ public final class net.corda.core.node.services.TimeWindowChecker extends java.l
|
||||
public <init>()
|
||||
public final void commitInputStates(List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party)
|
||||
@org.jetbrains.annotations.NotNull protected org.slf4j.Logger getLog()
|
||||
@org.jetbrains.annotations.NotNull protected abstract net.corda.core.node.services.TimeWindowChecker getTimeWindowChecker()
|
||||
@org.jetbrains.annotations.NotNull protected net.corda.core.node.services.TimeWindowChecker getTimeWindowChecker()
|
||||
@org.jetbrains.annotations.NotNull protected abstract net.corda.core.node.services.UniquenessProvider getUniquenessProvider()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.TransactionSignature sign(net.corda.core.crypto.SecureHash)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.DigitalSignature$WithKey sign(byte[])
|
||||
|
@ -18,6 +18,7 @@ import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.UntrustworthyData
|
||||
import net.corda.core.utilities.unwrap
|
||||
import java.security.SignatureException
|
||||
import java.time.Instant
|
||||
import java.util.function.Predicate
|
||||
|
||||
class NotaryFlow {
|
||||
@ -167,7 +168,14 @@ sealed class NotaryError {
|
||||
}
|
||||
|
||||
/** Occurs when time specified in the [TimeWindow] command is outside the allowed tolerance. */
|
||||
object TimeWindowInvalid : NotaryError()
|
||||
data class TimeWindowInvalid(val currentTime: Instant, val txTimeWindow: TimeWindow) : NotaryError() {
|
||||
override fun toString() = "Current time $currentTime is outside the time bounds specified by the transaction: $txTimeWindow"
|
||||
|
||||
companion object {
|
||||
@JvmField @Deprecated("Here only for binary compatibility purposes, do not use.")
|
||||
val INSTANCE = TimeWindowInvalid(Instant.EPOCH, TimeWindow.fromOnly(Instant.EPOCH))
|
||||
}
|
||||
}
|
||||
|
||||
data class TransactionInvalid(val cause: Throwable) : NotaryError() {
|
||||
override fun toString() = cause.toString()
|
||||
|
@ -12,6 +12,7 @@ import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import org.slf4j.Logger
|
||||
import java.security.PublicKey
|
||||
import java.time.Clock
|
||||
|
||||
abstract class NotaryService : SingletonSerializeAsToken() {
|
||||
companion object {
|
||||
@ -27,6 +28,24 @@ abstract class NotaryService : SingletonSerializeAsToken() {
|
||||
if (custom) append(".custom")
|
||||
}.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current instant provided by the clock falls within the specified time window.
|
||||
*
|
||||
* @throws NotaryException if current time is outside the specified time window. The exception contains
|
||||
* the [NotaryError.TimeWindowInvalid] error.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(NotaryException::class)
|
||||
fun validateTimeWindow(clock: Clock, timeWindow: TimeWindow?) {
|
||||
if (timeWindow == null) return
|
||||
val currentTime = clock.instant()
|
||||
if (currentTime !in timeWindow) {
|
||||
throw NotaryException(
|
||||
NotaryError.TimeWindowInvalid(currentTime, timeWindow)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract val services: ServiceHub
|
||||
@ -52,14 +71,9 @@ abstract class TrustedAuthorityNotaryService : NotaryService() {
|
||||
}
|
||||
|
||||
protected open val log: Logger get() = staticLog
|
||||
// TODO: specify the valid time window in config, and convert TimeWindowChecker to a utility method
|
||||
protected abstract val timeWindowChecker: TimeWindowChecker
|
||||
protected abstract val uniquenessProvider: UniquenessProvider
|
||||
|
||||
fun validateTimeWindow(t: TimeWindow?) {
|
||||
if (t != null && !timeWindowChecker.isValid(t))
|
||||
throw NotaryException(NotaryError.TimeWindowInvalid)
|
||||
}
|
||||
fun validateTimeWindow(t: TimeWindow?) = NotaryService.validateTimeWindow(services.clock, t)
|
||||
|
||||
/**
|
||||
* A NotaryException is thrown if any of the states have been consumed by a different transaction. Note that
|
||||
@ -98,4 +112,7 @@ abstract class TrustedAuthorityNotaryService : NotaryService() {
|
||||
val signableData = SignableData(txId, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(notaryIdentityKey).schemeNumberID))
|
||||
return services.keyManagementService.sign(signableData, notaryIdentityKey)
|
||||
}
|
||||
|
||||
@Deprecated("This property is no longer used") @Suppress("DEPRECATION")
|
||||
protected open val timeWindowChecker: TimeWindowChecker get() = throw UnsupportedOperationException("No default implementation, need to override")
|
||||
}
|
@ -6,6 +6,7 @@ import java.time.Clock
|
||||
/**
|
||||
* Checks if the current instant provided by the input clock falls within the provided time-window.
|
||||
*/
|
||||
@Deprecated("This class is no longer used")
|
||||
class TimeWindowChecker(val clock: Clock = Clock.systemUTC()) {
|
||||
fun isValid(timeWindow: TimeWindow): Boolean = clock.instant() in timeWindow
|
||||
}
|
||||
|
@ -1,39 +0,0 @@
|
||||
package net.corda.core.node.services
|
||||
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.utilities.seconds
|
||||
import org.junit.Test
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class TimeWindowCheckerTests {
|
||||
val clock: Clock = Clock.fixed(Instant.now(), ZoneOffset.UTC)
|
||||
val timeWindowChecker = TimeWindowChecker(clock)
|
||||
|
||||
@Test
|
||||
fun `should return true for valid time-window`() {
|
||||
val now = clock.instant()
|
||||
val timeWindowBetween = TimeWindow.between(now - 10.seconds, now + 10.seconds)
|
||||
val timeWindowFromOnly = TimeWindow.fromOnly(now - 10.seconds)
|
||||
val timeWindowUntilOnly = TimeWindow.untilOnly(now + 10.seconds)
|
||||
assertTrue { timeWindowChecker.isValid(timeWindowBetween) }
|
||||
assertTrue { timeWindowChecker.isValid(timeWindowFromOnly) }
|
||||
assertTrue { timeWindowChecker.isValid(timeWindowUntilOnly) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return false for invalid time-window`() {
|
||||
val now = clock.instant()
|
||||
val timeWindowBetweenPast = TimeWindow.between(now - 10.seconds, now - 2.seconds)
|
||||
val timeWindowBetweenFuture = TimeWindow.between(now + 2.seconds, now + 10.seconds)
|
||||
val timeWindowFromOnlyFuture = TimeWindow.fromOnly(now + 10.seconds)
|
||||
val timeWindowUntilOnlyPast = TimeWindow.untilOnly(now - 10.seconds)
|
||||
assertFalse { timeWindowChecker.isValid(timeWindowBetweenPast) }
|
||||
assertFalse { timeWindowChecker.isValid(timeWindowBetweenFuture) }
|
||||
assertFalse { timeWindowChecker.isValid(timeWindowFromOnlyFuture) }
|
||||
assertFalse { timeWindowChecker.isValid(timeWindowUntilOnlyPast) }
|
||||
}
|
||||
}
|
@ -13,7 +13,6 @@ import net.corda.core.flows.NotaryException
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.services.NotaryService
|
||||
import net.corda.core.node.services.TimeWindowChecker
|
||||
import net.corda.core.node.services.UniquenessProvider
|
||||
import net.corda.core.schemas.PersistentStateRef
|
||||
import net.corda.core.serialization.deserialize
|
||||
@ -54,8 +53,7 @@ class BFTNonValidatingNotaryService(
|
||||
// Replica startup must be in parallel with other replicas, otherwise the constructor may not return:
|
||||
thread(name = "BFT SMaRt replica $replicaId init", isDaemon = true) {
|
||||
configHandle.use {
|
||||
val timeWindowChecker = TimeWindowChecker(services.clock)
|
||||
val replica = Replica(it, replicaId, { createMap() }, services, notaryIdentityKey, timeWindowChecker)
|
||||
val replica = Replica(it, replicaId, { createMap() }, services, notaryIdentityKey)
|
||||
replicaHolder.set(replica)
|
||||
log.info("BFT SMaRt replica $replicaId is running.")
|
||||
}
|
||||
@ -131,8 +129,7 @@ class BFTNonValidatingNotaryService(
|
||||
replicaId: Int,
|
||||
createMap: () -> AppendOnlyPersistentMap<StateRef, UniquenessProvider.ConsumingTx, PersistedCommittedState, PersistentStateRef>,
|
||||
services: ServiceHubInternal,
|
||||
notaryIdentityKey: PublicKey,
|
||||
timeWindowChecker: TimeWindowChecker) : BFTSMaRt.Replica(config, replicaId, createMap, services, notaryIdentityKey, timeWindowChecker) {
|
||||
notaryIdentityKey: PublicKey) : BFTSMaRt.Replica(config, replicaId, createMap, services, notaryIdentityKey) {
|
||||
|
||||
override fun executeCommand(command: ByteArray): ByteArray {
|
||||
val request = command.deserialize<BFTSMaRt.CommitRequest>()
|
||||
@ -146,7 +143,7 @@ class BFTNonValidatingNotaryService(
|
||||
val id = ftx.id
|
||||
val inputs = ftx.inputs
|
||||
val notary = ftx.notary
|
||||
validateTimeWindow(ftx.timeWindow)
|
||||
NotaryService.validateTimeWindow(services.clock, ftx.timeWindow)
|
||||
if (notary !in services.myInfo.legalIdentities) throw NotaryException(NotaryError.WrongNotary)
|
||||
commitInputStates(inputs, id, callerIdentity)
|
||||
log.debug { "Inputs committed successfully, signing $id" }
|
||||
|
@ -13,14 +13,12 @@ import bftsmart.tom.server.defaultservices.DefaultRecoverable
|
||||
import bftsmart.tom.server.defaultservices.DefaultReplier
|
||||
import bftsmart.tom.util.Extractor
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.flows.NotaryError
|
||||
import net.corda.core.flows.NotaryException
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.declaredField
|
||||
import net.corda.core.internal.toTypedArray
|
||||
import net.corda.core.node.services.TimeWindowChecker
|
||||
import net.corda.core.node.services.UniquenessProvider
|
||||
import net.corda.core.schemas.PersistentStateRef
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
@ -178,8 +176,7 @@ object BFTSMaRt {
|
||||
createMap: () -> AppendOnlyPersistentMap<StateRef, UniquenessProvider.ConsumingTx,
|
||||
BFTNonValidatingNotaryService.PersistedCommittedState, PersistentStateRef>,
|
||||
protected val services: ServiceHubInternal,
|
||||
protected val notaryIdentityKey: PublicKey,
|
||||
private val timeWindowChecker: TimeWindowChecker) : DefaultRecoverable() {
|
||||
protected val notaryIdentityKey: PublicKey) : DefaultRecoverable() {
|
||||
companion object {
|
||||
private val log = contextLogger()
|
||||
}
|
||||
@ -218,7 +215,7 @@ object BFTSMaRt {
|
||||
|
||||
/**
|
||||
* Implement logic to execute the command and commit the transaction to the log.
|
||||
* Helper methods are provided for transaction processing: [commitInputStates], [validateTimeWindow], and [sign].
|
||||
* Helper methods are provided for transaction processing: [commitInputStates], and [sign].
|
||||
*/
|
||||
abstract fun executeCommand(command: ByteArray): ByteArray?
|
||||
|
||||
@ -245,11 +242,6 @@ object BFTSMaRt {
|
||||
}
|
||||
}
|
||||
|
||||
protected fun validateTimeWindow(t: TimeWindow?) {
|
||||
if (t != null && !timeWindowChecker.isValid(t))
|
||||
throw NotaryException(NotaryError.TimeWindowInvalid)
|
||||
}
|
||||
|
||||
protected fun sign(bytes: ByteArray): DigitalSignature.WithKey {
|
||||
return services.database.transaction { services.keyManagementService.sign(bytes, notaryIdentityKey) }
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package net.corda.node.services.transactions
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.NotaryFlow
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.services.TimeWindowChecker
|
||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||
import java.security.PublicKey
|
||||
|
||||
@ -13,8 +12,6 @@ class RaftNonValidatingNotaryService(
|
||||
override val notaryIdentityKey: PublicKey,
|
||||
override val uniquenessProvider: RaftUniquenessProvider
|
||||
) : TrustedAuthorityNotaryService() {
|
||||
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
|
||||
|
||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
|
||||
return NonValidatingNotaryFlow(otherPartySession, this)
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package net.corda.node.services.transactions
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.NotaryFlow
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.services.TimeWindowChecker
|
||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||
import java.security.PublicKey
|
||||
|
||||
@ -13,8 +12,6 @@ class RaftValidatingNotaryService(
|
||||
override val notaryIdentityKey: PublicKey,
|
||||
override val uniquenessProvider: RaftUniquenessProvider
|
||||
) : TrustedAuthorityNotaryService() {
|
||||
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
|
||||
|
||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
|
||||
return ValidatingNotaryFlow(otherPartySession, this)
|
||||
}
|
||||
|
@ -2,14 +2,12 @@ package net.corda.node.services.transactions
|
||||
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.NotaryFlow
|
||||
import net.corda.core.node.services.TimeWindowChecker
|
||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import java.security.PublicKey
|
||||
|
||||
/** A simple Notary service that does not perform transaction validation */
|
||||
class SimpleNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
||||
override val uniquenessProvider = PersistentUniquenessProvider()
|
||||
|
||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = NonValidatingNotaryFlow(otherPartySession, this)
|
||||
|
@ -2,15 +2,12 @@ package net.corda.node.services.transactions
|
||||
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.NotaryFlow
|
||||
import net.corda.core.node.services.TimeWindowChecker
|
||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||
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 */
|
||||
class ValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
||||
|
||||
override val uniquenessProvider = PersistentUniquenessProvider()
|
||||
|
||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = ValidatingNotaryFlow(otherPartySession, this)
|
||||
|
@ -22,7 +22,6 @@ import java.security.SignatureException
|
||||
// START 1
|
||||
@CordaService
|
||||
class MyCustomValidatingNotaryService(override val services: AppServiceHub, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
||||
override val uniquenessProvider = PersistentUniquenessProvider()
|
||||
|
||||
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = MyValidatingNotaryFlow(otherPartySession, this)
|
||||
|
Loading…
Reference in New Issue
Block a user