CORDA-2554 - Bootstrapper - option to include contracts to whitelist from signed JARs (#4712)

* NetworkBoostrapper can optionally whitelist contracts from signed jars based on include_whitelist.txt file.

* refactoring, docs

* logs

* add ne parameters to the generateWhitelist method at the end

* Addressing review comments.

* CORDA-2577 disable non-downgrade rule - test fix and docs
This commit is contained in:
szymonsztuka 2019-03-04 11:01:08 +00:00 committed by josecoll
parent 5a4db990e7
commit 9da30b431f
4 changed files with 30 additions and 16 deletions

View File

@ -91,7 +91,8 @@ By default the Bootstrapper will whitelist all the contracts found in the unsign
Whitelisted contracts are checked by `Zone constraints`, while contract classes from signed JARs will be checked by `Signature constraints`.
To prevent certain contracts from unsigned JARs from being whitelisted, add their fully qualified class name in the ``exclude_whitelist.txt``.
These will instead use the more restrictive ``HashAttachmentConstraint``.
Refer to :doc:`api-contract-constraints` to understand the implication of different constraint types before adding ``exclude_whitelist.txt`` files.
To add certain contracts from signed JARs to whitelist, add their fully qualified class name in the ``include_whitelist.txt``.
Refer to :doc:`api-contract-constraints` to understand the implication of different constraint types before adding ``exclude_whitelist.txt`` or ``include_whitelist.txt`` files.
For example:

View File

@ -247,8 +247,10 @@ internal constructor(private val initSerEnv: Boolean,
println("Gathering notary identities")
val notaryInfos = gatherNotaryInfos(nodeInfoFiles, configs)
println("Generating contract implementations whitelist")
// Only add contracts to the whitelist from unsigned jars
val newWhitelist = generateWhitelist(existingNetParams, readExcludeWhitelist(directory), cordappJars.filter { !isSigned(it) }.map(contractsJarConverter))
val signedJars = cordappJars.filter { isSigned(it) } // signed JARs are excluded by default, optionally include them in order to transition states from CZ whitelist to signature constraint
val unsignedJars = cordappJars - signedJars
val newWhitelist = generateWhitelist(existingNetParams, readExcludeWhitelist(directory), unsignedJars.map(contractsJarConverter),
readIncludeWhitelist(directory), signedJars.map(contractsJarConverter))
val newNetParams = installNetworkParameters(notaryInfos, newWhitelist, existingNetParams, nodeDirs, networkParametersOverrides)
if (newNetParams != existingNetParams) {
println("${if (existingNetParams == null) "New" else "Updated"} $newNetParams")

View File

@ -1,10 +1,7 @@
package net.corda.nodeapi.internal.network
import net.corda.core.contracts.ContractClassName
import net.corda.core.internal.div
import net.corda.core.internal.exists
import net.corda.core.internal.readAllLines
import net.corda.core.internal.toMultiMap
import net.corda.core.internal.*
import net.corda.core.node.NetworkParameters
import net.corda.core.node.services.AttachmentId
import net.corda.nodeapi.internal.ContractsJar
@ -12,15 +9,18 @@ import org.slf4j.LoggerFactory
import java.nio.file.Path
private const val EXCLUDE_WHITELIST_FILE_NAME = "exclude_whitelist.txt"
private const val INCLUDE_WHITELIST_FILE_NAME = "include_whitelist.txt"
private val logger = LoggerFactory.getLogger("net.corda.nodeapi.internal.network.WhitelistGenerator")
fun generateWhitelist(networkParameters: NetworkParameters?,
excludeContracts: List<ContractClassName>,
cordappJars: List<ContractsJar>): Map<ContractClassName, List<AttachmentId>> {
cordappJars: List<ContractsJar>,
includeContracts: List<ContractClassName>,
optionalCordappJars: List<ContractsJar>): Map<ContractClassName, List<AttachmentId>> {
val existingWhitelist = networkParameters?.whitelistedContractImplementations ?: emptyMap()
if (excludeContracts.isNotEmpty()) {
logger.info("Exclude contracts from whitelist: ${excludeContracts.joinToString()}")
logger.info("Exclude contracts from $EXCLUDE_WHITELIST_FILE_NAME: ${excludeContracts.joinToString()}")
existingWhitelist.keys.forEach {
require(it !in excludeContracts) { "$it is already part of the existing whitelist and cannot be excluded." }
}
@ -30,14 +30,23 @@ fun generateWhitelist(networkParameters: NetworkParameters?,
.flatMap { jar -> (jar.scan() - excludeContracts).map { it to jar.hash } }
.toMultiMap()
return (newWhiteList.keys + existingWhitelist.keys).associateBy({ it }) {
if (includeContracts.isNotEmpty())
logger.info("Include contracts from $INCLUDE_WHITELIST_FILE_NAME: ${includeContracts.joinToString()} present in JARs: $optionalCordappJars.")
val newSignedJarsWhiteList = optionalCordappJars
.flatMap { jar -> (jar.scan()).filter { includeContracts.contains(it) }.map { it to jar.hash } }
.toMultiMap()
return (newWhiteList.keys + existingWhitelist.keys + newSignedJarsWhiteList.keys).associateBy({ it }) {
val existingHashes = existingWhitelist[it] ?: emptyList()
val newHashes = newWhiteList[it] ?: emptyList()
(existingHashes + newHashes).distinct()
val newHashesFormSignedJar = newSignedJarsWhiteList[it] ?: emptyList()
(existingHashes + newHashes + newHashesFormSignedJar).distinct()
}
}
fun readExcludeWhitelist(directory: Path): List<String> {
val file = directory / EXCLUDE_WHITELIST_FILE_NAME
return if (file.exists()) file.readAllLines().map(String::trim) else emptyList()
}
fun readExcludeWhitelist(directory: Path): List<String> = readAllLines(directory / EXCLUDE_WHITELIST_FILE_NAME)
fun readIncludeWhitelist(directory: Path): List<String> = readAllLines(directory / INCLUDE_WHITELIST_FILE_NAME)
private fun readAllLines(path: Path) : List<String> = if (path.exists()) path.readAllLines().map(String::trim) else emptyList()

View File

@ -129,7 +129,9 @@ class WhitelistGeneratorTest {
return generateWhitelist(
testNetworkParameters(whitelistedContractImplementations = existingWhitelist),
excludeContracts,
contractJars
contractJars,
emptyList(),
emptyList()
)
}
}