diff --git a/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt b/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt index becd7e9d29..4747c9aece 100644 --- a/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt +++ b/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt @@ -3,6 +3,7 @@ package net.corda.core.node import net.corda.core.identity.Party import net.corda.core.node.services.AttachmentId import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.DeprecatedConstructorForDeserialization import net.corda.core.utilities.days import java.time.Duration import java.time.Instant @@ -23,7 +24,7 @@ import java.time.Instant * during this period */ @CordaSerializable -data class NetworkParameters @JvmOverloads constructor( +data class NetworkParameters( val minimumPlatformVersion: Int, val notaries: List, val maxMessageSize: Int, @@ -31,8 +32,26 @@ data class NetworkParameters @JvmOverloads constructor( val modifiedTime: Instant, val epoch: Int, val whitelistedContractImplementations: Map>, - val eventHorizon: Duration = Int.MAX_VALUE.days + val eventHorizon: Duration ) { + @DeprecatedConstructorForDeserialization(1) + constructor (minimumPlatformVersion: Int, + notaries: List, + maxMessageSize: Int, + maxTransactionSize: Int, + modifiedTime: Instant, + epoch: Int, + whitelistedContractImplementations: Map> + ) : this(minimumPlatformVersion, + notaries, + maxMessageSize, + maxTransactionSize, + modifiedTime, + epoch, + whitelistedContractImplementations, + Int.MAX_VALUE.days + ) + init { require(minimumPlatformVersion > 0) { "minimumPlatformVersion must be at least 1" } require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" } diff --git a/docs/source/serialization-default-evolution.rst b/docs/source/serialization-default-evolution.rst index 282ca452b8..1f4923a357 100644 --- a/docs/source/serialization-default-evolution.rst +++ b/docs/source/serialization-default-evolution.rst @@ -64,7 +64,7 @@ above. A sensible default for the missing value is provided for instantiation of order, see the discussion below. As before, instances of the class at version A will be able to deserialize serialized forms of example B as it -will simply treat them as if the property has been removed (as from its perspective, they will have been.) +will simply treat them as if the property has been removed (as from its perspective, they will have been). Constructor Versioning @@ -144,7 +144,6 @@ be: // The third alteration, and how it currently exists, property e added data class Example3 (val a: Int, val b: Int, val c: Int, val d: Int, val: Int e) { - // NOTE: version number purposefully omitted from annotation for demonstration purposes @DeprecatedConstructorForDeserialization(1) constructor (a: Int, b: Int) : this(a, b, -1, -1, -1) // alt constructor 1 @DeprecatedConstructorForDeserialization(2) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 755913f54b..759fcade4a 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -283,8 +283,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration, val identityService = makeIdentityService(identity.certificate) networkMapClient = configuration.networkServices?.let { NetworkMapClient(it.networkMapURL, identityService.trustRoot) } - - val networkParameters = NetworkParametersReader(identityService.trustRoot, networkMapClient, configuration.baseDirectory).networkParameters + val networkParameteresReader = NetworkParametersReader(identityService.trustRoot, networkMapClient, configuration.baseDirectory) + val networkParameters = networkParameteresReader.networkParameters check(networkParameters.minimumPlatformVersion <= versionInfo.platformVersion) { "Node's platform version is lower than network's required minimumPlatformVersion" } @@ -354,7 +354,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, networkMapUpdater = NetworkMapUpdater(services.networkMapCache, NodeInfoWatcher(configuration.baseDirectory, getRxIoScheduler(), Duration.ofMillis(configuration.additionalNodeInfoPollingFrequencyMsec)), networkMapClient, - networkParameters.serialize().hash, + networkParameteresReader.hash, services.myInfo.serialize().hash, configuration.baseDirectory, configuration.extraNetworkMapKeys) diff --git a/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt b/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt index fb565e149e..8b1a7001d6 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt @@ -21,11 +21,14 @@ class NetworkParametersReader(private val trustRoot: X509Certificate, private val logger = contextLogger() } + private data class NetworkParamsAndHash(val networkParameters: NetworkParameters, val hash: SecureHash) private val networkParamsFile = baseDirectory / NETWORK_PARAMS_FILE_NAME private val parametersUpdateFile = baseDirectory / NETWORK_PARAMS_UPDATE_FILE_NAME - val networkParameters by lazy { retrieveNetworkParameters() } + private val netParamsAndHash by lazy { retrieveNetworkParameters() } + val networkParameters get() = netParamsAndHash.networkParameters + val hash get() = netParamsAndHash.hash - private fun retrieveNetworkParameters(): NetworkParameters { + private fun retrieveNetworkParameters(): NetworkParamsAndHash { val advertisedParametersHash = try { networkMapClient?.getNetworkMap()?.payload?.networkParameterHash } catch (e: Exception) { @@ -43,17 +46,17 @@ class NetworkParametersReader(private val trustRoot: X509Certificate, // on the other we have parameters update process - it needs to be unified. Say you start the node, you don't have matching parameters, // you get them from network map, but you have to run the approval step. if (signedParametersFromFile == null) { // Node joins for the first time. - downloadParameters(trustRoot, advertisedParametersHash) + downloadParameters(advertisedParametersHash) } else if (signedParametersFromFile.raw.hash == advertisedParametersHash) { // Restarted with the same parameters. - signedParametersFromFile.verifiedNetworkMapCert(trustRoot) + signedParametersFromFile } else { // Update case. - readParametersUpdate(advertisedParametersHash, signedParametersFromFile.raw.hash).verifiedNetworkMapCert(trustRoot) + readParametersUpdate(advertisedParametersHash, signedParametersFromFile.raw.hash) } } else { // No compatibility zone configured. Node should proceed with parameters from file. - signedParametersFromFile?.verifiedNetworkMapCert(trustRoot) ?: throw IllegalArgumentException("Couldn't find network parameters file and compatibility zone wasn't configured/isn't reachable") + signedParametersFromFile ?: throw IllegalArgumentException("Couldn't find network parameters file and compatibility zone wasn't configured/isn't reachable") } logger.info("Loaded network parameters: $parameters") - return parameters + return NetworkParamsAndHash(parameters.verifiedNetworkMapCert(trustRoot), parameters.raw.hash) } private fun readParametersUpdate(advertisedParametersHash: SecureHash, previousParametersHash: SecureHash): SignedNetworkParameters { @@ -74,14 +77,13 @@ class NetworkParametersReader(private val trustRoot: X509Certificate, } // Used only when node joins for the first time. - private fun downloadParameters(trustRoot: X509Certificate, parametersHash: SecureHash): NetworkParameters { + private fun downloadParameters(parametersHash: SecureHash): SignedNetworkParameters { logger.info("No network-parameters file found. Expecting network parameters to be available from the network map.") val networkMapClient = checkNotNull(networkMapClient) { "Node hasn't been configured to connect to a network map from which to get the network parameters" } val signedParams = networkMapClient.getNetworkParameters(parametersHash) - val verifiedParams = signedParams.verifiedNetworkMapCert(trustRoot) signedParams.serialize().open().copyTo(baseDirectory / NETWORK_PARAMS_FILE_NAME) - return verifiedParams + return signedParams } } diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt index ce0bb7c4dd..2541a527ba 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt @@ -181,7 +181,7 @@ The node will shutdown now.""") val (update, signedNewNetParams) = requireNotNull(newNetworkParameters) { "Couldn't find parameters update for the hash: $parametersHash" } // We should check that we sign the right data structure hash. val newNetParams = signedNewNetParams.verifiedNetworkMapCert(networkMapClient.trustedRoot) - val newParametersHash = newNetParams.serialize().hash + val newParametersHash = signedNewNetParams.raw.hash if (parametersHash == newParametersHash) { // The latest parameters have priority. signedNewNetParams.serialize() diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt index 975b2e077d..cfd1e6825c 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt @@ -6,6 +6,8 @@ import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.exists import net.corda.core.internal.readObject +import net.corda.core.serialization.deserialize +import net.corda.core.utilities.days import net.corda.core.utilities.seconds import net.corda.node.internal.NetworkParametersReader import net.corda.nodeapi.internal.network.* @@ -23,6 +25,7 @@ import java.net.URL import java.nio.file.FileSystem import kotlin.test.assertEquals import kotlin.test.assertFalse +import kotlin.test.assertNotNull class NetworkParametersReaderTest { @Rule @@ -73,4 +76,14 @@ class NetworkParametersReaderTest { val parameters = NetworkParametersReader(DEV_ROOT_CA.certificate, networkMapClient, baseDirectory).networkParameters assertThat(parameters).isEqualTo(fileParameters) } + + @Test + fun `serialized parameters compatibility`() { + // Network parameters file from before eventHorizon extension + val inputStream = javaClass.classLoader.getResourceAsStream("network-compatibility/network-parameters") + assertNotNull(inputStream) + val inByteArray: ByteArray = inputStream.readBytes() + val parameters = inByteArray.deserialize() + assertThat(parameters.verified().eventHorizon).isEqualTo(Int.MAX_VALUE.days) + } } \ No newline at end of file diff --git a/node/src/test/resources/network-compatibility/network-parameters b/node/src/test/resources/network-compatibility/network-parameters new file mode 100644 index 0000000000..7eac063459 Binary files /dev/null and b/node/src/test/resources/network-compatibility/network-parameters differ diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializer.kt index 7a99b142ff..d5a71f5ac4 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializer.kt @@ -120,7 +120,7 @@ abstract class EvolutionSerializer( this.resultsIndex = it.index } ?: if (!it.value.type.isMarkedNullable) { throw NotSerializableException( - "New parameter ${it.value.name} is mandatory, should be nullable for evolution to worK") + "New parameter ${it.value.name} is mandatory, should be nullable for evolution to work") } } return EvolutionSerializerViaConstructor(new.type, factory, readersAsSerialized, constructor, constructorArgs)