Skip to content

Commit

Permalink
[EG-896] Additional uniqueness provider test cases (corda#6085)
Browse files Browse the repository at this point in the history
  • Loading branch information
relyafi authored Mar 24, 2020
1 parent dbefd62 commit c697b58
Showing 1 changed file with 200 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class UniquenessProviderTests(
}

/*
There are 6 types of transactions to test:
There are 7 types of transaction to test:
A B C D E F G
================== === === === === === === ===
Expand All @@ -95,27 +95,91 @@ class UniquenessProviderTests(
/* Group A: only time window */

@Test(timeout=300_000)
fun `commits transaction with valid time window`() {
val inputState1 = generateStateRef()
fun `rejects transaction before time window is valid`() {
val firstTxId = SecureHash.randomSHA256()
val timeWindow = TimeWindow.between(
Clock.systemUTC().instant().plus(30.minutes),
Clock.systemUTC().instant().plus(60.minutes))
val result = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, timeWindow).get()
assert(result is UniquenessProvider.Result.Failure)
val error = (result as UniquenessProvider.Result.Failure).error as NotaryError.TimeWindowInvalid
assertEquals(timeWindow, error.txTimeWindow)

// Once time window behaviour has changed, we should add an additional test case here to check
// that retry within time window still fails. We can't do that now because currently it will
// succeed and that will result in the past time window case succeeding too.

// Retry still fails after advancing past time window
testClock.advanceBy(90.minutes)
val result2 = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, timeWindow).get()
assert(result2 is UniquenessProvider.Result.Failure)
val error2 = (result2 as UniquenessProvider.Result.Failure).error as NotaryError.TimeWindowInvalid
assertEquals(timeWindow, error2.txTimeWindow)
}

@Test(timeout=300_000)
fun `commits transaction within time window`() {
val firstTxId = SecureHash.randomSHA256()
val timeWindow = TimeWindow.untilOnly(Clock.systemUTC().instant().plus(30.minutes))
val result = uniquenessProvider.commit(listOf(inputState1), firstTxId, identity, requestSignature, timeWindow).get()
val result = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, timeWindow).get()
assert(result is UniquenessProvider.Result.Success)

// Idempotency: can re-notarise successfully later.
testClock.advanceBy(90.minutes)
val result2 = uniquenessProvider.commit(listOf(inputState1), firstTxId, identity, requestSignature, timeWindow).get()
// Retry is successful whilst still within time window
testClock.advanceBy(10.minutes)
val result2 = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, timeWindow).get()
assert(result2 is UniquenessProvider.Result.Success)

// Retry is successful after time window has expired
testClock.advanceBy(80.minutes)
val result3 = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, timeWindow).get()
assert(result3 is UniquenessProvider.Result.Success)
}

@Test(timeout=300_000)
fun `rejects transaction with invalid time window`() {
val inputState1 = generateStateRef()
fun `rejects transaction after time window has expired`() {
val firstTxId = SecureHash.randomSHA256()
val invalidTimeWindow = TimeWindow.untilOnly(Clock.systemUTC().instant().minus(30.minutes))
val result = uniquenessProvider.commit(listOf(inputState1), firstTxId, identity, requestSignature, invalidTimeWindow).get()
val timeWindow = TimeWindow.untilOnly(Clock.systemUTC().instant().minus(30.minutes))
val result = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, timeWindow).get()
assert(result is UniquenessProvider.Result.Failure)
val error = (result as UniquenessProvider.Result.Failure).error as NotaryError.TimeWindowInvalid
assertEquals(invalidTimeWindow, error.txTimeWindow)
assertEquals(timeWindow, error.txTimeWindow)

// Retry still fails at a later time
testClock.advanceBy(10.minutes)
val result2 = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, timeWindow).get()
assert(result2 is UniquenessProvider.Result.Failure)
val error2 = (result2 as UniquenessProvider.Result.Failure).error as NotaryError.TimeWindowInvalid
assertEquals(timeWindow, error2.txTimeWindow)
}

@Test(timeout=300_000)
fun `time window only transactions are processed correctly when duplicate requests occur in succession`() {
val firstTxId = SecureHash.randomSHA256()
val secondTxId = SecureHash.randomSHA256()
val timeWindow = TimeWindow.untilOnly(Clock.systemUTC().instant().plus(30.minutes))
val invalidTimeWindow = TimeWindow.untilOnly(Clock.systemUTC().instant().minus(30.minutes))

val validFuture1 = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, timeWindow)
val validFuture2 = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, timeWindow)
val invalidFuture1 = uniquenessProvider.commit(
emptyList(), secondTxId, identity, requestSignature, invalidTimeWindow)
val invalidFuture2 = uniquenessProvider.commit(
emptyList(), secondTxId, identity, requestSignature, invalidTimeWindow)

// Ensure that transactions are processed correctly and duplicates get the same responses to original
assert(validFuture1.get() is UniquenessProvider.Result.Success)
assert(validFuture2.get() is UniquenessProvider.Result.Success)
assert(invalidFuture1.get() is UniquenessProvider.Result.Failure)
assert(invalidFuture2.get() is UniquenessProvider.Result.Failure)
}

/* Group B: only reference states */
Expand Down Expand Up @@ -154,6 +218,52 @@ class UniquenessProviderTests(
assertEquals(StateConsumptionDetails.ConsumedStateType.REFERENCE_INPUT_STATE, conflictCause.type)
}

@Test(timeout=300_000)
fun `commits retry transaction when reference states were spent since initial transaction`() {
val firstTxId = SecureHash.randomSHA256()
val referenceState = generateStateRef()

val result = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, references = listOf(referenceState))
.get()
assert(result is UniquenessProvider.Result.Success)

// Spend reference state
val secondTxId = SecureHash.randomSHA256()
val result2 = uniquenessProvider.commit(
listOf(referenceState), secondTxId, identity, requestSignature, references = emptyList())
.get()
assert(result2 is UniquenessProvider.Result.Success)

// Retry referencing the now spent state still succeeds
val result3 = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, references = listOf(referenceState))
.get()
assert(result3 is UniquenessProvider.Result.Success)
}

@Test(timeout=300_000)
fun `reference state only transactions are processed correctly when duplicate requests occur in succession`() {
val firstTxId = SecureHash.randomSHA256()
val secondTxId = SecureHash.randomSHA256()
val referenceState = generateStateRef()

val validFuture3 = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, references = listOf(referenceState))
val validFuture4 = uniquenessProvider.commit(
emptyList(), firstTxId, identity, requestSignature, references = listOf(referenceState))
val validFuture1 = uniquenessProvider.commit(
emptyList(), secondTxId, identity, requestSignature, references = listOf(referenceState))
val validFuture2 = uniquenessProvider.commit(
emptyList(), secondTxId, identity, requestSignature, references = listOf(referenceState))

// Ensure that transactions are processed correctly and duplicates get the same responses to original
assert(validFuture1.get() is UniquenessProvider.Result.Success)
assert(validFuture2.get() is UniquenessProvider.Result.Success)
assert(validFuture3.get() is UniquenessProvider.Result.Success)
assert(validFuture4.get() is UniquenessProvider.Result.Success)
}

/* Group C: reference states & time window */

@Test(timeout=300_000)
Expand Down Expand Up @@ -262,6 +372,28 @@ class UniquenessProviderTests(
assertEquals(firstTxId.sha256(), conflictCause.hashOfTransactionId)
}

@Test(timeout=300_000)
fun `input state only transactions are processed correctly when duplicate requests occur in succession`() {
val firstTxId = SecureHash.randomSHA256()
val secondTxId = SecureHash.randomSHA256()
val inputState = generateStateRef()

val validFuture1 = uniquenessProvider.commit(
listOf(inputState), firstTxId, identity, requestSignature)
val validFuture2 = uniquenessProvider.commit(
listOf(inputState), firstTxId, identity, requestSignature)
val invalidFuture1 = uniquenessProvider.commit(
listOf(inputState), secondTxId, identity, requestSignature)
val invalidFuture2 = uniquenessProvider.commit(
listOf(inputState), secondTxId, identity, requestSignature)

// Ensure that transactions are processed correctly and duplicates get the same responses to original
assert(validFuture1.get() is UniquenessProvider.Result.Success)
assert(validFuture2.get() is UniquenessProvider.Result.Success)
assert(invalidFuture1.get() is UniquenessProvider.Result.Failure)
assert(invalidFuture2.get() is UniquenessProvider.Result.Failure)
}

/* Group E: input states & time window */

@Test(timeout=300_000)
Expand Down Expand Up @@ -346,6 +478,37 @@ class UniquenessProviderTests(
assert(result2 is UniquenessProvider.Result.Success)
}

@Test(timeout=300_000)
fun `re-notarise after reference state is spent`() {
val firstTxId = SecureHash.randomSHA256()
val inputState = generateStateRef()
val referenceState = generateStateRef()
val timeWindow = TimeWindow.untilOnly(Clock.systemUTC().instant().plus(30.minutes))

val result = uniquenessProvider.commit(
listOf(inputState), firstTxId, identity, requestSignature, timeWindow, references = listOf(referenceState))
.get()
assert(result is UniquenessProvider.Result.Success)

// Spend the reference state.
val referenceSpend = uniquenessProvider.commit(
listOf(referenceState),
SecureHash.randomSHA256(),
identity,
requestSignature,
timeWindow,
emptyList()).get()
assert(referenceSpend is UniquenessProvider.Result.Success)

// Idempotency: can re-notarise successfully
testClock.advanceBy(90.minutes)
val result2 = uniquenessProvider.commit(
listOf(inputState), firstTxId, identity, requestSignature, timeWindow, references = listOf(referenceState))
.get()
// Known failure - this should return success. Will be fixed in a future release.
assert(result2 is UniquenessProvider.Result.Failure)
}

@Test(timeout=300_000)
fun `rejects transaction with unused reference states and used input states`() {
val firstTxId = SecureHash.randomSHA256()
Expand Down Expand Up @@ -387,6 +550,31 @@ class UniquenessProviderTests(
assertEquals(StateConsumptionDetails.ConsumedStateType.REFERENCE_INPUT_STATE, conflictCause.type)
}

@Test(timeout=300_000)
fun `input and reference state transactions are processed correctly when duplicate requests occur in succession`() {
val firstTxId = SecureHash.randomSHA256()
val secondTxId = SecureHash.randomSHA256()
val referenceState = generateStateRef()

// Ensure batch contains duplicates
val validFuture1 = uniquenessProvider.commit(
emptyList(), secondTxId, identity, requestSignature, references = listOf(referenceState))
val validFuture2 = uniquenessProvider.commit(
emptyList(), secondTxId, identity, requestSignature, references = listOf(referenceState))
val validFuture3 = uniquenessProvider.commit(
listOf(referenceState), firstTxId, identity, requestSignature)

// Attempt to use the reference state after it has been consumed
val validFuture4 = uniquenessProvider.commit(
emptyList(), SecureHash.randomSHA256(), identity, requestSignature, references = listOf(referenceState))

// Ensure that transactions are processed correctly and duplicates get the same responses to original
assert(validFuture1.get() is UniquenessProvider.Result.Success)
assert(validFuture2.get() is UniquenessProvider.Result.Success)
assert(validFuture3.get() is UniquenessProvider.Result.Success)
assert(validFuture4.get() is UniquenessProvider.Result.Failure)
}

/* Group G: input, reference states and time window – covered by previous tests. */

/* Transaction signing tests. */
Expand Down

0 comments on commit c697b58

Please sign in to comment.