mirror of
https://github.com/corda/corda.git
synced 2025-01-24 13:28:07 +00:00
CORDA-1811 - If a second identity is mistakenly created the node will not start
This commit is contained in:
parent
40e5e65365
commit
a11df32dd6
@ -24,7 +24,6 @@ import net.corda.core.node.services.*
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.*
|
||||
import net.corda.node.CordaClock
|
||||
@ -155,7 +154,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
|
||||
/** Set to non-null once [start] has been successfully called. */
|
||||
open val started get() = _started
|
||||
@Volatile private var _started: StartedNode<AbstractNode>? = null
|
||||
@Volatile
|
||||
private var _started: StartedNode<AbstractNode>? = null
|
||||
|
||||
/** The implementation of the [CordaRPCOps] interface used by this node. */
|
||||
open fun makeRPCOps(flowStarter: FlowStarter, database: CordaPersistence, smm: StateMachineManager): CordaRPCOps {
|
||||
@ -319,7 +319,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
serial = 0
|
||||
)
|
||||
|
||||
val nodeInfoFromDb = networkMapCache.getNodeByLegalName(identity.name)
|
||||
val nodeInfoFromDb = getPreviousNodeInfoIfPresent(networkMapCache, identity)
|
||||
|
||||
|
||||
val nodeInfo = if (potentialNodeInfo == nodeInfoFromDb?.copy(serial = 0)) {
|
||||
// The node info hasn't changed. We use the one from the database to preserve the serial.
|
||||
@ -349,6 +350,19 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
return Pair(keyPairs, nodeInfo)
|
||||
}
|
||||
|
||||
private fun getPreviousNodeInfoIfPresent(networkMapCache: NetworkMapCacheBaseInternal, identity: PartyAndCertificate): NodeInfo? {
|
||||
val nodeInfosFromDb = networkMapCache.getNodesByLegalName(identity.name)
|
||||
|
||||
return when (nodeInfosFromDb.size) {
|
||||
0 -> null
|
||||
1 -> nodeInfosFromDb[0]
|
||||
else -> {
|
||||
log.warn("Found more than one node registration with our legal name, this is only expected if our keypair has been regenerated")
|
||||
nodeInfosFromDb[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Publish node info on startup and start task that sends every day a heartbeat - republishes node info.
|
||||
private fun tryPublishNodeInfoAsync(signedNodeInfo: SignedNodeInfo, networkMapClient: NetworkMapClient) {
|
||||
// By default heartbeat interval should be set to 1 day, but for testing we may change it.
|
||||
@ -712,7 +726,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
}
|
||||
|
||||
private fun makeCoreNotaryService(notaryConfig: NotaryConfig, database: CordaPersistence): NotaryService {
|
||||
val notaryKey = myNotaryIdentity?.owningKey ?: throw IllegalArgumentException("No notary identity initialized when creating a notary service")
|
||||
val notaryKey = myNotaryIdentity?.owningKey
|
||||
?: throw IllegalArgumentException("No notary identity initialized when creating a notary service")
|
||||
return notaryConfig.run {
|
||||
if (raft != null) {
|
||||
val uniquenessProvider = RaftUniquenessProvider(configuration, database, services.monitoringService.metrics, raft)
|
||||
@ -846,7 +861,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
override val networkMapUpdater: NetworkMapUpdater get() = this@AbstractNode.networkMapUpdater
|
||||
override fun <T : SerializeAsToken> cordaService(type: Class<T>): T {
|
||||
require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" }
|
||||
return cordappServices.getInstance(type) ?: throw IllegalArgumentException("Corda service ${type.name} does not exist")
|
||||
return cordappServices.getInstance(type)
|
||||
?: throw IllegalArgumentException("Corda service ${type.name} does not exist")
|
||||
}
|
||||
|
||||
override fun getFlowFactory(initiatingFlowClass: Class<out FlowLogic<*>>): InitiatedFlowFactory<*>? {
|
||||
|
@ -162,7 +162,7 @@ open class PersistentNetworkMapCache(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNodesByLegalName(name: CordaX500Name): List<NodeInfo> = database.transaction { queryByLegalName(session, name) }
|
||||
override fun getNodesByLegalName(name: CordaX500Name): List<NodeInfo> = database.transaction { queryByLegalName(session, name) }.sortedByDescending { it.serial }
|
||||
|
||||
override fun getNodesByLegalIdentityKey(identityKey: PublicKey): List<NodeInfo> = nodesByKeyCache[identityKey]
|
||||
|
||||
|
@ -6,13 +6,17 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.readObject
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.schemas.NodeInfoSchemaV1
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier.Companion.NODE_INFO_FILE_NAME_PREFIX
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.internal.createNodeInfoAndSigned
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import org.junit.Rule
|
||||
@ -67,4 +71,67 @@ class NodeTest {
|
||||
assertEquals(node.generateNodeInfo(), node.generateNodeInfo()) // Node info doesn't change (including the serial)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Node can start with multiple keypairs for it's identity`() {
|
||||
val configuration = createConfig(ALICE_NAME)
|
||||
val (nodeInfo1, _) = createNodeInfoAndSigned(ALICE_NAME)
|
||||
val (nodeInfo2, _) = createNodeInfoAndSigned(ALICE_NAME)
|
||||
|
||||
|
||||
val persistentNodeInfo2 = NodeInfoSchemaV1.PersistentNodeInfo(
|
||||
id = 0,
|
||||
hash = nodeInfo2.serialize().hash.toString(),
|
||||
addresses = nodeInfo2.addresses.map { NodeInfoSchemaV1.DBHostAndPort.fromHostAndPort(it) },
|
||||
legalIdentitiesAndCerts = nodeInfo2.legalIdentitiesAndCerts.mapIndexed { idx, elem ->
|
||||
NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0)
|
||||
},
|
||||
platformVersion = nodeInfo2.platformVersion,
|
||||
serial = nodeInfo2.serial
|
||||
)
|
||||
|
||||
val persistentNodeInfo1 = NodeInfoSchemaV1.PersistentNodeInfo(
|
||||
id = 0,
|
||||
hash = nodeInfo1.serialize().hash.toString(),
|
||||
addresses = nodeInfo1.addresses.map { NodeInfoSchemaV1.DBHostAndPort.fromHostAndPort(it) },
|
||||
legalIdentitiesAndCerts = nodeInfo1.legalIdentitiesAndCerts.mapIndexed { idx, elem ->
|
||||
NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0)
|
||||
},
|
||||
platformVersion = nodeInfo1.platformVersion,
|
||||
serial = nodeInfo1.serial
|
||||
)
|
||||
|
||||
configureDatabase(configuration.dataSourceProperties, configuration.database, rigorousMock()).use {
|
||||
it.transaction {
|
||||
session.save(persistentNodeInfo1)
|
||||
}
|
||||
it.transaction {
|
||||
session.save(persistentNodeInfo2)
|
||||
}
|
||||
|
||||
val node = Node(configuration, rigorousMock<VersionInfo>().also {
|
||||
doReturn(10).whenever(it).platformVersion
|
||||
}, initialiseSerialization = false)
|
||||
|
||||
//this throws an exception with old behaviour
|
||||
node.generateNodeInfo()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createConfig(nodeName: CordaX500Name): NodeConfiguration {
|
||||
val dataSourceProperties = makeTestDataSourceProperties()
|
||||
val databaseConfig = DatabaseConfig()
|
||||
val nodeAddress = NetworkHostAndPort("0.1.2.3", 456)
|
||||
return rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn(nodeAddress).whenever(it).p2pAddress
|
||||
doReturn(nodeName).whenever(it).myLegalName
|
||||
doReturn(null).whenever(it).notary // Don't add notary identity.
|
||||
doReturn(dataSourceProperties).whenever(it).dataSourceProperties
|
||||
doReturn(databaseConfig).whenever(it).database
|
||||
doReturn(temporaryFolder.root.toPath()).whenever(it).baseDirectory
|
||||
doReturn(true).whenever(it).devMode // Needed for identity cert.
|
||||
doReturn("tsp").whenever(it).trustStorePassword
|
||||
doReturn("ksp").whenever(it).keyStorePassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user