diff --git a/core/src/main/kotlin/net/corda/core/contracts/TimeWindow.kt b/core/src/main/kotlin/net/corda/core/contracts/TimeWindow.kt index 960bd9f310..e26ea50cd0 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TimeWindow.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TimeWindow.kt @@ -3,24 +3,29 @@ package net.corda.core.contracts import net.corda.core.internal.div import net.corda.core.internal.until import net.corda.core.serialization.CordaSerializable +import net.corda.core.transactions.WireTransaction import java.time.Duration import java.time.Instant /** - * A time-window is required for validation/notarization purposes. If present in a transaction, contains a time that was - * verified by the uniqueness service. The true time must be in the time interval `[fromTime, untilTime)`. + * An interval on the time-line; not a single instantaneous point. * - * Usually a time-window is required to have both sides defined. However some apps may require a time-window which is - * open-ended on one of the two sides. + * There is no such thing as _exact_ time in networked systems due to the underlying physics involved and other issues + * such as network latency. The best that can be approximated is "fuzzy time" or an instant of time which has margin of + * tolerance around it. This is what [TimeWindow] represents. Time windows can be open-ended (i.e. specify only one of + * [fromTime] and [untilTime]) or they can be fully bounded. + * + * [WireTransaction] has an optional time-window property, which if specified, restricts the validity of the transaction + * to that time-interval as the Consensus Service will not sign it if it's received outside of this window. */ @CordaSerializable abstract class TimeWindow { companion object { - /** Creates a [TimeWindow] with null [untilTime], i.e. the time interval `[fromTime, ∞]`. [midpoint] will return null. */ + /** Creates a [TimeWindow] with null [untilTime], i.e. the time interval `[fromTime, ∞)`. [midpoint] will return null. */ @JvmStatic fun fromOnly(fromTime: Instant): TimeWindow = From(fromTime) - /** Creates a [TimeWindow] with null [fromTime], i.e. the time interval `[∞, untilTime)`. [midpoint] will return null. */ + /** Creates a [TimeWindow] with null [fromTime], i.e. the time interval `(∞, untilTime)`. [midpoint] will return null. */ @JvmStatic fun untilOnly(untilTime: Instant): TimeWindow = Until(untilTime) @@ -55,7 +60,7 @@ abstract class TimeWindow { /** * Returns the midpoint of [fromTime] and [untilTime] if both are non-null, calculated as - * `fromTime + (untilTime - fromTime)/2`, otherwise returns null. + * `fromTime + (untilTime - fromTime) / 2`, otherwise returns null. */ abstract val midpoint: Instant? @@ -66,14 +71,14 @@ abstract class TimeWindow { override val untilTime: Instant? get() = null override val midpoint: Instant? get() = null override fun contains(instant: Instant): Boolean = instant >= fromTime - override fun toString(): String = "[$fromTime, ∞]" + override fun toString(): String = "[$fromTime, ∞)" } private data class Until(override val untilTime: Instant) : TimeWindow() { override val fromTime: Instant? get() = null override val midpoint: Instant? get() = null override fun contains(instant: Instant): Boolean = instant < untilTime - override fun toString(): String = "[∞, $untilTime)" + override fun toString(): String = "(∞, $untilTime)" } private data class Between(override val fromTime: Instant, override val untilTime: Instant) : TimeWindow() {