Add advertised services to AbstractNode and subclasses

This commit is contained in:
Ross Nicoll 2016-04-27 15:17:41 +01:00
parent 8a42da5362
commit 5134dd4bbc
9 changed files with 47 additions and 34 deletions

View File

@ -24,7 +24,8 @@ import java.util.*
* A base node implementation that can be customised either for production (with real implementations that do real * A base node implementation that can be customised either for production (with real implementations that do real
* I/O), or a mock implementation suitable for unit test environments. * I/O), or a mock implementation suitable for unit test environments.
*/ */
abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration, val timestamperAddress: NodeInfo?, val platformClock: Clock) { abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration, val timestamperAddress: NodeInfo?,
val advertisedServices: Set<ServiceType>, val platformClock: Clock) {
companion object { companion object {
val PRIVATE_KEY_FILE_NAME = "identity-private-key" val PRIVATE_KEY_FILE_NAME = "identity-private-key"
val PUBLIC_IDENTITY_FILE_NAME = "identity-public" val PUBLIC_IDENTITY_FILE_NAME = "identity-public"
@ -53,7 +54,7 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration,
} }
val info: NodeInfo by lazy { val info: NodeInfo by lazy {
NodeInfo(net.myAddress, storage.myLegalIdentity, emptySet(), findMyLocation()) NodeInfo(net.myAddress, storage.myLegalIdentity, advertisedServices, findMyLocation())
} }
protected open fun findMyLocation(): PhysicalLocation? = CityDatabase[configuration.nearestCity] protected open fun findMyLocation(): PhysicalLocation? = CityDatabase[configuration.nearestCity]

View File

@ -7,6 +7,7 @@ import com.codahale.metrics.JmxReporter
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import core.messaging.MessagingService import core.messaging.MessagingService
import core.node.services.ArtemisMessagingService import core.node.services.ArtemisMessagingService
import core.node.services.ServiceType
import core.node.servlets.AttachmentDownloadServlet import core.node.servlets.AttachmentDownloadServlet
import core.node.servlets.DataUploadServlet import core.node.servlets.DataUploadServlet
import core.utilities.AffinityExecutor import core.utilities.AffinityExecutor
@ -41,11 +42,13 @@ class ConfigurationException(message: String) : Exception(message)
* have to specify that yourself. * have to specify that yourself.
* @param configuration This is typically loaded from a .properties file * @param configuration This is typically loaded from a .properties file
* @param timestamperAddress If null, this node will become a timestamping node, otherwise, it will use that one. * @param timestamperAddress If null, this node will become a timestamping node, otherwise, it will use that one.
* @param advertisedServices The services this node advertises. This must be a subset of the services it runs,
* but nodes are not required to advertise services they run (hence subset).
* @param clock The clock used within the node and by all protocols etc * @param clock The clock used within the node and by all protocols etc
*/ */
class Node(dir: Path, val p2pAddr: HostAndPort, configuration: NodeConfiguration, class Node(dir: Path, val p2pAddr: HostAndPort, configuration: NodeConfiguration,
timestamperAddress: NodeInfo?, timestamperAddress: NodeInfo?, advertisedServices: Set<ServiceType>,
clock: Clock = Clock.systemUTC()) : AbstractNode(dir, configuration, timestamperAddress, clock) { clock: Clock = Clock.systemUTC()) : AbstractNode(dir, configuration, timestamperAddress, advertisedServices, clock) {
companion object { companion object {
/** The port that is used by default if none is specified. As you know, 31337 is the most elite number. */ /** The port that is used by default if none is specified. As you know, 31337 is the most elite number. */
val DEFAULT_PORT = 31337 val DEFAULT_PORT = 31337

View File

@ -8,7 +8,6 @@ import core.node.AbstractNode
import core.node.NodeConfiguration import core.node.NodeConfiguration
import core.node.NodeInfo import core.node.NodeInfo
import core.node.PhysicalLocation import core.node.PhysicalLocation
import core.testing.MockIdentityService
import core.node.services.ServiceType import core.node.services.ServiceType
import core.node.services.TimestamperService import core.node.services.TimestamperService
import core.utilities.AffinityExecutor import core.utilities.AffinityExecutor
@ -46,18 +45,19 @@ class MockNetwork(private val threadPerNode: Boolean = false,
/** Allows customisation of how nodes are created. */ /** Allows customisation of how nodes are created. */
interface Factory { interface Factory {
fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, fun create(dir: Path, config: NodeConfiguration, network: MockNetwork,
timestamperAddr: NodeInfo?, id: Int): MockNode timestamperAddr: NodeInfo?, advertisedServices: Set<ServiceType>, id: Int): MockNode
} }
object DefaultFactory : Factory { object DefaultFactory : Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork,
timestamperAddr: NodeInfo?, id: Int): MockNode { timestamperAddr: NodeInfo?, advertisedServices: Set<ServiceType>, id: Int): MockNode {
return MockNode(dir, config, network, timestamperAddr, id) return MockNode(dir, config, network, timestamperAddr, advertisedServices, id)
} }
} }
open class MockNode(dir: Path, config: NodeConfiguration, val mockNet: MockNetwork, open class MockNode(dir: Path, config: NodeConfiguration, val mockNet: MockNetwork,
withTimestamper: NodeInfo?, val id: Int) : AbstractNode(dir, config, withTimestamper, Clock.systemUTC()) { withTimestamper: NodeInfo?, advertisedServices: Set<ServiceType>,
val id: Int) : AbstractNode(dir, config, withTimestamper, advertisedServices, Clock.systemUTC()) {
override val log: Logger = loggerFor<MockNode>() override val log: Logger = loggerFor<MockNode>()
override val serverThread: AffinityExecutor = override val serverThread: AffinityExecutor =
if (mockNet.threadPerNode) if (mockNet.threadPerNode)
@ -101,8 +101,7 @@ class MockNetwork(private val threadPerNode: Boolean = false,
override val exportJMXto: String = "" override val exportJMXto: String = ""
override val nearestCity: String = "Atlantis" override val nearestCity: String = "Atlantis"
} }
val node = nodeFactory.create(path, config, this, withTimestamper, id).start() val node = nodeFactory.create(path, config, this, withTimestamper, advertisedServices, id).start()
node.info.advertisedServices = advertisedServices
_nodes.add(node) _nodes.add(node)
return node return node
} }

View File

@ -5,6 +5,7 @@ import core.node.CityDatabase
import core.node.NodeConfiguration import core.node.NodeConfiguration
import core.node.NodeInfo import core.node.NodeInfo
import core.node.PhysicalLocation import core.node.PhysicalLocation
import core.node.services.ServiceType
import core.protocols.ProtocolLogic import core.protocols.ProtocolLogic
import core.then import core.then
import core.utilities.ProgressTracker import core.utilities.ProgressTracker
@ -32,14 +33,15 @@ abstract class Simulation(val runAsync: Boolean,
// This puts together a mock network of SimulatedNodes. // This puts together a mock network of SimulatedNodes.
open class SimulatedNode(dir: Path, config: NodeConfiguration, mockNet: MockNetwork, open class SimulatedNode(dir: Path, config: NodeConfiguration, mockNet: MockNetwork,
withTimestamper: NodeInfo?, id: Int) : MockNetwork.MockNode(dir, config, mockNet, withTimestamper, id) { withTimestamper: NodeInfo?, advertisedServices: Set<ServiceType>, id: Int)
: MockNetwork.MockNode(dir, config, mockNet, withTimestamper, advertisedServices, id) {
override fun findMyLocation(): PhysicalLocation? = CityDatabase[configuration.nearestCity] override fun findMyLocation(): PhysicalLocation? = CityDatabase[configuration.nearestCity]
} }
inner class BankFactory : MockNetwork.Factory { inner class BankFactory : MockNetwork.Factory {
var counter = 0 var counter = 0
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?, id: Int): MockNetwork.MockNode { override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?, advertisedServices: Set<ServiceType>, id: Int): MockNetwork.MockNode {
val letter = 'A' + counter val letter = 'A' + counter
val city = bankLocations[counter++ % bankLocations.size] val city = bankLocations[counter++ % bankLocations.size]
val cfg = object : NodeConfiguration { val cfg = object : NodeConfiguration {
@ -48,7 +50,7 @@ abstract class Simulation(val runAsync: Boolean,
override val exportJMXto: String = "" override val exportJMXto: String = ""
override val nearestCity: String = city override val nearestCity: String = city
} }
return SimulatedNode(dir, cfg, network, timestamperAddr, id) return SimulatedNode(dir, cfg, network, timestamperAddr, advertisedServices, id)
} }
fun createAll(): List<SimulatedNode> = bankLocations.map { network.createNode(timestamper.info, nodeFactory = this) as SimulatedNode } fun createAll(): List<SimulatedNode> = bankLocations.map { network.createNode(timestamper.info, nodeFactory = this) as SimulatedNode }
@ -57,25 +59,25 @@ abstract class Simulation(val runAsync: Boolean,
val bankFactory = BankFactory() val bankFactory = BankFactory()
object TimestampingNodeFactory : MockNetwork.Factory { object TimestampingNodeFactory : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?, id: Int): MockNetwork.MockNode { override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?, advertisedServices: Set<ServiceType>, id: Int): MockNetwork.MockNode {
val cfg = object : NodeConfiguration { val cfg = object : NodeConfiguration {
override val myLegalName: String = "Timestamping Service" // A magic string recognised by the CP contract override val myLegalName: String = "Timestamping Service" // A magic string recognised by the CP contract
override val exportJMXto: String = "" override val exportJMXto: String = ""
override val nearestCity: String = "Zurich" override val nearestCity: String = "Zurich"
} }
return SimulatedNode(dir, cfg, network, timestamperAddr, id) return SimulatedNode(dir, cfg, network, timestamperAddr, advertisedServices, id)
} }
} }
object RatesOracleFactory : MockNetwork.Factory { object RatesOracleFactory : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?, id: Int): MockNetwork.MockNode { override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?, advertisedServices: Set<ServiceType>, id: Int): MockNetwork.MockNode {
val cfg = object : NodeConfiguration { val cfg = object : NodeConfiguration {
override val myLegalName: String = "Rates Service Provider" override val myLegalName: String = "Rates Service Provider"
override val exportJMXto: String = "" override val exportJMXto: String = ""
override val nearestCity: String = "Madrid" override val nearestCity: String = "Madrid"
} }
val n = object : SimulatedNode(dir, cfg, network, timestamperAddr, id) { val n = object : SimulatedNode(dir, cfg, network, timestamperAddr, advertisedServices, id) {
override fun makeInterestRatesOracleService() { override fun makeInterestRatesOracleService() {
super.makeInterestRatesOracleService() super.makeInterestRatesOracleService()
interestRatesService.upload(javaClass.getResourceAsStream("example.rates.txt")) interestRatesService.upload(javaClass.getResourceAsStream("example.rates.txt"))
@ -86,14 +88,14 @@ abstract class Simulation(val runAsync: Boolean,
} }
object RegulatorFactory : MockNetwork.Factory { object RegulatorFactory : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?, id: Int): MockNetwork.MockNode { override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?, advertisedServices: Set<ServiceType>, id: Int): MockNetwork.MockNode {
val cfg = object : NodeConfiguration { val cfg = object : NodeConfiguration {
override val myLegalName: String = "Regulator A" override val myLegalName: String = "Regulator A"
override val exportJMXto: String = "" override val exportJMXto: String = ""
override val nearestCity: String = "Paris" override val nearestCity: String = "Paris"
} }
val n = object : SimulatedNode(dir, cfg, network, timestamperAddr, id) { val n = object : SimulatedNode(dir, cfg, network, timestamperAddr, advertisedServices, id) {
// TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request. // TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request.
// So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it. // So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it.
// But that's fine for visualisation purposes. // But that's fine for visualisation purposes.

View File

@ -22,6 +22,7 @@ import joptsimple.OptionParser
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.util.*
import kotlin.system.exitProcess import kotlin.system.exitProcess
// IRS DEMO // IRS DEMO
@ -66,11 +67,13 @@ fun main(args: Array<String>) {
} }
val config = loadConfigFile(configFile) val config = loadConfigFile(configFile)
val advertisedServices = HashSet<ServiceType>()
val myNetAddr = HostAndPort.fromString(options.valueOf(networkAddressArg)).withDefaultPort(Node.DEFAULT_PORT) val myNetAddr = HostAndPort.fromString(options.valueOf(networkAddressArg)).withDefaultPort(Node.DEFAULT_PORT)
// The timestamping node runs in the same process as the one that passes null to Node constructor. // The timestamping node runs in the same process as the one that passes null to Node constructor.
val timestamperId = if (options.valueOf(timestamperNetAddr).equals(options.valueOf(networkAddressArg))) { val timestamperId = if (options.valueOf(timestamperNetAddr).equals(options.valueOf(networkAddressArg))) {
// This node provides timestamping services
advertisedServices.add(TimestamperService.Type)
null null
} else { } else {
try { try {
@ -82,6 +85,7 @@ fun main(args: Array<String>) {
// The timestamping node runs in the same process as the one that passes null to Node constructor. // The timestamping node runs in the same process as the one that passes null to Node constructor.
val rateOracleId = if (options.valueOf(rateOracleNetAddr).equals(options.valueOf(networkAddressArg))) { val rateOracleId = if (options.valueOf(rateOracleNetAddr).equals(options.valueOf(networkAddressArg))) {
advertisedServices.add(NodeInterestRates.Type)
null null
} else { } else {
try { try {
@ -91,7 +95,7 @@ fun main(args: Array<String>) {
} }
} }
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, timestamperId, DemoClock()).start() } val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, timestamperId, advertisedServices, DemoClock()).start() }
// Add self to network map // Add self to network map
(node.services.networkMapCache as MockNetworkMapCache).addRegistration(node.info) (node.services.networkMapCache as MockNetworkMapCache).addRegistration(node.info)

View File

@ -7,6 +7,7 @@ import core.node.NodeConfiguration
import core.node.NodeInfo import core.node.NodeInfo
import core.node.services.ArtemisMessagingService import core.node.services.ArtemisMessagingService
import core.node.services.NodeInterestRates import core.node.services.NodeInterestRates
import core.node.services.ServiceType
import core.serialization.deserialize import core.serialization.deserialize
import core.utilities.ANSIProgressRenderer import core.utilities.ANSIProgressRenderer
import core.utilities.BriefLogFormatter import core.utilities.BriefLogFormatter
@ -59,13 +60,14 @@ fun main(args: Array<String>) {
val rateTolerance = BigDecimal(options.valueOf(rateToleranceArg)) val rateTolerance = BigDecimal(options.valueOf(rateToleranceArg))
// Bring up node. // Bring up node.
var advertisedServices: Set<ServiceType> = emptySet()
val myNetAddr = ArtemisMessagingService.toHostAndPort(options.valueOf(networkAddressArg)) val myNetAddr = ArtemisMessagingService.toHostAndPort(options.valueOf(networkAddressArg))
val config = object : NodeConfiguration { val config = object : NodeConfiguration {
override val myLegalName: String = "Rate fix demo node" override val myLegalName: String = "Rate fix demo node"
override val exportJMXto: String = "http" override val exportJMXto: String = "http"
override val nearestCity: String = "Atlantis" override val nearestCity: String = "Atlantis"
} }
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, null).start() } val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, null, advertisedServices).start() }
// Make a garbage transaction that includes a rate fix. // Make a garbage transaction that includes a rate fix.
val tx = TransactionBuilder() val tx = TransactionBuilder()

View File

@ -12,10 +12,7 @@ import core.node.Node
import core.node.NodeConfiguration import core.node.NodeConfiguration
import core.node.NodeConfigurationFromConfig import core.node.NodeConfigurationFromConfig
import core.node.NodeInfo import core.node.NodeInfo
import core.node.services.ArtemisMessagingService import core.node.services.*
import core.node.services.NodeAttachmentService
import core.node.services.NodeWalletService
import core.node.services.TimestamperService
import core.protocols.ProtocolLogic import core.protocols.ProtocolLogic
import core.serialization.deserialize import core.serialization.deserialize
import core.utilities.ANSIProgressRenderer import core.utilities.ANSIProgressRenderer
@ -75,6 +72,7 @@ fun main(args: Array<String>) {
val config = loadConfigFile(configFile) val config = loadConfigFile(configFile)
var advertisedServices: Set<ServiceType> = emptySet()
val myNetAddr = HostAndPort.fromString(options.valueOf(networkAddressArg)).withDefaultPort(Node.DEFAULT_PORT) val myNetAddr = HostAndPort.fromString(options.valueOf(networkAddressArg)).withDefaultPort(Node.DEFAULT_PORT)
val listening = options.has(serviceFakeTradesArg) val listening = options.has(serviceFakeTradesArg)
@ -91,7 +89,7 @@ fun main(args: Array<String>) {
NodeInfo(ArtemisMessagingService.makeRecipient(addr), party, advertisedServices = setOf(TimestamperService.Type)) NodeInfo(ArtemisMessagingService.makeRecipient(addr), party, advertisedServices = setOf(TimestamperService.Type))
} else null } else null
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, timestamperId).start() } val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, timestamperId, advertisedServices).start() }
if (listening) { if (listening) {
// For demo purposes just extract attachment jars when saved to disk, so the user can explore them. // For demo purposes just extract attachment jars when saved to disk, so the user can explore them.

View File

@ -6,6 +6,7 @@ import core.crypto.sha256
import core.node.NodeConfiguration import core.node.NodeConfiguration
import core.node.NodeInfo import core.node.NodeInfo
import core.node.services.NodeAttachmentService import core.node.services.NodeAttachmentService
import core.node.services.ServiceType
import core.node.services.TimestamperService import core.node.services.TimestamperService
import core.serialization.OpaqueBytes import core.serialization.OpaqueBytes
import core.testing.MockNetwork import core.testing.MockNetwork
@ -87,8 +88,9 @@ class AttachmentTests {
fun maliciousResponse() { fun maliciousResponse() {
// Make a node that doesn't do sanity checking at load time. // Make a node that doesn't do sanity checking at load time.
val n0 = network.createNode(null, nodeFactory = object : MockNetwork.Factory { val n0 = network.createNode(null, nodeFactory = object : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?, id: Int): MockNetwork.MockNode { override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?,
return object : MockNetwork.MockNode(dir, config, network, timestamperAddr, id) { advertisedServices: Set<ServiceType>, id: Int): MockNetwork.MockNode {
return object : MockNetwork.MockNode(dir, config, network, timestamperAddr, advertisedServices, id) {
override fun start(): MockNetwork.MockNode { override fun start(): MockNetwork.MockNode {
super.start() super.start()
(storage.attachments as NodeAttachmentService).checkAttachmentsOnLoad = false (storage.attachments as NodeAttachmentService).checkAttachmentsOnLoad = false

View File

@ -154,8 +154,9 @@ class TwoPartyTradeProtocolTests {
// ... bring the node back up ... the act of constructing the SMM will re-register the message handlers // ... bring the node back up ... the act of constructing the SMM will re-register the message handlers
// that Bob was waiting on before the reboot occurred. // that Bob was waiting on before the reboot occurred.
bobNode = net.createNode(timestamperAddr, bobAddr.id, object : MockNetwork.Factory { bobNode = net.createNode(timestamperAddr, bobAddr.id, object : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?, id: Int): MockNetwork.MockNode { override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?,
return object : MockNetwork.MockNode(dir, config, network, timestamperAddr, bobAddr.id) { advertisedServices: Set<ServiceType>, id: Int): MockNetwork.MockNode {
return object : MockNetwork.MockNode(dir, config, network, timestamperAddr, advertisedServices, bobAddr.id) {
override fun initialiseStorageService(dir: Path): StorageService { override fun initialiseStorageService(dir: Path): StorageService {
val ss = super.initialiseStorageService(dir) val ss = super.initialiseStorageService(dir)
val smMap = ss.stateMachines val smMap = ss.stateMachines
@ -185,8 +186,9 @@ class TwoPartyTradeProtocolTests {
private fun makeNodeWithTracking(name: String): MockNetwork.MockNode { private fun makeNodeWithTracking(name: String): MockNetwork.MockNode {
// Create a node in the mock network ... // Create a node in the mock network ...
return net.createNode(nodeFactory = object : MockNetwork.Factory { return net.createNode(nodeFactory = object : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?, id: Int): MockNetwork.MockNode { override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: NodeInfo?,
return object : MockNetwork.MockNode(dir, config, network, timestamperAddr, id) { advertisedServices: Set<ServiceType>, id: Int): MockNetwork.MockNode {
return object : MockNetwork.MockNode(dir, config, network, timestamperAddr, advertisedServices, id) {
// That constructs the storage service object in a customised way ... // That constructs the storage service object in a customised way ...
override fun constructStorageService(attachments: NodeAttachmentService, keypair: KeyPair, identity: Party): StorageServiceImpl { override fun constructStorageService(attachments: NodeAttachmentService, keypair: KeyPair, identity: Party): StorageServiceImpl {
// To use RecordingMaps instead of ordinary HashMaps. // To use RecordingMaps instead of ordinary HashMaps.