Cleaned up TimeWindow and added a bit more docs.

This commit is contained in:
Shams Asari 2017-07-20 13:32:53 +01:00
parent bf98f64269
commit e702025f62
18 changed files with 189 additions and 96 deletions

2
.gitignore vendored
View File

@ -67,7 +67,7 @@ lib/dokka.jar
## Plugin-specific files: ## Plugin-specific files:
# IntelliJ # IntelliJ
/out/ **/out/
/classes/ /classes/
# mpeltonen/sbt-idea plugin # mpeltonen/sbt-idea plugin

View File

@ -21,7 +21,6 @@ import java.math.BigDecimal
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.time.Duration import java.time.Duration
import java.time.temporal.Temporal
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import java.util.concurrent.Future import java.util.concurrent.Future
@ -95,9 +94,6 @@ fun <A> ListenableFuture<out A>.toObservable(): Observable<A> {
} }
} }
// Simple infix function to add back null safety that the JDK lacks: timeA until timeB
infix fun Temporal.until(endExclusive: Temporal): Duration = Duration.between(this, endExclusive)
/** Returns the index of the given item or throws [IllegalArgumentException] if not found. */ /** Returns the index of the given item or throws [IllegalArgumentException] if not found. */
fun <T> List<T>.indexOfOrThrow(item: T): Int { fun <T> List<T>.indexOfOrThrow(item: T): Int {
val i = indexOf(item) val i = indexOf(item)

View File

@ -17,7 +17,6 @@ import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.security.PublicKey import java.security.PublicKey
import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.jar.JarInputStream import java.util.jar.JarInputStream
@ -328,63 +327,6 @@ data class AuthenticatedObject<out T : Any>(
) )
// DOCEND 6 // DOCEND 6
/**
* 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
* between (fromTime, untilTime).
* Usually, a time-window is required to have both sides set (fromTime, untilTime).
* However, some apps may require that a time-window has a start [Instant] (fromTime), but no end [Instant] (untilTime) and vice versa.
* TODO: Consider refactoring using TimeWindow abstraction like TimeWindow.From, TimeWindow.Until, TimeWindow.Between.
*/
@CordaSerializable
class TimeWindow private constructor(
/** The time at which this transaction is said to have occurred is after this moment. */
val fromTime: Instant?,
/** The time at which this transaction is said to have occurred is before this moment. */
val untilTime: Instant?
) {
companion object {
/** Use when the left-side [fromTime] of a [TimeWindow] is only required and we don't need an end instant (untilTime). */
@JvmStatic
fun fromOnly(fromTime: Instant) = TimeWindow(fromTime, null)
/** Use when the right-side [untilTime] of a [TimeWindow] is only required and we don't need a start instant (fromTime). */
@JvmStatic
fun untilOnly(untilTime: Instant) = TimeWindow(null, untilTime)
/** Use when both sides of a [TimeWindow] must be set ([fromTime], [untilTime]). */
@JvmStatic
fun between(fromTime: Instant, untilTime: Instant): TimeWindow {
require(fromTime < untilTime) { "fromTime should be earlier than untilTime" }
return TimeWindow(fromTime, untilTime)
}
/** Use when we have a start time and a period of validity. */
@JvmStatic
fun fromStartAndDuration(fromTime: Instant, duration: Duration): TimeWindow = between(fromTime, fromTime + duration)
/**
* When we need to create a [TimeWindow] based on a specific time [Instant] and some tolerance in both sides of this instant.
* The result will be the following time-window: ([time] - [tolerance], [time] + [tolerance]).
*/
@JvmStatic
fun withTolerance(time: Instant, tolerance: Duration) = between(time - tolerance, time + tolerance)
}
/** The midpoint is calculated as fromTime + (untilTime - fromTime)/2. Note that it can only be computed if both sides are set. */
val midpoint: Instant get() = fromTime!! + Duration.between(fromTime, untilTime!!).dividedBy(2)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is TimeWindow) return false
return (fromTime == other.fromTime && untilTime == other.untilTime)
}
override fun hashCode() = 31 * (fromTime?.hashCode() ?: 0) + (untilTime?.hashCode() ?: 0)
override fun toString() = "TimeWindow(fromTime=$fromTime, untilTime=$untilTime)"
}
// DOCSTART 5 // DOCSTART 5
/** /**
* Implemented by a program that implements business logic on the shared ledger. All participants run this code for * Implemented by a program that implements business logic on the shared ledger. All participants run this code for

View File

@ -0,0 +1,87 @@
package net.corda.core.contracts
import net.corda.core.internal.div
import net.corda.core.internal.until
import net.corda.core.serialization.CordaSerializable
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)`.
*
* 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.
*/
@CordaSerializable
abstract class TimeWindow {
companion object {
/** 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. */
@JvmStatic
fun untilOnly(untilTime: Instant): TimeWindow = Until(untilTime)
/**
* Creates a [TimeWindow] with the time interval `[fromTime, untilTime)`. [midpoint] will return
* `fromTime + (untilTime - fromTime) / 2`.
* @throws IllegalArgumentException If [fromTime] [untilTime]
*/
@JvmStatic
fun between(fromTime: Instant, untilTime: Instant): TimeWindow = Between(fromTime, untilTime)
/**
* Creates a [TimeWindow] with the time interval `[fromTime, fromTime + duration)`. [midpoint] will return
* `fromTime + duration / 2`
*/
@JvmStatic
fun fromStartAndDuration(fromTime: Instant, duration: Duration): TimeWindow = between(fromTime, fromTime + duration)
/**
* Creates a [TimeWindow] which is centered around [instant] with the given [tolerance] on both sides, i.e the
* time interval `[instant - tolerance, instant + tolerance)`. [midpoint] will return [instant].
*/
@JvmStatic
fun withTolerance(instant: Instant, tolerance: Duration) = between(instant - tolerance, instant + tolerance)
}
/** Returns the inclusive lower-bound of this [TimeWindow]'s interval, with null implying infinity. */
abstract val fromTime: Instant?
/** Returns the exclusive upper-bound of this [TimeWindow]'s interval, with null implying infinity. */
abstract val untilTime: Instant?
/**
* Returns the midpoint of [fromTime] and [untilTime] if both are non-null, calculated as
* `fromTime + (untilTime - fromTime)/2`, otherwise returns null.
*/
abstract val midpoint: Instant?
/** Returns true iff the given [instant] is within the time interval of this [TimeWindow]. */
abstract operator fun contains(instant: Instant): Boolean
private data class From(override val fromTime: Instant) : 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, ∞]"
}
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)"
}
private data class Between(override val fromTime: Instant, override val untilTime: Instant) : TimeWindow() {
init {
require(fromTime < untilTime) { "fromTime must be earlier than untilTime" }
}
override val midpoint: Instant get() = fromTime + (fromTime until untilTime) / 2
override fun contains(instant: Instant): Boolean = instant >= fromTime && instant < untilTime
override fun toString(): String = "[$fromTime, $untilTime)"
}
}

View File

@ -7,9 +7,16 @@ import java.nio.charset.Charset
import java.nio.charset.StandardCharsets.UTF_8 import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.* import java.nio.file.*
import java.nio.file.attribute.FileAttribute import java.nio.file.attribute.FileAttribute
import java.time.Duration
import java.time.temporal.Temporal
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.reflect.KClass import kotlin.reflect.KClass
infix fun Temporal.until(endExclusive: Temporal): Duration = Duration.between(this, endExclusive)
operator fun Duration.div(divider: Long): Duration = dividedBy(divider)
operator fun Duration.times(multiplicand: Long): Duration = multipliedBy(multiplicand)
/** /**
* Allows you to write code like: Paths.get("someDir") / "subdir" / "filename" but using the Paths API to avoid platform * Allows you to write code like: Paths.get("someDir") / "subdir" / "filename" but using the Paths API to avoid platform
* separator problems. * separator problems.

View File

@ -7,16 +7,5 @@ import java.time.Clock
* Checks if the current instant provided by the input clock falls within the provided time-window. * Checks if the current instant provided by the input clock falls within the provided time-window.
*/ */
class TimeWindowChecker(val clock: Clock = Clock.systemUTC()) { class TimeWindowChecker(val clock: Clock = Clock.systemUTC()) {
fun isValid(timeWindow: TimeWindow): Boolean { fun isValid(timeWindow: TimeWindow): Boolean = clock.instant() in timeWindow
val fromTime = timeWindow.fromTime
val untilTime = timeWindow.untilTime
val now = clock.instant()
// We don't need to test for (fromTime == null && untilTime == null) or backwards bounds because the TimeWindow
// constructor already checks that.
if (fromTime != null && now < fromTime) return false
if (untilTime != null && now > untilTime) return false
return true
}
} }

View File

@ -0,0 +1,67 @@
package net.corda.core.contracts
import net.corda.core.utilities.millis
import net.corda.core.utilities.minutes
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneOffset.UTC
class TimeWindowTest {
private val now = Instant.now()
@Test
fun fromOnly() {
val timeWindow = TimeWindow.fromOnly(now)
assertThat(timeWindow.fromTime).isEqualTo(now)
assertThat(timeWindow.untilTime).isNull()
assertThat(timeWindow.midpoint).isNull()
assertThat(timeWindow.contains(now - 1.millis)).isFalse()
assertThat(timeWindow.contains(now)).isTrue()
assertThat(timeWindow.contains(now + 1.millis)).isTrue()
}
@Test
fun untilOnly() {
val timeWindow = TimeWindow.untilOnly(now)
assertThat(timeWindow.fromTime).isNull()
assertThat(timeWindow.untilTime).isEqualTo(now)
assertThat(timeWindow.midpoint).isNull()
assertThat(timeWindow.contains(now - 1.millis)).isTrue()
assertThat(timeWindow.contains(now)).isFalse()
assertThat(timeWindow.contains(now + 1.millis)).isFalse()
}
@Test
fun between() {
val today = LocalDate.now()
val fromTime = today.atTime(12, 0).toInstant(UTC)
val untilTime = today.atTime(12, 30).toInstant(UTC)
val timeWindow = TimeWindow.between(fromTime, untilTime)
assertThat(timeWindow.fromTime).isEqualTo(fromTime)
assertThat(timeWindow.untilTime).isEqualTo(untilTime)
assertThat(timeWindow.midpoint).isEqualTo(today.atTime(12, 15).toInstant(UTC))
assertThat(timeWindow.contains(fromTime - 1.millis)).isFalse()
assertThat(timeWindow.contains(fromTime)).isTrue()
assertThat(timeWindow.contains(fromTime + 1.millis)).isTrue()
assertThat(timeWindow.contains(untilTime)).isFalse()
assertThat(timeWindow.contains(untilTime + 1.millis)).isFalse()
}
@Test
fun fromStartAndDuration() {
val timeWindow = TimeWindow.fromStartAndDuration(now, 10.minutes)
assertThat(timeWindow.fromTime).isEqualTo(now)
assertThat(timeWindow.untilTime).isEqualTo(now + 10.minutes)
assertThat(timeWindow.midpoint).isEqualTo(now + 5.minutes)
}
@Test
fun withTolerance() {
val timeWindow = TimeWindow.withTolerance(now, 10.minutes)
assertThat(timeWindow.fromTime).isEqualTo(now - 10.minutes)
assertThat(timeWindow.untilTime).isEqualTo(now + 10.minutes)
assertThat(timeWindow.midpoint).isEqualTo(now)
}
}

View File

@ -5,12 +5,12 @@ import net.corda.core.utilities.seconds
import org.junit.Test import org.junit.Test
import java.time.Clock import java.time.Clock
import java.time.Instant import java.time.Instant
import java.time.ZoneId import java.time.ZoneOffset
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
class TimeWindowCheckerTests { class TimeWindowCheckerTests {
val clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()) val clock: Clock = Clock.fixed(Instant.now(), ZoneOffset.UTC)
val timeWindowChecker = TimeWindowChecker(clock) val timeWindowChecker = TimeWindowChecker(clock)
@Test @Test

View File

@ -72,7 +72,7 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
*/ */
override fun verify(tx: LedgerTransaction) { override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<TradeApprovalContract.Commands>() val command = tx.commands.requireSingleCommand<TradeApprovalContract.Commands>()
require(tx.timeWindow?.midpoint != null) { "must have a time-window" } requireNotNull(tx.timeWindow) { "must have a time-window" }
when (command.value) { when (command.value) {
is Commands.Issue -> { is Commands.Issue -> {
requireThat { requireThat {

View File

@ -2,8 +2,10 @@ package net.corda.services.messaging
import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.*
import net.corda.core.crypto.random63BitValue import net.corda.core.crypto.random63BitValue
import net.corda.core.elapsedTime
import net.corda.core.getOrThrow
import net.corda.core.internal.times
import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
@ -43,7 +45,7 @@ class P2PMessagingTest : NodeBasedTest() {
// Start the network map a second time - this will restore message queues from the journal. // Start the network map a second time - this will restore message queues from the journal.
// This will hang and fail prior the fix. https://github.com/corda/corda/issues/37 // This will hang and fail prior the fix. https://github.com/corda/corda/issues/37
stopAllNodes() stopAllNodes()
startNodes().getOrThrow(timeout = startUpDuration.multipliedBy(3)) startNodes().getOrThrow(timeout = startUpDuration * 3)
} }
// https://github.com/corda/corda/issues/71 // https://github.com/corda/corda/issues/71

View File

@ -3,12 +3,12 @@ package net.corda.node.utilities
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import co.paralleluniverse.strands.SettableFuture import co.paralleluniverse.strands.SettableFuture
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.internal.until
import net.corda.core.then import net.corda.core.then
import rx.Observable import rx.Observable
import rx.Subscriber import rx.Subscriber
import rx.subscriptions.Subscriptions import rx.subscriptions.Subscriptions
import java.time.Clock import java.time.Clock
import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.concurrent.* import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicLong
@ -80,7 +80,7 @@ fun Clock.awaitWithDeadline(deadline: Instant, future: Future<*> = GuavaSettable
} else { } else {
null null
} }
nanos = Duration.between(this.instant(), deadline).toNanos() nanos = (instant() until deadline).toNanos()
if (nanos > 0) { if (nanos > 0) {
try { try {
// This will return when it times out, or when the clock mutates or when when the original future completes. // This will return when it times out, or when the clock mutates or when when the original future completes.

View File

@ -1,10 +1,13 @@
package net.corda.node.utilities package net.corda.node.utilities
import net.corda.core.internal.until
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SerializeAsTokenContext import net.corda.core.serialization.SerializeAsTokenContext
import net.corda.core.serialization.SingletonSerializationToken
import net.corda.core.serialization.SingletonSerializationToken.Companion.singletonSerializationToken import net.corda.core.serialization.SingletonSerializationToken.Companion.singletonSerializationToken
import java.time.* import java.time.Clock
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
/** /**
@ -21,7 +24,7 @@ class TestClock(private var delegateClock: Clock = Clock.systemUTC()) : MutableC
val currentDate = LocalDate.now(this) val currentDate = LocalDate.now(this)
if (currentDate.isBefore(date)) { if (currentDate.isBefore(date)) {
// It's ok to increment // It's ok to increment
delegateClock = Clock.offset(delegateClock, Duration.between(currentDate.atStartOfDay(), date.atStartOfDay())) delegateClock = Clock.offset(delegateClock, currentDate.atStartOfDay() until date.atStartOfDay())
notifyMutationObservers() notifyMutationObservers()
return true return true
} }

View File

@ -522,7 +522,7 @@ class InterestRateSwap : Contract {
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
groupingKey: Unit?): Set<Commands> { groupingKey: Unit?): Set<Commands> {
require(tx.timeWindow?.midpoint != null) { "must be have a time-window)" } requireNotNull(tx.timeWindow) { "must be have a time-window)" }
// We return an empty set because we don't process any commands // We return an empty set because we don't process any commands
return emptySet() return emptySet()
} }
@ -584,8 +584,7 @@ class InterestRateSwap : Contract {
"There is only one change in the IRS floating leg payment schedule" using (paymentDifferences.size == 1) "There is only one change in the IRS floating leg payment schedule" using (paymentDifferences.size == 1)
} }
val changedRates = paymentDifferences.single().second // Ignore the date of the changed rate (we checked that earlier). val (oldFloatingRatePaymentEvent, newFixedRatePaymentEvent) = paymentDifferences.single().second // Ignore the date of the changed rate (we checked that earlier).
val (oldFloatingRatePaymentEvent, newFixedRatePaymentEvent) = changedRates
val fixValue = command.value.fix val fixValue = command.value.fix
// Need to check that everything is the same apart from the new fixed rate entry. // Need to check that everything is the same apart from the new fixed rate entry.
requireThat { requireThat {

View File

@ -23,7 +23,7 @@ data class OGTrade(override val legalContractReference: SecureHash = SecureHash.
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
groupingKey: Unit?): Set<Commands> { groupingKey: Unit?): Set<Commands> {
require(tx.timeWindow?.midpoint != null) { "must have a time-window" } requireNotNull(tx.timeWindow) { "must have a time-window" }
// We return an empty set because we don't process any commands // We return an empty set because we don't process any commands
return emptySet() return emptySet()
} }
@ -43,7 +43,7 @@ data class OGTrade(override val legalContractReference: SecureHash = SecureHash.
groupingKey: UniqueIdentifier?): Set<Commands> { groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Agree>() val command = tx.commands.requireSingleCommand<Commands.Agree>()
require(inputs.size == 0) { "Inputs must be empty" } require(inputs.isEmpty()) { "Inputs must be empty" }
require(outputs.size == 1) { "" } require(outputs.size == 1) { "" }
require(outputs[0].buyer != outputs[0].seller) require(outputs[0].buyer != outputs[0].seller)
require(outputs[0].participants.containsAll(outputs[0].participants)) require(outputs[0].participants.containsAll(outputs[0].participants))

View File

@ -25,7 +25,7 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
groupingKey: Unit?): Set<Commands> { groupingKey: Unit?): Set<Commands> {
require(tx.timeWindow?.midpoint != null) { "must have a time-window)" } requireNotNull(tx.timeWindow) { "must have a time-window)" }
// We return an empty set because we don't process any commands // We return an empty set because we don't process any commands
return emptySet() return emptySet()
} }
@ -69,7 +69,7 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
val command = tx.commands.requireSingleCommand<Commands.Agree>() val command = tx.commands.requireSingleCommand<Commands.Agree>()
requireThat { requireThat {
"there are no inputs" using (inputs.size == 0) "there are no inputs" using (inputs.isEmpty())
"there is one output" using (outputs.size == 1) "there is one output" using (outputs.size == 1)
"valuer must be a party" using (outputs[0].participants.contains(outputs[0].valuer)) "valuer must be a party" using (outputs[0].participants.contains(outputs[0].valuer))
} }

View File

@ -16,6 +16,7 @@ import net.corda.core.crypto.appendToCommonName
import net.corda.core.crypto.commonName import net.corda.core.crypto.commonName
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.times
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
@ -348,7 +349,7 @@ fun <A> poll(
override fun run() { override fun run() {
if (resultFuture.isCancelled) return // Give up, caller can no longer get the result. if (resultFuture.isCancelled) return // Give up, caller can no longer get the result.
if (++counter == warnCount) { if (++counter == warnCount) {
log.warn("Been polling $pollName for ${pollInterval.multipliedBy(warnCount.toLong()).seconds} seconds...") log.warn("Been polling $pollName for ${(pollInterval * warnCount.toLong()).seconds} seconds...")
} }
try { try {
val checkResult = check() val checkResult = check()

View File

@ -1,8 +1,8 @@
package net.corda.testing.node package net.corda.testing.node
import net.corda.core.internal.until
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SerializeAsTokenContext import net.corda.core.serialization.SerializeAsTokenContext
import net.corda.core.serialization.SingletonSerializationToken
import net.corda.core.serialization.SingletonSerializationToken.Companion.singletonSerializationToken import net.corda.core.serialization.SingletonSerializationToken.Companion.singletonSerializationToken
import net.corda.node.utilities.MutableClock import net.corda.node.utilities.MutableClock
import java.time.Clock import java.time.Clock
@ -35,7 +35,7 @@ class TestClock(private var delegateClock: Clock = Clock.systemUTC()) : MutableC
* *
* This will only be approximate due to the time ticking away, but will be some time shortly after the requested [Instant]. * This will only be approximate due to the time ticking away, but will be some time shortly after the requested [Instant].
*/ */
@Synchronized fun setTo(newInstant: Instant) = advanceBy(Duration.between(instant(), newInstant)) @Synchronized fun setTo(newInstant: Instant) = advanceBy(instant() until newInstant)
@Synchronized override fun instant(): Instant { @Synchronized override fun instant(): Instant {
return delegateClock.instant() return delegateClock.instant()

View File

@ -5,7 +5,7 @@ import com.google.common.util.concurrent.RateLimiter
import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.SettableFuture
import net.corda.core.catch import net.corda.core.catch
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.core.until import net.corda.core.internal.until
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.demobench.model.NodeConfig import net.corda.demobench.model.NodeConfig
import net.corda.demobench.readErrorLines import net.corda.demobench.readErrorLines