Rename the inbox (#2360)

Add changelog entry

Address review comments

Alternate solution to service queues

Fixup after merge
This commit is contained in:
Matthew Nesbit
2018-01-17 16:06:31 +00:00
committed by GitHub
parent d0b62db0d2
commit 6edf95506b
16 changed files with 157 additions and 63 deletions

View File

@ -3,18 +3,18 @@ package net.corda.node.amqp
import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.crypto.toStringShort
import net.corda.core.internal.div
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.toBase58String
import net.corda.node.internal.protonwrapper.netty.AMQPServer
import net.corda.node.internal.security.RPCSecurityManager
import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.node.services.config.*
import net.corda.node.services.messaging.ArtemisMessagingClient
import net.corda.node.services.messaging.ArtemisMessagingServer
import net.corda.nodeapi.internal.ArtemisMessagingComponent
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE
import net.corda.nodeapi.internal.crypto.loadKeyStore
import net.corda.testing.*
import net.corda.testing.internal.rigorousMock
@ -50,7 +50,7 @@ class AMQPBridgeTest {
@Test
fun `test acked and nacked messages`() {
// Create local queue
val sourceQueueName = "internal.peers." + BOB.publicKey.toBase58String()
val sourceQueueName = "internal.peers." + BOB.publicKey.toStringShort()
val (artemisServer, artemisClient) = createArtemis(sourceQueueName)
// Pre-populate local queue with 3 messages
@ -130,11 +130,13 @@ class AMQPBridgeTest {
@Test
fun `Test legacy bridge still works`() {
// Create local queue
val sourceQueueName = "internal.peers." + ALICE.publicKey.toBase58String()
val sourceQueueName = "internal.peers." + BOB.publicKey.toStringShort()
val (artemisLegacyServer, artemisLegacyClient) = createLegacyArtemis(sourceQueueName)
val (artemisServer, artemisClient) = createArtemis(null)
val inbox = ArtemisMessagingComponent.RemoteInboxAddress(BOB.party).queueName
artemisClient.started!!.session.createQueue(inbox, RoutingType.ANYCAST, inbox, true)
val artemis = artemisLegacyClient.started!!
for (i in 0 until 3) {
@ -147,8 +149,7 @@ class AMQPBridgeTest {
artemis.producer.send(sourceQueueName, artemisMessage)
}
val subs = artemisClient.started!!.session.createConsumer(P2P_QUEUE)
val subs = artemisClient.started!!.session.createConsumer(inbox)
for (i in 0 until 3) {
val msg = subs.receive()
val messageBody = ByteArray(msg.bodySize).apply { msg.bodyBuffer.readBytes(this) }
@ -174,9 +175,9 @@ class AMQPBridgeTest {
doReturn(true).whenever(it).useAMQPBridges
}
artemisConfig.configureWithDevSSLCertificate()
val networkMap = rigorousMock<NetworkMapCache>().also {
val networkMap = rigorousMock<NetworkMapCacheInternal>().also {
doReturn(Observable.never<NetworkMapCache.MapChange>()).whenever(it).changed
doReturn(listOf(NodeInfo(listOf(amqpAddress), listOf(BOB.identity), 1, 1L))).whenever(it).getNodesByLegalIdentityKey(any())
doReturn(listOf(NodeInfo(listOf(amqpAddress), listOf(BOB.identity), 1, 1L))).whenever(it).getNodesByOwningKeyIndex(any())
}
val userService = rigorousMock<RPCSecurityManager>()
val artemisServer = ArtemisMessagingServer(artemisConfig, artemisPort, null, networkMap, userService, MAX_MESSAGE_SIZE)
@ -186,7 +187,7 @@ class AMQPBridgeTest {
val artemis = artemisClient.started!!
if (sourceQueueName != null) {
// Local queue for outgoing messages
artemis.session.createQueue(sourceQueueName, RoutingType.MULTICAST, sourceQueueName, true)
artemis.session.createQueue(sourceQueueName, RoutingType.ANYCAST, sourceQueueName, true)
}
return Pair(artemisServer, artemisClient)
}
@ -203,9 +204,9 @@ class AMQPBridgeTest {
doReturn(ActiveMqServerConfiguration(BridgeConfiguration(0, 0, 0.0))).whenever(it).activeMQServer
}
artemisConfig.configureWithDevSSLCertificate()
val networkMap = rigorousMock<NetworkMapCache>().also {
val networkMap = rigorousMock<NetworkMapCacheInternal>().also {
doReturn(Observable.never<NetworkMapCache.MapChange>()).whenever(it).changed
doReturn(listOf(NodeInfo(listOf(artemisAddress), listOf(ALICE.identity), 1, 1L))).whenever(it).getNodesByLegalIdentityKey(any())
doReturn(listOf(NodeInfo(listOf(artemisAddress), listOf(ALICE.identity), 1, 1L))).whenever(it).getNodesByOwningKeyIndex(any())
}
val userService = rigorousMock<RPCSecurityManager>()
val artemisServer = ArtemisMessagingServer(artemisConfig, artemisPort2, null, networkMap, userService, MAX_MESSAGE_SIZE)
@ -214,7 +215,7 @@ class AMQPBridgeTest {
artemisClient.start()
val artemis = artemisClient.started!!
// Local queue for outgoing messages
artemis.session.createQueue(sourceQueueName, RoutingType.MULTICAST, sourceQueueName, true)
artemis.session.createQueue(sourceQueueName, RoutingType.ANYCAST, sourceQueueName, true)
return Pair(artemisServer, artemisClient)
}

View File

@ -13,11 +13,13 @@ import net.corda.node.internal.protonwrapper.messages.MessageStatus
import net.corda.node.internal.protonwrapper.netty.AMQPClient
import net.corda.node.internal.protonwrapper.netty.AMQPServer
import net.corda.node.internal.security.RPCSecurityManager
import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.node.services.config.CertChainPolicyConfig
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.configureWithDevSSLCertificate
import net.corda.node.services.messaging.ArtemisMessagingClient
import net.corda.node.services.messaging.ArtemisMessagingServer
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
import net.corda.nodeapi.internal.crypto.loadKeyStore
import net.corda.testing.*
@ -48,7 +50,7 @@ class ProtonWrapperTests {
amqpServer.start()
val receiveSubs = amqpServer.onReceive.subscribe {
assertEquals(BOB_NAME.toString(), it.sourceLegalName)
assertEquals("p2p.inbound", it.topic)
assertEquals(P2P_PREFIX + "Test", it.topic)
assertEquals("Test", String(it.payload))
it.complete(true)
}
@ -64,7 +66,7 @@ class ProtonWrapperTests {
assertEquals(true, clientConnect.connected)
assertEquals(ALICE_NAME, CordaX500Name.build(clientConnect.remoteCert!!.subjectX500Principal))
val msg = amqpClient.createMessage("Test".toByteArray(),
"p2p.inbound",
P2P_PREFIX + "Test",
ALICE_NAME.toString(),
emptyMap())
amqpClient.write(msg)
@ -151,8 +153,8 @@ class ProtonWrapperTests {
assertEquals(true, clientConnected.get().connected)
assertEquals(CHARLIE_NAME, CordaX500Name.build(clientConnected.get().remoteCert!!.subjectX500Principal))
val artemis = artemisClient.started!!
val sendAddress = "p2p.inbound"
artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true)
val sendAddress = P2P_PREFIX + "Test"
artemis.session.createQueue(sendAddress, RoutingType.ANYCAST, "queue", true)
val consumer = artemis.session.createConsumer("queue")
val testData = "Test".toByteArray()
val testProperty = mutableMapOf<Any?, Any?>()
@ -230,7 +232,7 @@ class ProtonWrapperTests {
}
artemisConfig.configureWithDevSSLCertificate()
val networkMap = rigorousMock<NetworkMapCache>().also {
val networkMap = rigorousMock<NetworkMapCacheInternal>().also {
doReturn(never<NetworkMapCache.MapChange>()).whenever(it).changed
}
val userService = rigorousMock<RPCSecurityManager>()

View File

@ -5,6 +5,7 @@ import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.CordaRPCConnection
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.random63BitValue
import net.corda.core.crypto.toStringShort
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession
import net.corda.core.flows.InitiatedBy
@ -14,14 +15,13 @@ import net.corda.core.identity.Party
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.toBase58String
import net.corda.core.utilities.unwrap
import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX
import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.testing.ALICE_NAME
@ -71,30 +71,30 @@ abstract class MQSecurityTest : NodeBasedTest() {
@Test
fun `consume message from P2P queue`() {
assertConsumeAttackFails(P2P_QUEUE)
assertConsumeAttackFails("$P2P_PREFIX${alice.info.chooseIdentity().owningKey.toStringShort()}")
}
@Test
fun `consume message from peer queue`() {
val bobParty = startBobAndCommunicateWithAlice()
assertConsumeAttackFails("$PEERS_PREFIX${bobParty.owningKey.toBase58String()}")
assertConsumeAttackFails("$PEERS_PREFIX${bobParty.owningKey.toStringShort()}")
}
@Test
fun `send message to address of peer which has been communicated with`() {
val bobParty = startBobAndCommunicateWithAlice()
assertSendAttackFails("$PEERS_PREFIX${bobParty.owningKey.toBase58String()}")
assertSendAttackFails("$PEERS_PREFIX${bobParty.owningKey.toStringShort()}")
}
@Test
fun `create queue for peer which has not been communicated with`() {
val bob = startNode(BOB_NAME)
assertAllQueueCreationAttacksFail("$PEERS_PREFIX${bob.info.chooseIdentity().owningKey.toBase58String()}")
assertAllQueueCreationAttacksFail("$PEERS_PREFIX${bob.info.chooseIdentity().owningKey.toStringShort()}")
}
@Test
fun `create queue for unknown peer`() {
val invalidPeerQueue = "$PEERS_PREFIX${generateKeyPair().public.toBase58String()}"
val invalidPeerQueue = "$PEERS_PREFIX${generateKeyPair().public.toStringShort()}"
assertAllQueueCreationAttacksFail(invalidPeerQueue)
}

View File

@ -40,6 +40,7 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory
import rx.Scheduler
import rx.schedulers.Schedulers
import java.security.PublicKey
import java.time.Clock
import java.util.concurrent.atomic.AtomicInteger
import javax.management.ObjectName
@ -166,11 +167,14 @@ open class Node(configuration: NodeConfiguration,
VerifierType.OutOfProcess -> VerifierMessagingClient(configuration, serverAddress, services.monitoringService.metrics, networkParameters.maxMessageSize)
VerifierType.InMemory -> null
}
require(info.legalIdentities.size in 1..2) { "Currently nodes must have a primary address and optionally one serviced address" }
val serviceIdentity: PublicKey? = if (info.legalIdentities.size == 1) null else info.legalIdentities[1].owningKey
return P2PMessagingClient(
configuration,
versionInfo,
serverAddress,
info.legalIdentities[0].owningKey,
serviceIdentity,
serverThread,
database,
advertisedAddress,

View File

@ -29,6 +29,10 @@ interface NetworkMapCacheBaseInternal : NetworkMapCacheBase {
fun getNodeByHash(nodeHash: SecureHash): NodeInfo?
/** Find nodes from the [PublicKey] toShortString representation.
* This is used for Artemis bridge lookup process. */
fun getNodesByOwningKeyIndex(identityKeyIndex: String): List<NodeInfo>
/** Adds a node to the local cache (generally only used for adding ourselves). */
fun addNode(node: NodeInfo)

View File

@ -12,8 +12,8 @@ import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.messaging.AMQPBridgeManager.AMQPBridge.Companion.getBridgeName
import net.corda.nodeapi.internal.ArtemisMessagingComponent
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
import net.corda.nodeapi.internal.ArtemisMessagingComponent.RemoteInboxAddress.Companion.translateLocalQueueToInboxAddress
import net.corda.nodeapi.internal.crypto.loadKeyStore
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
@ -132,7 +132,8 @@ internal class AMQPBridgeManager(val config: NodeConfiguration, val p2pAddress:
properties[key.toString()] = value
}
log.debug { "Bridged Send to ${legalNames.first()} uuid: ${artemisMessage.getObjectProperty("_AMQ_DUPL_ID")}" }
val sendableMessage = amqpClient.createMessage(data, P2P_QUEUE,
val peerInbox = translateLocalQueueToInboxAddress(queueName)
val sendableMessage = amqpClient.createMessage(data, peerInbox,
legalNames.first().toString(),
properties)
sendableMessage.onComplete.then {

View File

@ -8,16 +8,15 @@ import net.corda.core.internal.div
import net.corda.core.internal.noneOrSingle
import net.corda.core.internal.uncheckedCast
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.debug
import net.corda.core.utilities.parsePublicKeyBase58
import net.corda.node.internal.Node
import net.corda.node.internal.security.Password
import net.corda.node.internal.security.RPCSecurityManager
import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.messaging.NodeLoginModule.Companion.NODE_ROLE
import net.corda.node.services.messaging.NodeLoginModule.Companion.PEER_ROLE
@ -31,7 +30,7 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.ArtemisPeerAddress
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
import net.corda.nodeapi.internal.ArtemisMessagingComponent.NodeAddress
@ -94,7 +93,7 @@ import javax.security.cert.CertificateException
class ArtemisMessagingServer(private val config: NodeConfiguration,
private val p2pPort: Int,
val rpcPort: Int?,
val networkMapCache: NetworkMapCache,
val networkMapCache: NetworkMapCacheInternal,
val securityManager: RPCSecurityManager,
val maxMessageSize: Int) : SingletonSerializeAsToken() {
companion object {
@ -191,7 +190,6 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
// by having its password be an unknown securely random 128-bit value.
clusterPassword = BigInteger(128, newSecureRandom()).toString(16)
queueConfigurations = listOf(
queueConfig(P2P_QUEUE, durable = true),
// Create an RPC queue: this will service locally connected clients only (not via a bridge) and those
// clients must have authenticated. We could use a single consumer for everything and perhaps we should,
// but these queues are not worth persisting.
@ -243,7 +241,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
private fun ConfigurationImpl.configureAddressSecurity(): Pair<Configuration, LoginListener> {
val nodeInternalRole = Role(NODE_ROLE, true, true, true, true, true, true, true, true)
securityRoles["$INTERNAL_PREFIX#"] = setOf(nodeInternalRole) // Do not add any other roles here as it's only for the node
securityRoles[P2P_QUEUE] = setOf(nodeInternalRole, restrictedRole(PEER_ROLE, send = true))
securityRoles["$P2P_PREFIX#"] = setOf(nodeInternalRole, restrictedRole(PEER_ROLE, send = true))
securityRoles[RPCApi.RPC_SERVER_QUEUE_NAME] = setOf(nodeInternalRole, restrictedRole(RPC_ROLE, send = true))
// Each RPC user must have its own role and its own queue. This prevents users accessing each other's queues
// and stealing RPC responses.
@ -309,8 +307,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
if (queueName.startsWith(PEERS_PREFIX)) {
try {
val identity = parsePublicKeyBase58(queueName.substring(PEERS_PREFIX.length))
val nodeInfos = networkMapCache.getNodesByLegalIdentityKey(identity)
val nodeInfos = networkMapCache.getNodesByOwningKeyIndex(queueName.substring(PEERS_PREFIX.length))
if (nodeInfos.isNotEmpty()) {
nodeInfos.forEach { deployBridgeToPeer(it) }
} else {

View File

@ -10,15 +10,17 @@ import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.ArtemisTcpTransport
import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.internal.ArtemisMessagingComponent
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
import net.corda.nodeapi.internal.ArtemisMessagingComponent.RemoteInboxAddress.Companion.translateLocalQueueToInboxAddress
import net.corda.nodeapi.internal.crypto.X509Utilities
import org.apache.activemq.artemis.api.core.Message
import org.apache.activemq.artemis.core.config.BridgeConfiguration
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnection
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnector
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants
import org.apache.activemq.artemis.core.server.ActiveMQServer
import org.apache.activemq.artemis.core.server.cluster.Transformer
import org.apache.activemq.artemis.spi.core.remoting.*
import org.apache.activemq.artemis.utils.ConfigurationHelper
import java.time.Duration
@ -46,7 +48,7 @@ internal class CoreBridgeManager(val config: NodeConfiguration, val activeMQServ
/**
* All nodes are expected to have a public facing address called [ArtemisMessagingComponent.P2P_QUEUE] for receiving
* All nodes are expected to have a public facing address called p2p.inbound.$identity for receiving
* messages from other nodes. When we want to send a message to a node we send it to our internal address/queue for it,
* as defined by ArtemisAddress.queueName. A bridge is then created to forward messages from this queue to the node's
* P2P address.
@ -64,7 +66,6 @@ internal class CoreBridgeManager(val config: NodeConfiguration, val activeMQServ
activeMQServer.deployBridge(BridgeConfiguration().apply {
name = getBridgeName(queueName, target)
this.queueName = queueName
forwardingAddress = P2P_QUEUE
staticConnectors = listOf(target.toString())
confirmationWindowSize = 100000 // a guess
isUseDuplicateDetection = true // Enable the bridge's automatic deduplication logic
@ -77,6 +78,7 @@ internal class CoreBridgeManager(val config: NodeConfiguration, val activeMQServ
// our TLS certificate.
user = PEER_USER
password = PEER_USER
transformerClassName = InboxTopicTransformer::class.java.name
})
}
@ -99,6 +101,13 @@ internal class CoreBridgeManager(val config: NodeConfiguration, val activeMQServ
}
}
class InboxTopicTransformer : Transformer {
override fun transform(message: Message): Message {
message.address = translateLocalQueueToInboxAddress(message.address)
return message
}
}
class VerifyingNettyConnectorFactory : NettyConnectorFactory() {
override fun createConnector(configuration: MutableMap<String, Any>,
handler: BufferHandler?,

View File

@ -21,7 +21,6 @@ import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.node.utilities.PersistentMap
import net.corda.nodeapi.internal.ArtemisMessagingComponent.*
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import org.apache.activemq.artemis.api.core.ActiveMQObjectClosedException
@ -55,21 +54,28 @@ import javax.persistence.Lob
* invoke methods on the provided implementation. There is more documentation on this in the docsite and the
* CordaRPCClient class.
*
* @param serverAddress The address of the broker instance to connect to (might be running in the same process).
* @param myIdentity The public key to be used as the ArtemisMQ address and queue name for the node.
* @param nodeExecutor An executor to run received message tasks upon.
* @param advertisedAddress The node address for inbound connections, advertised to the network map service and peers.
* If not provided, will default to [serverAddress].
* @param config The configuration of the node, which is used for controlling the message redelivery options.
* @param versionInfo All messages from the node carry the version info and received messages are checked against this for compatibility.
* @param serverAddress The host and port of the Artemis broker.
* @param myIdentity The primary identity of the node, which defines the messaging address for externally received messages.
* It is also used to construct the myAddress field, which is ultimately advertised in the network map.
* @param serviceIdentity An optional second identity if the node is also part of a group address, for example a notary.
* @param nodeExecutor The received messages are marshalled onto the server executor to prevent Netty buffers leaking during fiber suspends.
* @param database The nodes database, which is used to deduplicate messages.
* @param advertisedAddress The externally advertised version of the Artemis broker address used to construct myAddress and included
* in the network map data.
* @param maxMessageSize A bound applied to the message size.
*/
@ThreadSafe
class P2PMessagingClient(config: NodeConfiguration,
private val versionInfo: VersionInfo,
serverAddress: NetworkHostAndPort,
private val myIdentity: PublicKey,
private val serviceIdentity: PublicKey?,
private val nodeExecutor: AffinityExecutor.ServiceAffinityExecutor,
private val database: CordaPersistence,
advertisedAddress: NetworkHostAndPort = serverAddress,
private val maxMessageSize: Int
maxMessageSize: Int
) : SingletonSerializeAsToken(), MessagingService {
companion object {
private val log = contextLogger()
@ -128,6 +134,7 @@ class P2PMessagingClient(config: NodeConfiguration,
private class InnerState {
var running = false
var p2pConsumer: ClientConsumer? = null
var serviceConsumer: ClientConsumer? = null
}
private val messagesToRedeliver = database.transaction {
@ -183,8 +190,23 @@ class P2PMessagingClient(config: NodeConfiguration,
fun start() {
state.locked {
val session = artemis.start().session
val inbox = RemoteInboxAddress(myIdentity).queueName
// Create a queue, consumer and producer for handling P2P network messages.
p2pConsumer = session.createConsumer(P2P_QUEUE)
createQueueIfAbsent(inbox)
p2pConsumer = session.createConsumer(inbox)
if (serviceIdentity != null) {
val serviceAddress = RemoteInboxAddress(serviceIdentity).queueName
createQueueIfAbsent(serviceAddress)
val serviceHandler = session.createConsumer(serviceAddress)
serviceHandler.setMessageHandler { msg ->
val message: ReceivedMessage? = artemisToCordaMessage(msg)
if (message != null)
deliver(message)
state.locked {
msg.individualAcknowledge()
}
}
}
}
resumeMessageRedelivery()
@ -261,7 +283,7 @@ class P2PMessagingClient(config: NodeConfiguration,
val platformVersion = message.required(platformVersionProperty) { getIntProperty(it) }
// Use the magic deduplication property built into Artemis as our message identity too
val uuid = message.required(HDR_DUPLICATE_DETECTION_ID) { UUID.fromString(message.getStringProperty(it)) }
log.trace { "Received message from: ${message.address} user: $user topic: $topic sessionID: $sessionID uuid: $uuid" }
log.info("Received message from: ${message.address} user: $user topic: $topic sessionID: $sessionID uuid: $uuid")
return ArtemisReceivedMessage(TopicSession(topic, sessionID), CordaX500Name.parse(user), platformVersion, uuid, message)
} catch (e: Exception) {
@ -346,6 +368,13 @@ class P2PMessagingClient(config: NodeConfiguration,
// Ignore it: this can happen if the server has gone away before we do.
}
p2pConsumer = null
val s = serviceConsumer
try {
s?.close()
} catch (e: ActiveMQObjectClosedException) {
// Ignore it: this can happen if the server has gone away before we do.
}
serviceConsumer = null
prevRunning
}
if (running && !nodeExecutor.isOnThread) {
@ -446,7 +475,7 @@ class P2PMessagingClient(config: NodeConfiguration,
private fun getMQAddress(target: MessageRecipients): String {
return if (target == myAddress) {
// If we are sending to ourselves then route the message directly to our P2P queue.
P2P_QUEUE
RemoteInboxAddress(myIdentity).queueName
} else {
// Otherwise we send the message to an internal queue for the target residing on our broker. It's then the
// broker's job to route the message to the target's P2P queue.
@ -463,7 +492,7 @@ class P2PMessagingClient(config: NodeConfiguration,
val queueQuery = session.queueQuery(SimpleString(queueName))
if (!queueQuery.isExists) {
log.info("Create fresh queue $queueName bound on same address")
session.createQueue(queueName, RoutingType.MULTICAST, queueName, true)
session.createQueue(queueName, RoutingType.ANYCAST, queueName, true)
}
}
}

View File

@ -7,15 +7,15 @@ import net.corda.core.transactions.LedgerTransaction
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor
import net.corda.node.services.transactions.OutOfProcessTransactionVerifierService
import net.corda.node.utilities.*
import net.corda.node.utilities.AffinityExecutor
import net.corda.nodeapi.VerifierApi
import net.corda.nodeapi.VerifierApi.VERIFICATION_REQUESTS_QUEUE_NAME
import net.corda.nodeapi.VerifierApi.VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX
import net.corda.nodeapi.internal.config.SSLConfiguration
import org.apache.activemq.artemis.api.core.RoutingType
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.*
import java.util.concurrent.*
import org.apache.activemq.artemis.api.core.client.ClientConsumer
import java.util.concurrent.TimeUnit
class VerifierMessagingClient(config: SSLConfiguration, serverAddress: NetworkHostAndPort, metrics: MetricRegistry, private val maxMessageSize: Int) : SingletonSerializeAsToken() {
companion object {
@ -40,7 +40,7 @@ class VerifierMessagingClient(config: SSLConfiguration, serverAddress: NetworkHo
val queueQuery = session.queueQuery(SimpleString(queueName))
if (!queueQuery.isExists) {
log.info("Create fresh queue $queueName bound on same address")
session.createQueue(queueName, RoutingType.MULTICAST, queueName, true)
session.createQueue(queueName, RoutingType.ANYCAST, queueName, true)
}
}
createQueueIfAbsent(VERIFICATION_REQUESTS_QUEUE_NAME)

View File

@ -159,6 +159,12 @@ open class PersistentNetworkMapCache(
private val nodesByKeyCache = NonInvalidatingCache<PublicKey, List<NodeInfo>>(1024, 8, { key -> database.transaction { queryByIdentityKey(session, key) } })
override fun getNodesByOwningKeyIndex(identityKeyIndex: String): List<NodeInfo> {
return database.transaction {
queryByIdentityKeyIndex(session, identityKeyIndex)
}
}
override fun getNodeByAddress(address: NetworkHostAndPort): NodeInfo? = database.transaction { queryByAddress(session, address) }
override fun getPeerCertificateByLegalName(name: CordaX500Name): PartyAndCertificate? = identityByLegalNameCache.get(name).orElse(null)
@ -244,15 +250,23 @@ open class PersistentNetworkMapCache(
}
private fun findByIdentityKey(session: Session, identityKey: PublicKey): List<NodeInfoSchemaV1.PersistentNodeInfo> {
return findByIdentityKeyIndex(session, identityKey.toStringShort())
}
private fun findByIdentityKeyIndex(session: Session, identityKeyIndex: String): List<NodeInfoSchemaV1.PersistentNodeInfo> {
val query = session.createQuery(
"SELECT n FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n JOIN n.legalIdentitiesAndCerts l WHERE l.owningKeyHash = :owningKeyHash",
NodeInfoSchemaV1.PersistentNodeInfo::class.java)
query.setParameter("owningKeyHash", identityKey.toStringShort())
query.setParameter("owningKeyHash", identityKeyIndex)
return query.resultList
}
private fun queryByIdentityKey(session: Session, identityKey: PublicKey): List<NodeInfo> {
val result = findByIdentityKey(session, identityKey)
return queryByIdentityKeyIndex(session, identityKey.toStringShort())
}
private fun queryByIdentityKeyIndex(session: Session, identityKeyIndex: String): List<NodeInfo> {
val result = findByIdentityKeyIndex(session, identityKeyIndex)
return result.map { it.toNodeInfo() }
}

View File

@ -173,6 +173,7 @@ class ArtemisMessagingTests {
MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
server,
identity.public,
null,
ServiceAffinityExecutor("ArtemisMessagingTests", 1),
database,
maxMessageSize = maxMessageSize).apply {