mirror of
https://github.com/corda/corda.git
synced 2025-06-23 01:19:00 +00:00
CORDA-2694: Prevent Node Explorer from crashing should it receive unknown transaction objects. (#4842)
* CORDA-2694: Prevent Node Explorer from crashing should it receive unknown transaction objects. Also ensure that LazyMappedList can only handle TransactionDeserialisationExceptions. * CORDA-2694: Add unit tests for eager LazyMappedList behaviour. * CORDA-2694: Hide LazyMappedList from the client:jfx module. * CORDA-2694: Create an unknown transaction state that has the correct notary.
This commit is contained in:
committed by
Tommy Lillehagen
parent
cfccfd075e
commit
fae74eecde
@ -1,7 +1,6 @@
|
||||
package net.corda.core.internal
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.isFulfilledBy
|
||||
import net.corda.core.crypto.keys
|
||||
import net.corda.core.internal.cordapp.CordappImpl
|
||||
import net.corda.core.utilities.loggerFor
|
||||
|
@ -524,6 +524,7 @@ fun <E> MutableSet<E>.toSynchronised(): MutableSet<E> = Collections.synchronized
|
||||
/**
|
||||
* List implementation that applies the expensive [transform] function only when the element is accessed and caches calculated values.
|
||||
* Size is very cheap as it doesn't call [transform].
|
||||
* Used internally by [net.corda.core.transactions.TraversableTransaction].
|
||||
*/
|
||||
class LazyMappedList<T, U>(val originalList: List<T>, val transform: (T, Int) -> U) : AbstractList<U>() {
|
||||
private val partialResolvedList = MutableList<U?>(originalList.size) { null }
|
||||
@ -532,6 +533,15 @@ class LazyMappedList<T, U>(val originalList: List<T>, val transform: (T, Int) ->
|
||||
return partialResolvedList[index]
|
||||
?: transform(originalList[index], index).also { computed -> partialResolvedList[index] = computed }
|
||||
}
|
||||
internal fun eager(onError: (TransactionDeserialisationException, Int) -> U?) {
|
||||
for (i in 0 until size) {
|
||||
try {
|
||||
get(i)
|
||||
} catch (ex: TransactionDeserialisationException) {
|
||||
partialResolvedList[i] = onError(ex, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -540,6 +550,17 @@ class LazyMappedList<T, U>(val originalList: List<T>, val transform: (T, Int) ->
|
||||
*/
|
||||
fun <T, U> List<T>.lazyMapped(transform: (T, Int) -> U): List<U> = LazyMappedList(this, transform)
|
||||
|
||||
/**
|
||||
* Iterate over a [LazyMappedList], forcing it to transform all of its elements immediately.
|
||||
* This transformation is assumed to be "deserialisation". Does nothing for any other kind of [List].
|
||||
* WARNING: Any changes made to the [LazyMappedList] contents are PERMANENT!
|
||||
*/
|
||||
fun <T> List<T>.eagerDeserialise(onError: (TransactionDeserialisationException, Int) -> T? = { ex, _ -> throw ex }) {
|
||||
if (this is LazyMappedList<*, T>) {
|
||||
eager(onError)
|
||||
}
|
||||
}
|
||||
|
||||
private const val MAX_SIZE = 100
|
||||
private val warnings = Collections.newSetFromMap(createSimpleCache<String, Boolean>(MAX_SIZE)).toSynchronised()
|
||||
|
||||
|
@ -9,7 +9,6 @@ import net.corda.core.contracts.ComponentGroupEnum.OUTPUTS_GROUP
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
|
@ -4,15 +4,12 @@ package net.corda.core.utilities
|
||||
|
||||
import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.internal.LazyMappedList
|
||||
import net.corda.core.internal.concurrent.get
|
||||
import net.corda.core.internal.createSimpleCache
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.Future
|
||||
import kotlin.reflect.KProperty
|
||||
|
@ -1,11 +1,20 @@
|
||||
package net.corda.core.utilities
|
||||
|
||||
import net.corda.core.contracts.ComponentGroupEnum.*
|
||||
import net.corda.core.internal.lazyMapped
|
||||
import net.corda.core.internal.TransactionDeserialisationException
|
||||
import net.corda.core.internal.eagerDeserialise
|
||||
import net.corda.core.serialization.MissingAttachmentsException
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.ExpectedException
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class LazyMappedListTest {
|
||||
|
||||
@get:Rule
|
||||
val exception: ExpectedException = ExpectedException.none()
|
||||
|
||||
@Test
|
||||
fun `LazyMappedList works`() {
|
||||
val originalList = (1 until 10).toList()
|
||||
@ -33,4 +42,29 @@ class LazyMappedListTest {
|
||||
assertEquals(1, callCounter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMissingAttachments() {
|
||||
exception.expect(MissingAttachmentsException::class.java)
|
||||
exception.expectMessage("Uncatchable!")
|
||||
|
||||
val lazyList = (0 until 5).toList().lazyMapped<Int, Int> { _, _ ->
|
||||
throw MissingAttachmentsException(emptyList(), "Uncatchable!")
|
||||
}
|
||||
|
||||
lazyList.eagerDeserialise { _, _ -> -999 }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeserialisationExceptions() {
|
||||
val lazyList = (0 until 5).toList().lazyMapped<Int, Int> { _, index ->
|
||||
throw TransactionDeserialisationException(
|
||||
OUTPUTS_GROUP, index, IllegalStateException("Catch this!"))
|
||||
}
|
||||
|
||||
lazyList.eagerDeserialise { _, _ -> -999 }
|
||||
assertEquals(5, lazyList.size)
|
||||
lazyList.forEachIndexed { idx, item ->
|
||||
assertEquals(-999, item, "Item[$idx] mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user