CORDA-2128: Moved all the helper methods added to NetworkParameters.kt into internal (#4466)

This commit is contained in:
Shams Asari 2018-12-27 14:21:37 +00:00 committed by GitHub
parent 0388872175
commit c08f65a92c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 144 additions and 145 deletions

View File

@ -1,12 +1,14 @@
package net.corda.core.internal package net.corda.core.internal
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
import net.corda.core.contracts.ContractClassName
import net.corda.core.cordapp.Cordapp import net.corda.core.cordapp.Cordapp
import net.corda.core.cordapp.CordappConfig import net.corda.core.cordapp.CordappConfig
import net.corda.core.cordapp.CordappContext import net.corda.core.cordapp.CordappContext
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.DataVendingFlow import net.corda.core.flows.DataVendingFlow
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.node.NetworkParameters
import net.corda.core.node.ServicesForResolution import net.corda.core.node.ServicesForResolution
import net.corda.core.node.ZoneVersionTooLowException import net.corda.core.node.ZoneVersionTooLowException
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -16,6 +18,7 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import org.slf4j.MDC import org.slf4j.MDC
import java.security.PublicKey
// *Internal* Corda-specific utilities. // *Internal* Corda-specific utilities.
@ -71,9 +74,7 @@ private fun isPackageValid(packageName: String): Boolean {
} }
} }
/** /** Check if a string is a legal Java package name. */
* Check if a string is a legal Java package name.
*/
fun requirePackageValid(name: String) { fun requirePackageValid(name: String) {
require(isPackageValid(name)) { "Invalid Java package name: `$name`." } require(isPackageValid(name)) { "Invalid Java package name: `$name`." }
} }
@ -85,3 +86,33 @@ fun requirePackageValid(name: String) {
*/ */
@CordaSerializable @CordaSerializable
object RetrieveAnyTransactionPayload : ArrayList<Any>() object RetrieveAnyTransactionPayload : ArrayList<Any>()
/**
* Returns true if the [fullClassName] is in a subpackage of [packageName].
* E.g.: "com.megacorp" owns "com.megacorp.tokens.MegaToken"
*
* Note: The ownership check is ignoring case to prevent people from just releasing a jar with: "com.megaCorp.megatoken" and pretend they are MegaCorp.
* By making the check case insensitive, the node will require that the jar is signed by MegaCorp, so the attack fails.
*/
private fun owns(packageName: String, fullClassName: String): Boolean = fullClassName.startsWith("$packageName.", ignoreCase = true)
/** Returns the public key of the package owner of the [contractClassName], or null if not owned. */
internal fun NetworkParameters.getPackageOwnerOf(contractClassName: ContractClassName): PublicKey? {
return packageOwnership.entries.singleOrNull { owns(it.key, contractClassName) }?.value
}
/**
* Returns the public key of the package owner if any of [contractClassNames] match, or null if not owned.
*/
internal fun NetworkParameters.getPackageOwnerOf(contractClassNames: Set<ContractClassName>): PublicKey? {
for (contractClassName in contractClassNames) {
val owner = getPackageOwnerOf(contractClassName)
if (owner != null) return owner
}
return null
}
// Make sure that packages don't overlap so that ownership is clear.
fun noPackageOverlap(packages: Collection<String>): Boolean {
return packages.all { outer -> packages.none { inner -> inner != outer && inner.startsWith("$outer.") } }
}

View File

@ -2,23 +2,17 @@ package net.corda.core.node
import net.corda.core.CordaRuntimeException import net.corda.core.CordaRuntimeException
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.core.contracts.ContractClassName
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.noPackageOverlap
import net.corda.core.internal.requirePackageValid import net.corda.core.internal.requirePackageValid
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.DeprecatedConstructorForDeserialization import net.corda.core.serialization.DeprecatedConstructorForDeserialization
import net.corda.core.utilities.days import net.corda.core.utilities.days
import java.lang.reflect.Method
import java.security.PublicKey import java.security.PublicKey
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import kotlin.reflect.KProperty1
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.jvm.javaGetter
// DOCSTART 1 // DOCSTART 1
/** /**
@ -54,13 +48,13 @@ data class NetworkParameters(
) { ) {
// DOCEND 1 // DOCEND 1
@DeprecatedConstructorForDeserialization(1) @DeprecatedConstructorForDeserialization(1)
constructor (minimumPlatformVersion: Int, constructor(minimumPlatformVersion: Int,
notaries: List<NotaryInfo>, notaries: List<NotaryInfo>,
maxMessageSize: Int, maxMessageSize: Int,
maxTransactionSize: Int, maxTransactionSize: Int,
modifiedTime: Instant, modifiedTime: Instant,
epoch: Int, epoch: Int,
whitelistedContractImplementations: Map<String, List<AttachmentId>> whitelistedContractImplementations: Map<String, List<AttachmentId>>
) : this(minimumPlatformVersion, ) : this(minimumPlatformVersion,
notaries, notaries,
maxMessageSize, maxMessageSize,
@ -73,14 +67,14 @@ data class NetworkParameters(
) )
@DeprecatedConstructorForDeserialization(2) @DeprecatedConstructorForDeserialization(2)
constructor (minimumPlatformVersion: Int, constructor(minimumPlatformVersion: Int,
notaries: List<NotaryInfo>, notaries: List<NotaryInfo>,
maxMessageSize: Int, maxMessageSize: Int,
maxTransactionSize: Int, maxTransactionSize: Int,
modifiedTime: Instant, modifiedTime: Instant,
epoch: Int, epoch: Int,
whitelistedContractImplementations: Map<String, List<AttachmentId>>, whitelistedContractImplementations: Map<String, List<AttachmentId>>,
eventHorizon: Duration eventHorizon: Duration
) : this(minimumPlatformVersion, ) : this(minimumPlatformVersion,
notaries, notaries,
maxMessageSize, maxMessageSize,
@ -92,32 +86,6 @@ data class NetworkParameters(
emptyMap() emptyMap()
) )
companion object {
private val memberPropertyPartition = NetworkParameters::class.declaredMemberProperties.asSequence()
.partition { it.isAutoAcceptable() }
private val autoAcceptableNamesAndGetters = memberPropertyPartition.first.associateBy({ it.name }, { it.javaGetter })
private val nonAutoAcceptableGetters = memberPropertyPartition.second.map { it.javaGetter }
val autoAcceptablePropertyNames = autoAcceptableNamesAndGetters.keys
/**
* Returns true if the [fullClassName] is in a subpackage of [packageName].
* E.g.: "com.megacorp" owns "com.megacorp.tokens.MegaToken"
*
* Note: The ownership check is ignoring case to prevent people from just releasing a jar with: "com.megaCorp.megatoken" and pretend they are MegaCorp.
* By making the check case insensitive, the node will require that the jar is signed by MegaCorp, so the attack fails.
*/
private fun owns(packageName: String, fullClassName: String) = fullClassName.startsWith("$packageName.", ignoreCase = true)
// Make sure that packages don't overlap so that ownership is clear.
fun noOverlap(packages: Collection<String>) = packages.all { currentPackage ->
packages.none { otherPackage -> otherPackage != currentPackage && otherPackage.startsWith("$currentPackage.") }
}
private fun KProperty1<out NetworkParameters, Any?>.isAutoAcceptable(): Boolean {
return this.findAnnotation<AutoAcceptable>() != null
}
}
init { init {
require(minimumPlatformVersion > 0) { "Minimum platform level must be at least 1" } require(minimumPlatformVersion > 0) { "Minimum platform level must be at least 1" }
require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" } require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" }
@ -126,7 +94,7 @@ data class NetworkParameters(
require(maxTransactionSize > 0) { "Maximum transaction size must be at least 1" } require(maxTransactionSize > 0) { "Maximum transaction size must be at least 1" }
require(!eventHorizon.isNegative) { "Event Horizon must be a positive value" } require(!eventHorizon.isNegative) { "Event Horizon must be a positive value" }
packageOwnership.keys.forEach(::requirePackageValid) packageOwnership.keys.forEach(::requirePackageValid)
require(noOverlap(packageOwnership.keys)) { "Multiple packages added to the packageOwnership overlap." } require(noPackageOverlap(packageOwnership.keys)) { "Multiple packages added to the packageOwnership overlap." }
} }
fun copy(minimumPlatformVersion: Int, fun copy(minimumPlatformVersion: Int,
@ -137,14 +105,16 @@ data class NetworkParameters(
epoch: Int, epoch: Int,
whitelistedContractImplementations: Map<String, List<AttachmentId>> whitelistedContractImplementations: Map<String, List<AttachmentId>>
): NetworkParameters { ): NetworkParameters {
return copy(minimumPlatformVersion = minimumPlatformVersion, return copy(
minimumPlatformVersion = minimumPlatformVersion,
notaries = notaries, notaries = notaries,
maxMessageSize = maxMessageSize, maxMessageSize = maxMessageSize,
maxTransactionSize = maxTransactionSize, maxTransactionSize = maxTransactionSize,
modifiedTime = modifiedTime, modifiedTime = modifiedTime,
epoch = epoch, epoch = epoch,
whitelistedContractImplementations = whitelistedContractImplementations, whitelistedContractImplementations = whitelistedContractImplementations,
eventHorizon = eventHorizon) eventHorizon = eventHorizon
)
} }
fun copy(minimumPlatformVersion: Int, fun copy(minimumPlatformVersion: Int,
@ -156,14 +126,16 @@ data class NetworkParameters(
whitelistedContractImplementations: Map<String, List<AttachmentId>>, whitelistedContractImplementations: Map<String, List<AttachmentId>>,
eventHorizon: Duration eventHorizon: Duration
): NetworkParameters { ): NetworkParameters {
return copy(minimumPlatformVersion = minimumPlatformVersion, return copy(
minimumPlatformVersion = minimumPlatformVersion,
notaries = notaries, notaries = notaries,
maxMessageSize = maxMessageSize, maxMessageSize = maxMessageSize,
maxTransactionSize = maxTransactionSize, maxTransactionSize = maxTransactionSize,
modifiedTime = modifiedTime, modifiedTime = modifiedTime,
epoch = epoch, epoch = epoch,
whitelistedContractImplementations = whitelistedContractImplementations, whitelistedContractImplementations = whitelistedContractImplementations,
eventHorizon = eventHorizon) eventHorizon = eventHorizon
)
} }
override fun toString(): String { override fun toString(): String {
@ -176,43 +148,13 @@ data class NetworkParameters(
${whitelistedContractImplementations.entries.joinToString("\n ")} ${whitelistedContractImplementations.entries.joinToString("\n ")}
} }
eventHorizon=$eventHorizon eventHorizon=$eventHorizon
modifiedTime=$modifiedTime packageOwnership {
epoch=$epoch,
packageOwnership= {
${packageOwnership.entries.joinToString("\n ") { "$it.key -> ${it.value.toStringShort()}" }} ${packageOwnership.entries.joinToString("\n ") { "$it.key -> ${it.value.toStringShort()}" }}
} }
modifiedTime=$modifiedTime
epoch=$epoch
}""" }"""
} }
/**
* Returns the public key of the package owner of the [contractClassName], or null if not owned.
*/
@VisibleForTesting
internal fun getPackageOwnerOf(contractClassName: ContractClassName): PublicKey? = this.packageOwnership.filterKeys { packageName -> owns(packageName, contractClassName) }.values.singleOrNull()
/**
* Returns the public key of the package owner if any of [contractClassName] match, or null if not owned.
*/
@VisibleForTesting
internal fun getPackageOwnerOf(contractClassNames: Set<ContractClassName>): PublicKey? {
val ownerKeys = contractClassNames.map { getPackageOwnerOf(it) }
return ownerKeys.find { it != null }
}
/**
* Returns true if the only properties changed in [newNetworkParameters] are [AutoAcceptable] and not
* included in the [excludedParameterNames]
*/
fun canAutoAccept(newNetworkParameters: NetworkParameters, excludedParameterNames: Set<String>): Boolean {
return nonAutoAcceptableGetters.none { valueChanged(newNetworkParameters, it) } &&
autoAcceptableNamesAndGetters.none { excludedParameterNames.contains(it.key) && valueChanged(newNetworkParameters, it.value) }
}
private fun valueChanged(newNetworkParameters: NetworkParameters, getter: Method?): Boolean {
val propertyValue = getter?.invoke(this)
val newPropertyValue = getter?.invoke(newNetworkParameters)
return propertyValue != newPropertyValue
}
} }
/** /**
@ -229,7 +171,3 @@ data class NotaryInfo(val identity: Party, val validating: Boolean)
* version. * version.
*/ */
class ZoneVersionTooLowException(message: String) : CordaRuntimeException(message) class ZoneVersionTooLowException(message: String) : CordaRuntimeException(message)
private fun KProperty1<out NetworkParameters, Any?>.isAutoAcceptable(): Boolean {
return this.findAnnotation<AutoAcceptable>() != null
}

View File

@ -1,7 +1,7 @@
package net.corda.core.node package net.corda.core.node
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.node.services.AttachmentId import net.corda.core.internal.getPackageOwnerOf
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.days import net.corda.core.utilities.days
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
@ -32,7 +32,8 @@ import kotlin.test.assertFails
class NetworkParametersTest { class NetworkParametersTest {
private val mockNet = InternalMockNetwork( private val mockNet = InternalMockNetwork(
defaultParameters = MockNetworkParameters(networkSendManuallyPumped = true), defaultParameters = MockNetworkParameters(networkSendManuallyPumped = true),
notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME))) notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME))
)
@After @After
fun tearDown() { fun tearDown() {
@ -129,44 +130,8 @@ class NetworkParametersTest {
assertEquals(params.getPackageOwnerOf("com.exam.something.MyClass"), null) assertEquals(params.getPackageOwnerOf("com.exam.something.MyClass"), null)
} }
@Test
fun `auto acceptance checks are correct`() {
val packageOwnership = mapOf(
"com.example1" to generateKeyPair().public,
"com.example2" to generateKeyPair().public)
val whitelistedContractImplementations = mapOf(
"example1" to listOf(AttachmentId.randomSHA256()),
"example2" to listOf(AttachmentId.randomSHA256()))
val netParams = testNetworkParameters()
val netParamsAutoAcceptable = netParams.copy(
packageOwnership = packageOwnership,
whitelistedContractImplementations = whitelistedContractImplementations)
val netParamsNotAutoAcceptable = netParamsAutoAcceptable.copy(
maxMessageSize = netParams.maxMessageSize + 1)
assert(netParams.canAutoAccept(netParams, emptySet())) {
"auto-acceptable if identical"
}
assert(netParams.canAutoAccept(netParams, NetworkParameters.autoAcceptablePropertyNames)) {
"auto acceptable if identical regardless of exclusions"
}
assert(netParams.canAutoAccept(netParamsAutoAcceptable, emptySet())) {
"auto-acceptable if only AutoAcceptable params have changed"
}
assert(netParams.canAutoAccept(netParamsAutoAcceptable, setOf("modifiedTime"))) {
"auto-acceptable if only AutoAcceptable params have changed and excluded param has not changed"
}
assert(!netParams.canAutoAccept(netParamsNotAutoAcceptable, emptySet())) {
"not auto-acceptable if non-AutoAcceptable param has changed"
}
assert(!netParams.canAutoAccept(netParamsAutoAcceptable, setOf("whitelistedContractImplementations"))) {
"not auto-acceptable if only AutoAcceptable params have changed but one has been added to the exclusion set"
}
}
// Helpers // Helpers
private fun dropParametersToDir(dir: Path, params: NetworkParameters) { private fun dropParametersToDir(dir: Path, params: NetworkParameters) {
NetworkParametersCopier(params).install(dir) NetworkParametersCopier(params).install(dir)
} }
} }

View File

@ -7,6 +7,7 @@ import net.corda.core.crypto.SignedData
import net.corda.core.internal.* import net.corda.core.internal.*
import net.corda.core.messaging.DataFeed import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.ParametersUpdateInfo import net.corda.core.messaging.ParametersUpdateInfo
import net.corda.core.node.AutoAcceptable
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
@ -21,6 +22,7 @@ import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.network.* import net.corda.nodeapi.internal.network.*
import rx.Subscription import rx.Subscription
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import java.lang.reflect.Method
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
@ -28,6 +30,10 @@ import java.time.Duration
import java.util.* import java.util.*
import java.util.concurrent.ScheduledThreadPoolExecutor import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.reflect.KProperty1
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.jvm.javaGetter
import kotlin.system.exitProcess import kotlin.system.exitProcess
class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
@ -78,7 +84,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
this.autoAcceptNetworkParameters = networkParameterAcceptanceSettings.autoAcceptEnabled this.autoAcceptNetworkParameters = networkParameterAcceptanceSettings.autoAcceptEnabled
this.excludedAutoAcceptNetworkParameters = networkParameterAcceptanceSettings.excludedAutoAcceptableParameters this.excludedAutoAcceptNetworkParameters = networkParameterAcceptanceSettings.excludedAutoAcceptableParameters
val autoAcceptNetworkParametersNames = NetworkParameters.autoAcceptablePropertyNames - excludedAutoAcceptNetworkParameters val autoAcceptNetworkParametersNames = autoAcceptablePropertyNames - excludedAutoAcceptNetworkParameters
if (autoAcceptNetworkParameters && autoAcceptNetworkParametersNames.isNotEmpty()) { if (autoAcceptNetworkParameters && autoAcceptNetworkParametersNames.isNotEmpty()) {
logger.info("Auto-accept enabled for network parameter changes which modify only: $autoAcceptNetworkParametersNames") logger.info("Auto-accept enabled for network parameter changes which modify only: $autoAcceptNetworkParametersNames")
} }
@ -250,3 +256,27 @@ The node will shutdown now.""")
} }
} }
} }
private val memberPropertyPartition = NetworkParameters::class.declaredMemberProperties.partition { it.isAutoAcceptable() }
private val autoAcceptableNamesAndGetters = memberPropertyPartition.first.associateBy({ it.name }, { it.javaGetter })
@VisibleForTesting
internal val autoAcceptablePropertyNames = autoAcceptableNamesAndGetters.keys
private val nonAutoAcceptableGetters = memberPropertyPartition.second.map { it.javaGetter }
/**
* Returns true if the only properties changed in [newNetworkParameters] are [AutoAcceptable] and not
* included in the [excludedParameterNames]
*/
@VisibleForTesting
internal fun NetworkParameters.canAutoAccept(newNetworkParameters: NetworkParameters, excludedParameterNames: Set<String>): Boolean {
return nonAutoAcceptableGetters.none { valueChanged(newNetworkParameters, it) } &&
autoAcceptableNamesAndGetters.none { it.key in excludedParameterNames && valueChanged(newNetworkParameters, it.value) }
}
private fun KProperty1<out NetworkParameters, Any?>.isAutoAcceptable(): Boolean = findAnnotation<AutoAcceptable>() != null
private fun NetworkParameters.valueChanged(newNetworkParameters: NetworkParameters, getter: Method?): Boolean {
val propertyValue = getter?.invoke(this)
val newPropertyValue = getter?.invoke(newNetworkParameters)
return propertyValue != newPropertyValue
}

View File

@ -5,6 +5,7 @@ import com.google.common.jimfs.Jimfs
import com.nhaarman.mockito_kotlin.* import com.nhaarman.mockito_kotlin.*
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.sign import net.corda.core.crypto.sign
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -13,17 +14,20 @@ import net.corda.core.internal.concurrent.openFuture
import net.corda.core.messaging.ParametersUpdateInfo import net.corda.core.messaging.ParametersUpdateInfo
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.AttachmentId
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.millis import net.corda.core.utilities.millis
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.internal.NetworkParametersStorageInternal
import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.node.services.config.NetworkParameterAcceptanceSettings import net.corda.node.services.config.NetworkParameterAcceptanceSettings
import net.corda.core.internal.NODE_INFO_DIRECTORY
import net.corda.node.internal.NetworkParametersStorageInternal
import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.network.* import net.corda.nodeapi.internal.network.NETWORK_PARAMS_UPDATE_FILE_NAME
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
import net.corda.nodeapi.internal.network.SignedNetworkParameters
import net.corda.nodeapi.internal.network.verifiedNetworkParametersCert
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.* import net.corda.testing.core.*
import net.corda.testing.internal.DEV_ROOT_CA import net.corda.testing.internal.DEV_ROOT_CA
@ -48,6 +52,8 @@ import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class NetworkMapUpdaterTest { class NetworkMapUpdaterTest {
@Rule @Rule
@ -68,7 +74,7 @@ class NetworkMapUpdaterTest {
private val networkParametersStorage: NetworkParametersStorageInternal = mock() private val networkParametersStorage: NetworkParametersStorageInternal = mock()
private lateinit var server: NetworkMapServer private lateinit var server: NetworkMapServer
private lateinit var networkMapClient: NetworkMapClient private lateinit var networkMapClient: NetworkMapClient
private lateinit var updater: NetworkMapUpdater private var updater: NetworkMapUpdater? = null
@Before @Before
fun setUp() { fun setUp() {
@ -82,7 +88,7 @@ class NetworkMapUpdaterTest {
@After @After
fun cleanUp() { fun cleanUp() {
updater.close() updater?.close()
fs.close() fs.close()
server.close() server.close()
} }
@ -95,7 +101,7 @@ class NetworkMapUpdaterTest {
networkParameters: NetworkParameters = server.networkParameters, networkParameters: NetworkParameters = server.networkParameters,
autoAcceptNetworkParameters: Boolean = true, autoAcceptNetworkParameters: Boolean = true,
excludedAutoAcceptNetworkParameters: Set<String> = emptySet()) { excludedAutoAcceptNetworkParameters: Set<String> = emptySet()) {
updater.start(DEV_ROOT_CA.certificate, updater!!.start(DEV_ROOT_CA.certificate,
server.networkParameters.serialize().hash, server.networkParameters.serialize().hash,
ourNodeInfo, ourNodeInfo,
networkParameters, networkParameters,
@ -206,7 +212,7 @@ class NetworkMapUpdaterTest {
@Test @Test
fun `emit new parameters update info on parameters update from network map`() { fun `emit new parameters update info on parameters update from network map`() {
setUpdater() setUpdater()
val paramsFeed = updater.trackParametersUpdate() val paramsFeed = updater!!.trackParametersUpdate()
val snapshot = paramsFeed.snapshot val snapshot = paramsFeed.snapshot
val updates = paramsFeed.updates.bufferUntilSubscribed() val updates = paramsFeed.updates.bufferUntilSubscribed()
assertEquals(null, snapshot) assertEquals(null, snapshot)
@ -237,7 +243,7 @@ class NetworkMapUpdaterTest {
val newHash = newParameters.serialize().hash val newHash = newParameters.serialize().hash
val updateFile = baseDir / NETWORK_PARAMS_UPDATE_FILE_NAME val updateFile = baseDir / NETWORK_PARAMS_UPDATE_FILE_NAME
assert(!updateFile.exists()) { "network parameters should not be auto accepted" } assert(!updateFile.exists()) { "network parameters should not be auto accepted" }
updater.acceptNewNetworkParameters(newHash) { it.serialize().sign(ourKeyPair) } updater!!.acceptNewNetworkParameters(newHash) { it.serialize().sign(ourKeyPair) }
verify(networkParametersStorage, times(1)).saveParameters(any()) verify(networkParametersStorage, times(1)).saveParameters(any())
val signedNetworkParams = updateFile.readObject<SignedNetworkParameters>() val signedNetworkParams = updateFile.readObject<SignedNetworkParameters>()
val paramsFromFile = signedNetworkParams.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) val paramsFromFile = signedNetworkParams.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate)
@ -411,6 +417,32 @@ class NetworkMapUpdaterTest {
assert(networkMapCache.allNodeHashes.size == 1) assert(networkMapCache.allNodeHashes.size == 1)
} }
@Test
fun `auto acceptance checks are correct`() {
val packageOwnership = mapOf(
"com.example1" to generateKeyPair().public,
"com.example2" to generateKeyPair().public
)
val whitelistedContractImplementations = mapOf(
"example1" to listOf(AttachmentId.randomSHA256()),
"example2" to listOf(AttachmentId.randomSHA256())
)
val netParams = testNetworkParameters()
val netParamsAutoAcceptable = netParams.copy(
packageOwnership = packageOwnership,
whitelistedContractImplementations = whitelistedContractImplementations
)
val netParamsNotAutoAcceptable = netParamsAutoAcceptable.copy(maxMessageSize = netParams.maxMessageSize + 1)
assertTrue(netParams.canAutoAccept(netParams, emptySet()), "auto-acceptable if identical")
assertTrue(netParams.canAutoAccept(netParams, autoAcceptablePropertyNames), "auto acceptable if identical regardless of exclusions")
assertTrue(netParams.canAutoAccept(netParamsAutoAcceptable, emptySet()), "auto-acceptable if only AutoAcceptable params have changed")
assertTrue(netParams.canAutoAccept(netParamsAutoAcceptable, setOf("modifiedTime")), "auto-acceptable if only AutoAcceptable params have changed and excluded param has not changed")
assertFalse(netParams.canAutoAccept(netParamsNotAutoAcceptable, emptySet()), "not auto-acceptable if non-AutoAcceptable param has changed")
assertFalse(netParams.canAutoAccept(netParamsAutoAcceptable, setOf("whitelistedContractImplementations")), "not auto-acceptable if only AutoAcceptable params have changed but one has been added to the exclusion set")
}
private fun createMockNetworkMapCache(): NetworkMapCacheInternal { private fun createMockNetworkMapCache(): NetworkMapCacheInternal {
return mock { return mock {
on { nodeReady }.thenReturn(nodeReadyFuture) on { nodeReady }.thenReturn(nodeReadyFuture)

View File

@ -6,9 +6,8 @@ import net.corda.common.configuration.parsing.internal.get
import net.corda.common.configuration.parsing.internal.mapValid import net.corda.common.configuration.parsing.internal.mapValid
import net.corda.common.configuration.parsing.internal.nested import net.corda.common.configuration.parsing.internal.nested
import net.corda.common.validation.internal.Validated import net.corda.common.validation.internal.Validated
import net.corda.core.internal.div import net.corda.core.internal.noPackageOverlap
import net.corda.core.internal.requirePackageValid import net.corda.core.internal.requirePackageValid
import net.corda.core.node.NetworkParameters
import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.nodeapi.internal.crypto.loadKeyStore
import net.corda.nodeapi.internal.network.NetworkParametersOverrides import net.corda.nodeapi.internal.network.NetworkParametersOverrides
import net.corda.nodeapi.internal.network.PackageOwner import net.corda.nodeapi.internal.network.PackageOwner
@ -84,8 +83,12 @@ internal object NetworkParameterOverridesSpec : Configuration.Specification<Netw
override fun parseValid(configuration: Config): Valid<NetworkParametersOverrides> { override fun parseValid(configuration: Config): Valid<NetworkParametersOverrides> {
val packageOwnership = configuration[packageOwnership] val packageOwnership = configuration[packageOwnership]
if (packageOwnership != null && !NetworkParameters.noOverlap(packageOwnership.map { it.javaPackageName })) { if (packageOwnership != null && !noPackageOverlap(packageOwnership.map { it.javaPackageName })) {
return Validated.invalid(sequenceOf(Configuration.Validation.Error.BadValue.of("Package namespaces must not overlap", keyName = "packageOwnership", containingPath = listOf())).toSet()) return Validated.invalid(sequenceOf(Configuration.Validation.Error.BadValue.of(
"Package namespaces must not overlap",
keyName = "packageOwnership",
containingPath = listOf()
)).toSet())
} }
return valid(NetworkParametersOverrides( return valid(NetworkParametersOverrides(
minimumPlatformVersion = configuration[minimumPlatformVersion], minimumPlatformVersion = configuration[minimumPlatformVersion],