From 727cd0e55c9c992f2e337b2e26f3e8472ac9d8c1 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 5 Oct 2017 12:27:45 +0100 Subject: [PATCH] Cleaned up notary configuration by introducing a notary config option. extraAdvertisedServiceIds is no longer used for this. --- build.gradle | 3 +- .../corda/client/jfx/NodeMonitorModelTest.kt | 4 +- .../client/rpc/CordaRPCJavaClientTest.java | 6 +- .../corda/client/rpc/CordaRPCClientTest.kt | 4 +- .../rpc/StandaloneCordaRPCJavaClientTest.java | 12 +- .../kotlin/rpc/StandaloneCordaRPClientTest.kt | 2 +- config/dev/nameservernode.conf | 4 +- .../kotlin/net/corda/core/flows/NotaryFlow.kt | 6 +- .../core/node/services/NetworkMapCache.kt | 6 +- .../corda/core/node/services/NotaryService.kt | 12 ++ .../net/corda/core/flows/AttachmentTests.kt | 5 +- docs/source/changelog.rst | 5 + docs/source/corda-configuration-file.rst | 34 ++++-- docs/source/deploying-a-node.rst | 3 +- docs/source/example-code/build.gradle | 3 +- .../corda/docs/IntegrationTestingTutorial.kt | 6 +- .../net/corda/docs/ClientRpcTutorial.kt | 4 +- .../net/corda/docs/CustomVaultQueryTest.kt | 8 +- .../docs/FxTransactionBuildTutorialTest.kt | 8 +- .../WorkflowTransactionBuildTutorialTest.kt | 13 +- docs/source/hello-world-running.rst | 3 +- docs/source/running-a-notary.rst | 8 +- docs/source/tutorial-custom-notary.rst | 6 - .../java/net/corda/cordform/CordformNode.java | 31 ++--- .../main/groovy/net/corda/plugins/Node.groovy | 16 +-- .../src/main/kotlin/net/corda/nodeapi/User.kt | 8 +- .../corda/nodeapi/config/ConfigUtilities.kt | 68 ++++++++++- .../corda/nodeapi/config/ConfigParsingTest.kt | 52 +++++--- .../net/corda/node/NodePerformanceTests.kt | 12 +- .../node/services/AttachmentLoadingTests.kt | 4 +- .../node/services/BFTNotaryServiceTests.kt | 23 ++-- .../node/services/DistributedServiceTests.kt | 5 +- .../node/services/RaftNotaryServiceTests.kt | 4 +- .../services/messaging/P2PMessagingTest.kt | 46 +------ .../test/node/NodeStatePersistenceTests.kt | 5 +- .../net/corda/node/internal/AbstractNode.kt | 113 ++++++++++-------- .../net/corda/node/internal/NodeStartup.kt | 11 +- .../node/services/config/NodeConfiguration.kt | 41 ++++--- .../network/PersistentNetworkMapCache.kt | 7 +- .../BFTNonValidatingNotaryService.kt | 62 +++++----- .../RaftNonValidatingNotaryService.kt | 13 +- .../transactions/RaftUniquenessProvider.kt | 26 ++-- .../RaftValidatingNotaryService.kt | 13 +- .../transactions/SimpleNotaryService.kt | 5 - .../transactions/ValidatingNotaryService.kt | 4 +- node/src/main/resources/reference.conf | 4 - .../kotlin/net/corda/node/CordappSmokeTest.kt | 2 +- .../net/corda/node/CordaRPCOpsImplTest.kt | 4 +- .../corda/node/internal/CordaServiceTest.kt | 11 +- .../corda/node/services/NotaryChangeTests.kt | 8 +- .../config/FullNodeConfigurationTest.kt | 4 +- .../services/events/ScheduledFlowTests.kt | 6 +- .../statemachine/FlowFrameworkTests.kt | 87 ++------------ .../transactions/NotaryServiceTests.kt | 5 +- .../ValidatingNotaryServiceTests.kt | 6 +- samples/attachment-demo/build.gradle | 3 +- .../attachmentdemo/AttachmentDemoTest.kt | 4 +- .../kotlin/net/corda/attachmentdemo/Main.kt | 6 +- samples/bank-of-corda-demo/build.gradle | 3 +- .../net/corda/bank/BankOfCordaHttpAPITest.kt | 4 +- .../corda/bank/BankOfCordaRPCClientTest.kt | 4 +- .../net/corda/bank/BankOfCordaDriver.kt | 8 +- samples/irs-demo/build.gradle | 3 +- .../kotlin/net/corda/irs/IRSDemoTest.kt | 6 +- .../src/test/kotlin/net/corda/irs/Main.kt | 6 +- .../net/corda/netmap/simulation/Simulation.kt | 14 +-- .../net/corda/notarydemo/BFTNotaryCordform.kt | 15 +-- .../corda/notarydemo/RaftNotaryCordform.kt | 24 ++-- .../corda/notarydemo/SingleNotaryCordform.kt | 15 ++- samples/simm-valuation-demo/build.gradle | 5 +- .../net/corda/vega/SimmValuationTest.kt | 11 +- .../src/test/kotlin/net/corda/vega/Main.kt | 4 +- samples/trader-demo/build.gradle | 3 +- .../net/corda/traderdemo/TraderDemoTest.kt | 4 +- .../test/kotlin/net/corda/traderdemo/Main.kt | 4 +- .../corda/testing/FlowStackSnapshotTest.kt | 8 +- .../net/corda/testing/driver/DriverTests.kt | 6 +- .../net/corda/testing/DriverConstants.kt | 18 +-- .../kotlin/net/corda/testing/NodeTestUtils.kt | 6 +- .../kotlin/net/corda/testing/driver/Driver.kt | 62 +++++++--- .../testing/internal/demorun/CordformUtils.kt | 13 +- .../kotlin/net/corda/testing/node/MockNode.kt | 56 ++++----- .../net/corda/testing/node/NodeBasedTest.kt | 49 +++++--- .../net/corda/smoketesting/NodeConfig.kt | 33 +++-- .../net/corda/demobench/model/NodeConfig.kt | 32 ++--- .../net/corda/demobench/model/UserTest.kt | 4 +- .../net/corda/explorer/ExplorerSimulation.kt | 6 +- .../net/corda/verifier/VerifierTests.kt | 6 +- 88 files changed, 600 insertions(+), 716 deletions(-) diff --git a/build.gradle b/build.gradle index 6cfd34fed9..bf12b2ff1f 100644 --- a/build.gradle +++ b/build.gradle @@ -233,7 +233,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { networkMap "O=Controller,OU=corda,L=London,C=GB" node { name "O=Controller,OU=corda,L=London,C=GB" - advertisedServices = ["corda.notary.validating"] + notary = [validating : true] + advertisedServices = [] p2pPort 10002 cordapps = [] } diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index ea0c88189c..e0131e6e99 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -27,9 +27,7 @@ import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.driver.driver import net.corda.testing.node.DriverBasedTest @@ -59,7 +57,7 @@ class NodeMonitorModelTest : DriverBasedTest() { startFlowPermission()) ) val aliceNodeFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)) - val notaryHandle = startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))).getOrThrow() + val notaryHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow() val aliceNodeHandle = aliceNodeFuture.getOrThrow() aliceNode = aliceNodeHandle.nodeInfo newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo } diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index 1862324c4f..e596a61418 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -11,9 +11,7 @@ import net.corda.finance.flows.CashPaymentFlow; import net.corda.finance.schemas.CashSchemaV1; import net.corda.node.internal.Node; import net.corda.node.internal.StartedNode; -import net.corda.node.services.transactions.ValidatingNotaryService; import net.corda.nodeapi.User; -import net.corda.nodeapi.internal.ServiceInfo; import net.corda.testing.CoreTestUtils; import net.corda.testing.node.NodeBasedTest; import org.junit.After; @@ -24,7 +22,6 @@ import java.io.IOException; import java.util.*; import java.util.concurrent.ExecutionException; -import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static java.util.Objects.requireNonNull; import static kotlin.test.AssertionsKt.assertEquals; @@ -53,8 +50,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { @Before public void setUp() throws ExecutionException, InterruptedException { setCordappPackages("net.corda.finance.contracts"); - Set services = new HashSet<>(singletonList(new ServiceInfo(ValidatingNotaryService.Companion.getType(), null))); - CordaFuture> nodeFuture = startNode(getALICE().getName(), 1, services, singletonList(rpcUser), emptyMap()); + CordaFuture> nodeFuture = startNotaryNode(getALICE().getName(), singletonList(rpcUser), true); node = nodeFuture.get(); node.getInternals().registerCustomSchemas(Collections.singleton(CashSchemaV1.INSTANCE)); client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress())); diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index 32284a970f..422f08f8f3 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -19,9 +19,7 @@ import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.ALICE import net.corda.testing.chooseIdentity import net.corda.testing.node.NodeBasedTest @@ -52,7 +50,7 @@ class CordaRPCClientTest : NodeBasedTest() { @Before fun setUp() { setCordappPackages("net.corda.finance.contracts") - node = startNode(ALICE.name, rpcUsers = listOf(rpcUser), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))).getOrThrow() + node = startNotaryNode(ALICE.name, rpcUsers = listOf(rpcUser)).getOrThrow() node.internals.registerCustomSchemas(setOf(CashSchemaV1)) client = CordaRPCClient(node.internals.configuration.rpcAddress!!) } diff --git a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java index bc522e42f0..e9ebce530f 100644 --- a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java +++ b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java @@ -6,7 +6,6 @@ import net.corda.core.identity.CordaX500Name; import net.corda.core.identity.Party; import net.corda.core.messaging.CordaRPCOps; import net.corda.core.messaging.FlowHandle; -import net.corda.core.node.NodeInfo; import net.corda.core.utilities.OpaqueBytes; import net.corda.finance.flows.AbstractCashFlow; import net.corda.finance.flows.CashIssueFlow; @@ -41,7 +40,6 @@ public class StandaloneCordaRPCJavaClientTest { private NodeProcess notary; private CordaRPCOps rpcProxy; private CordaRPCConnection connection; - private NodeInfo notaryNode; private Party notaryNodeIdentity; private NodeConfig notaryConfig = new NodeConfig( @@ -49,8 +47,8 @@ public class StandaloneCordaRPCJavaClientTest { port.getAndIncrement(), port.getAndIncrement(), port.getAndIncrement(), - Collections.singletonList("corda.notary.validating"), - Arrays.asList(rpcUser), + true, + Collections.singletonList(rpcUser), null ); @@ -61,7 +59,6 @@ public class StandaloneCordaRPCJavaClientTest { notary = factory.create(notaryConfig); connection = notary.connect(); rpcProxy = connection.getProxy(); - notaryNode = fetchNotaryIdentity(); notaryNodeIdentity = rpcProxy.nodeInfo().getLegalIdentities().get(0); } @@ -98,11 +95,6 @@ public class StandaloneCordaRPCJavaClientTest { } } - private NodeInfo fetchNotaryIdentity() { - List nodeDataSnapshot = rpcProxy.networkMapSnapshot(); - return nodeDataSnapshot.get(0); - } - @Test public void testCashBalances() throws NoSuchFieldException, ExecutionException, InterruptedException { Amount dollars123 = new Amount<>(123, Currency.getInstance("USD")); diff --git a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt index 6da6aa3dc4..40ae8c5f16 100644 --- a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt +++ b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt @@ -64,7 +64,7 @@ class StandaloneCordaRPClientTest { p2pPort = port.andIncrement, rpcPort = port.andIncrement, webPort = port.andIncrement, - extraServices = listOf("corda.notary.validating"), + isNotary = true, users = listOf(user) ) diff --git a/config/dev/nameservernode.conf b/config/dev/nameservernode.conf index 664f2b6816..0519e56872 100644 --- a/config/dev/nameservernode.conf +++ b/config/dev/nameservernode.conf @@ -3,5 +3,7 @@ keyStorePassword : "cordacadevpass" trustStorePassword : "trustpass" p2pAddress : "localhost:10000" webAddress : "localhost:10001" -extraAdvertisedServiceIds : [ "corda.notary.validating" ] +notary : { + validating : true +} useHTTPS : false diff --git a/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt b/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt index f738df62be..c4d122c649 100644 --- a/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt @@ -13,7 +13,6 @@ import net.corda.core.node.services.NotaryService import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.core.node.services.UniquenessProvider import net.corda.core.serialization.CordaSerializable -import net.corda.core.transactions.FilteredTransaction import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.UntrustworthyData @@ -138,8 +137,11 @@ class NotaryFlow { // Check if transaction is intended to be signed by this notary. @Suspendable protected fun checkNotary(notary: Party?) { - if (notary !in serviceHub.myInfo.legalIdentities) + // TODO This check implies that it's OK to use the node's main identity. Shouldn't it be just limited to the + // notary identities? + if (notary !in serviceHub.myInfo.legalIdentities) { throw NotaryException(NotaryError.WrongNotary) + } } @Suspendable diff --git a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt index 8217eb69b4..792baadb39 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt @@ -116,10 +116,8 @@ interface NetworkMapCache { fun isNotary(party: Party): Boolean = party in notaryIdentities /** Checks whether a given party is an validating notary identity. */ - fun isValidatingNotary(party: Party): Boolean { - require(isNotary(party)) { "No notary found with identity $party." } - return !party.name.toString().contains("corda.notary.simple", true) // TODO This implementation will change after introducing of NetworkParameters. - } + // TODO This implementation will change after introducing of NetworkParameters. + fun isValidatingNotary(party: Party): Boolean = isNotary(party) && "validating" in party.name.commonName!! /** Clear all network map data from local node cache. */ fun clearNetworkMapCache() diff --git a/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt b/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt index 71e24c506d..574008d6d8 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt @@ -13,6 +13,18 @@ import org.slf4j.Logger import java.security.PublicKey abstract class NotaryService : SingletonSerializeAsToken() { + companion object { + const val ID_PREFIX = "corda.notary." + fun constructId(validating: Boolean, raft: Boolean = false, bft: Boolean = false): String { + require(!raft || !bft) + return StringBuffer(ID_PREFIX).apply { + append(if (validating) "validating" else "simple") + if (raft) append(".raft") + if (bft) append(".bft") + }.toString() + } + } + abstract val services: ServiceHub abstract val notaryIdentityKey: PublicKey diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index 5da5583af6..034c28c834 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -12,7 +12,6 @@ import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.persistence.NodeAttachmentService -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.node.utilities.DatabaseTransactionManager import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.ALICE @@ -117,7 +116,7 @@ class AttachmentTests { @Test fun `malicious response`() { // Make a node that doesn't do sanity checking at load time. - val aliceNode = mockNet.createNode(legalName = ALICE.name, nodeFactory = object : MockNetwork.Factory { + val aliceNode = mockNet.createNotaryNode(legalName = ALICE.name, nodeFactory = object : MockNetwork.Factory { override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, advertisedServices: Set, id: Int, notaryIdentity: Pair?, @@ -126,7 +125,7 @@ class AttachmentTests { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false } } } - }, advertisedServices = *arrayOf(ServiceInfo(SimpleNotaryService.type))) + }, validating = false) val bobNode = mockNet.createNode(legalName = BOB.name) // Ensure that registration was successful before progressing any further diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index c996b4254b..f07f2e964e 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -20,6 +20,11 @@ UNRELEASED either annotated with the @CordaSerializable annotation or explicitly whitelisted then a NotSerializableException is thrown. +* ``extraAdvertisedServiceIds`` config has been removed as part of the previous work to retire the concept of advertised + services. The remaining use of this config was for notaries, the configuring of which has been cleaned up and simplified. + ``notaryNodeAddress``, ``notaryClusterAddresses`` and ``bftSMaRt`` have also been removed and replaced by a single + ``notary`` config object. See :doc:`corda-configuration-file` for more details. + .. _changelog_v1: Release 1.0 diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index e2c2365c92..8511b2d486 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -30,7 +30,7 @@ General node configuration file for hosting the IRSDemo services. .. literalinclude:: example-code/src/main/resources/example-node.conf :language: javascript -NetworkMapService plus Simple Notary configuration file. +Simple Notary configuration file. .. parsed-literal:: @@ -40,7 +40,9 @@ NetworkMapService plus Simple Notary configuration file. p2pAddress : "localhost:12345" rpcAddress : "localhost:12346" webAddress : "localhost:12347" - extraAdvertisedServiceIds : ["corda.notary.simple"] + notary : { + validating : false + } useHTTPS : false devMode : true // Certificate signing service will be hosted by R3 in the near future. @@ -92,19 +94,25 @@ path to the node's base directory. .. note:: The driver will not automatically create a webserver instance, but the Cordformation will. If this field is present the web server will start. -:extraAdvertisedServiceIds: A list of ServiceType id strings to be advertised to the NetworkMapService and thus be available - when other nodes query the NetworkMapCache for supporting nodes. This can also include plugin services loaded from .jar - files in the plugins folder. Optionally, a custom advertised service name can be provided by appending it to the service - type id: ``"corda.notary.validating|Notary A"`` +:notary: Optional config object which if present configures the node to run as a notary. If part of a Raft or BFT SMaRt + cluster then specify ``raft`` or ``bftSMaRt`` respectively as described below. If a single node notary then omit both. -:notaryNodeAddress: The host and port to which to bind the embedded Raft server. Required only when running a distributed - notary service. A group of Corda nodes can run a distributed notary service by each running an embedded Raft server and - joining them to the same cluster to replicate the committed state log. Note that the Raft cluster uses a separate transport - layer for communication that does not integrate with ArtemisMQ messaging services. + :validating: Boolean to determine whether the notary is a validating or non-validating one. -:notaryClusterAddresses: List of Raft cluster member addresses used to join the cluster. At least one of the specified - members must be active and be able to communicate with the cluster leader for joining. If empty, a new cluster will be - bootstrapped. Required only when running a distributed notary service. + :raft: If part of a distributed Raft cluster specify this config object, with the following settings: + + :nodeAddress: The host and port to which to bind the embedded Raft server. Note that the Raft cluster uses a + separate transport layer for communication that does not integrate with ArtemisMQ messaging services. + + :clusterAddresses: List of Raft cluster member addresses used to join the cluster. At least one of the specified + members must be active and be able to communicate with the cluster leader for joining. If empty, a new + cluster will be bootstrapped. + + :bftSMaRt: If part of a distributed BFT SMaRt cluster specify this config object, with the following settings: + + :replicaId: + + :clusterAddresses: :networkMapService: If `null`, or missing the node is declaring itself as the NetworkMapService host. Otherwise this is a config object with the details of the network map service: diff --git a/docs/source/deploying-a-node.rst b/docs/source/deploying-a-node.rst index 562df5952f..ec15b75d8b 100644 --- a/docs/source/deploying-a-node.rst +++ b/docs/source/deploying-a-node.rst @@ -20,7 +20,8 @@ notary/network map node: networkMap "O=Controller,OU=corda,L=London,C=UK" node { name "O=Controller,OU=corda,L=London,C=UK" - advertisedServices = ["corda.notary.validating"] + notary = [validating : true] + advertisedServices = [] p2pPort 10002 rpcPort 10003 webPort 10004 diff --git a/docs/source/example-code/build.gradle b/docs/source/example-code/build.gradle index cf0c3046e8..6709d9fcbe 100644 --- a/docs/source/example-code/build.gradle +++ b/docs/source/example-code/build.gradle @@ -75,7 +75,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { networkMap "O=Notary Service,OU=corda,L=London,C=GB" node { name "O=Notary Service,OU=corda,L=London,C=GB" - advertisedServices = ["corda.notary.validating"] + notary = [validating : true] + advertisedServices = [] p2pPort 10002 rpcPort 10003 webPort 10004 diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt index 07ef6d6983..c665a8cb67 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt @@ -11,8 +11,6 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.driver @@ -32,10 +30,10 @@ class IntegrationTestingTutorial { val bobUser = User("bobUser", "testPassword2", permissions = setOf( startFlowPermission() )) - val (alice, bob, notary) = listOf( + val (alice, bob) = listOf( startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)), startNode(providedName = BOB.name, rpcUsers = listOf(bobUser)), - startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))) + startNotaryNode(DUMMY_NOTARY.name) ).transpose().getOrThrow() // END 1 diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt index bf6b46be6e..5b11d79549 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt @@ -15,9 +15,7 @@ import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.ALICE import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.driver @@ -49,7 +47,7 @@ fun main(args: Array) { startFlowPermission())) driver(driverDirectory = baseDirectory, extraCordappPackagesToScan = listOf("net.corda.finance")) { - startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))) + startNotaryNode(DUMMY_NOTARY.name) val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)).get() // END 1 diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt index 36a98ebe8b..d313217f40 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt @@ -9,8 +9,6 @@ import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.internal.StartedNode -import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.node.MockNetwork import org.junit.After @@ -30,11 +28,7 @@ class CustomVaultQueryTest { fun setup() { setCordappPackages("net.corda.finance.contracts.asset") mockNet = MockNetwork(threadPerNode = true) - val notaryService = ServiceInfo(ValidatingNotaryService.type) - mockNet.createNode( - legalName = DUMMY_NOTARY.name, - notaryIdentity = notaryService to DUMMY_NOTARY_KEY, - advertisedServices = *arrayOf(notaryService)) + mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name) nodeA = mockNet.createPartyNode() nodeB = mockNet.createPartyNode() diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt index 3152da5650..be28ddbec0 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt @@ -9,8 +9,6 @@ import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.internal.StartedNode -import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.node.MockNetwork import org.junit.After @@ -28,11 +26,7 @@ class FxTransactionBuildTutorialTest { fun setup() { setCordappPackages("net.corda.finance.contracts.asset") mockNet = MockNetwork(threadPerNode = true) - val notaryService = ServiceInfo(ValidatingNotaryService.type) - mockNet.createNode( - legalName = DUMMY_NOTARY.name, - notaryIdentity = notaryService to DUMMY_NOTARY_KEY, - advertisedServices = *arrayOf(notaryService)) + mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name) nodeA = mockNet.createPartyNode() nodeB = mockNet.createPartyNode() nodeA.internals.registerCustomSchemas(setOf(CashSchemaV1)) diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt index eed9b53759..6b9f0d279d 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt @@ -9,10 +9,11 @@ import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.toFuture import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.testing.* +import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork +import net.corda.testing.setCordappPackages +import net.corda.testing.unsetCordappPackages import org.junit.After import org.junit.Before import org.junit.Test @@ -33,11 +34,7 @@ class WorkflowTransactionBuildTutorialTest { fun setup() { setCordappPackages("net.corda.docs") mockNet = MockNetwork(threadPerNode = true) - val notaryService = ServiceInfo(ValidatingNotaryService.type) - mockNet.createNode( - legalName = DUMMY_NOTARY.name, - notaryIdentity = Pair(notaryService, DUMMY_NOTARY_KEY), - advertisedServices = *arrayOf(notaryService)) + mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name) nodeA = mockNet.createPartyNode() nodeB = mockNet.createPartyNode() nodeA.internals.registerInitiatedFlow(RecordCompletionFlow::class.java) diff --git a/docs/source/hello-world-running.rst b/docs/source/hello-world-running.rst index 7bf00e4771..1bfc2be2df 100644 --- a/docs/source/hello-world-running.rst +++ b/docs/source/hello-world-running.rst @@ -28,7 +28,8 @@ Let's take a look at the nodes we're going to deploy. Open the project's ``build networkMap "O=Controller,L=London,C=GB" node { name "O=Controller,L=London,C=GB" - advertisedServices = ["corda.notary.validating"] + notary = [validating : true] + advertisedServices = [] p2pPort 10002 rpcPort 10003 cordapps = ["net.corda:corda-finance:$corda_release_version"] diff --git a/docs/source/running-a-notary.rst b/docs/source/running-a-notary.rst index 32b62359a8..57ce2d0964 100644 --- a/docs/source/running-a-notary.rst +++ b/docs/source/running-a-notary.rst @@ -11,20 +11,20 @@ At present we have several prototype notary implementations: we are using the `Copycat `_ framework 4. ``RaftValidatingNotaryService`` (distributed) -- as above, but performs validation on the transactions received -To have a node run a notary service, you need to set appropriate configuration values before starting it +To have a node run a notary service, you need to set appropriate ``notary`` configuration before starting it (see :doc:`corda-configuration-file` for reference). -For ``SimpleNotaryService``, simply add the following service id to the list of advertised services: +For ``SimpleNotaryService`` the config is simply: .. parsed-literal:: - extraAdvertisedServiceIds : [ "net.corda.notary.simple" ] + notary : { validating : false } For ``ValidatingNotaryService``, it is: .. parsed-literal:: - extraAdvertisedServiceIds : [ "net.corda.notary.validating" ] + notary : { validating : true } Setting up a Raft notary is currently slightly more involved and is not recommended for prototyping purposes. There is work in progress to simplify it. To see it in action, however, you can try out the :ref:`notary-demo`. diff --git a/docs/source/tutorial-custom-notary.rst b/docs/source/tutorial-custom-notary.rst index 190d51533a..01e5da1b71 100644 --- a/docs/source/tutorial-custom-notary.rst +++ b/docs/source/tutorial-custom-notary.rst @@ -24,9 +24,3 @@ as ``ValidatingNotaryFlow``, ``NonValidatingNotaryFlow``, or implement your own :language: kotlin :start-after: START 2 :end-before: END 2 - -To ensure the custom notary is installed and advertised by the node, specify it in the configuration file: - -.. parsed-literal:: - - extraAdvertisedServiceIds : ["corda.notary.validating.mycustom"] diff --git a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java index d219c52281..7ecaa9fce9 100644 --- a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java +++ b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java @@ -1,9 +1,7 @@ package net.corda.cordform; import static java.util.Collections.emptyList; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import com.typesafe.config.ConfigValueFactory; +import com.typesafe.config.*; import java.util.Collections; import java.util.List; import java.util.Map; @@ -30,11 +28,6 @@ public class CordformNode implements NodeDefinition { */ public List advertisedServices = emptyList(); - /** - * If running a Raft notary cluster, the address of at least one node in the cluster, or leave blank to start a new cluster. - * If running a BFT notary cluster, the addresses of all nodes in the cluster. - */ - public List notaryClusterAddresses = emptyList(); /** * Set the RPC users for this node. This configuration block allows arbitrary configuration. * The recommended current structure is: @@ -45,6 +38,12 @@ public class CordformNode implements NodeDefinition { */ public List> rpcUsers = emptyList(); + /** + * Apply the notary configuration if this node is a notary. The map is the config structure of + * net.corda.node.services.config.NotaryConfig + */ + public Map notary = null; + protected Config config = ConfigFactory.empty(); public Config getConfig() { @@ -78,20 +77,4 @@ public class CordformNode implements NodeDefinition { public void rpcPort(Integer rpcPort) { config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + rpcPort)); } - - /** - * Set the port which to bind the Copycat (Raft) node to. - * - * @param notaryPort The Raft port. - */ - public void notaryNodePort(Integer notaryPort) { - config = config.withValue("notaryNodeAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + notaryPort)); - } - - /** - * @param id The (0-based) BFT replica ID. - */ - public void bftReplicaId(Integer id) { - config = config.withValue("bftSMaRt", ConfigValueFactory.fromMap(Collections.singletonMap("replicaId", id))); - } } diff --git a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy index 19e3a6b9b3..262f4df38c 100644 --- a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy +++ b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Node.groovy @@ -99,7 +99,7 @@ class Node extends CordformNode { } protected void build() { - configureRpcUsers() + configureProperties() installCordaJar() if (config.hasPath("webAddress")) { installWebserverJar() @@ -118,11 +118,12 @@ class Node extends CordformNode { return config.getString("p2pAddress") } - /** - * Write the RPC users to the config - */ - private void configureRpcUsers() { + private void configureProperties() { config = config.withValue("rpcUsers", ConfigValueFactory.fromIterable(rpcUsers)) + if (notary) { + config = config.withValue("notary", ConfigValueFactory.fromMap(notary)) + } + config = config.withValue('extraAdvertisedServiceIds', ConfigValueFactory.fromIterable(advertisedServices*.toString())) } /** @@ -177,11 +178,6 @@ class Node extends CordformNode { * Installs the configuration file to this node's directory and detokenises it. */ private void installConfig() { - // Adding required default values - config = config.withValue('extraAdvertisedServiceIds', ConfigValueFactory.fromIterable(advertisedServices*.toString())) - if (notaryClusterAddresses.size() > 0) { - config = config.withValue('notaryClusterAddresses', ConfigValueFactory.fromIterable(notaryClusterAddresses*.toString())) - } def configFileText = config.root().render(new ConfigRenderOptions(false, false, true, false)).split("\n").toList() // Need to write a temporary file first to use the project.copy, which resolves directories correctly. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/User.kt b/node-api/src/main/kotlin/net/corda/nodeapi/User.kt index 0c0d259ab2..aa86e64414 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/User.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/User.kt @@ -1,6 +1,7 @@ package net.corda.nodeapi import net.corda.nodeapi.config.OldConfig +import net.corda.nodeapi.config.toConfig data class User( @OldConfig("user") @@ -8,9 +9,6 @@ data class User( val password: String, val permissions: Set) { override fun toString(): String = "${javaClass.simpleName}($username, permissions=$permissions)" - fun toMap() = mapOf( - "username" to username, - "password" to password, - "permissions" to permissions - ) + @Deprecated("Use toConfig().root().unwrapped() instead") + fun toMap(): Map = toConfig().root().unwrapped() } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt index 1463229b13..78283ea654 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt @@ -2,18 +2,23 @@ package net.corda.nodeapi.config import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigUtil +import com.typesafe.config.ConfigValueFactory import net.corda.core.identity.CordaX500Name import net.corda.core.internal.noneOrSingle import net.corda.core.internal.uncheckedCast import net.corda.core.utilities.NetworkHostAndPort import org.slf4j.LoggerFactory +import java.lang.reflect.Field +import java.lang.reflect.ParameterizedType import java.net.Proxy import java.net.URL import java.nio.file.Path import java.nio.file.Paths import java.time.Instant import java.time.LocalDate +import java.time.temporal.Temporal import java.util.* import kotlin.reflect.KClass import kotlin.reflect.KProperty @@ -71,8 +76,8 @@ private fun Config.getSingleValue(path: String, type: KType): Any? { NetworkHostAndPort::class -> NetworkHostAndPort.parse(getString(path)) Path::class -> Paths.get(getString(path)) URL::class -> URL(getString(path)) - Properties::class -> getConfig(path).toProperties() CordaX500Name::class -> CordaX500Name.parse(getString(path)) + Properties::class -> getConfig(path).toProperties() else -> if (typeClass.java.isEnum) { parseEnum(typeClass.java, getString(path)) } else { @@ -126,4 +131,65 @@ private fun parseEnum(enumType: Class<*>, name: String): Enum<*> = enumBridge> enumBridge(clazz: Class, name: String): T = java.lang.Enum.valueOf(clazz, name) +/** + * Convert the receiver object into a [Config]. This does the inverse action of [parseAs]. + */ +fun Any.toConfig(): Config = ConfigValueFactory.fromMap(toConfigMap()).toConfig() + +@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") +// Reflect over the fields of the receiver and generate a value Map that can use to create Config object. +private fun Any.toConfigMap(): Map { + val values = HashMap() + for (field in javaClass.declaredFields) { + if (field.isSynthetic) continue + field.isAccessible = true + val value = field.get(this) ?: continue + val configValue = if (value is String || value is Boolean || value is Number) { + // These types are supported by Config as use as is + value + } else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name || value is Path || value is URL) { + // These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs + value.toString() + } else if (value is Enum<*>) { + // Expicitly use the Enum's name in case the toString is overridden, which would make parsing problematic. + value.name + } else if (value is Properties) { + // For Properties we treat keys with . as nested configs + ConfigFactory.parseMap(uncheckedCast(value)).root() + } else if (value is Iterable<*>) { + value.toConfigIterable(field) + } else { + // Else this is a custom object recursed over + value.toConfigMap() + } + values[field.name] = configValue + } + return values +} + +// For Iterables figure out the type parameter and apply the same logic as above on the individual elements. +private fun Iterable<*>.toConfigIterable(field: Field): Iterable { + val elementType = (field.genericType as ParameterizedType).actualTypeArguments[0] as Class<*> + return when (elementType) { + // For the types already supported by Config we can use the Iterable as is + String::class.java -> this + Integer::class.java -> this + java.lang.Long::class.java -> this + java.lang.Double::class.java -> this + java.lang.Boolean::class.java -> this + LocalDate::class.java -> map(Any?::toString) + Instant::class.java -> map(Any?::toString) + NetworkHostAndPort::class.java -> map(Any?::toString) + Path::class.java -> map(Any?::toString) + URL::class.java -> map(Any?::toString) + CordaX500Name::class.java -> map(Any?::toString) + Properties::class.java -> map { ConfigFactory.parseMap(uncheckedCast(it)).root() } + else -> if (elementType.isEnum) { + map { (it as Enum<*>).name } + } else { + map { it?.toConfigMap() } + } + } +} + private val logger = LoggerFactory.getLogger("net.corda.nodeapi.config") diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/config/ConfigParsingTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/config/ConfigParsingTest.kt index e13c47ce05..8737ac9160 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/config/ConfigParsingTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/config/ConfigParsingTest.kt @@ -76,54 +76,66 @@ class ConfigParsingTest { testPropertyType(URL("http://localhost:1234"), URL("http://localhost:1235"), valuesToString = true) } + @Test + fun CordaX500Name() { + testPropertyType( + CordaX500Name(organisation = "Mock Party", locality = "London", country = "GB"), + CordaX500Name(organisation = "Mock Party 2", locality = "London", country = "GB"), + valuesToString = true) + } + @Test fun `flat Properties`() { val config = config("value" to mapOf("key" to "prop")) - assertThat(config.parseAs().value).isEqualTo(Properties().apply { this["key"] = "prop" }) + val data = PropertiesData(Properties().apply { this["key"] = "prop" }) + assertThat(config.parseAs()).isEqualTo(data) + assertThat(data.toConfig()).isEqualTo(config) } @Test fun `Properties key with dot`() { val config = config("value" to mapOf("key.key2" to "prop")) - assertThat(config.parseAs().value).isEqualTo(Properties().apply { this["key.key2"] = "prop" }) + val data = PropertiesData(Properties().apply { this["key.key2"] = "prop" }) + assertThat(config.parseAs().value).isEqualTo(data.value) } @Test fun `nested Properties`() { val config = config("value" to mapOf("first" to mapOf("second" to "prop"))) - assertThat(config.parseAs().value).isEqualTo(Properties().apply { this["first.second"] = "prop" }) + val data = PropertiesData(Properties().apply { this["first.second"] = "prop" }) + assertThat(config.parseAs().value).isEqualTo(data.value) + assertThat(data.toConfig()).isEqualTo(config) } @Test fun `List of Properties`() { val config = config("values" to listOf(emptyMap(), mapOf("key" to "prop"))) - assertThat(config.parseAs().values).containsExactly( + val data = PropertiesListData(listOf( Properties(), - Properties().apply { this["key"] = "prop" }) + Properties().apply { this["key"] = "prop" })) + assertThat(config.parseAs().values).isEqualTo(data.values) + assertThat(data.toConfig()).isEqualTo(config) } @Test fun `Set`() { - val config = config("values" to listOf("a", "a", "b")) - assertThat(config.parseAs().values).containsOnly("a", "b") + val data = StringSetData(setOf("a", "b")) + assertThat(config("values" to listOf("a", "a", "b")).parseAs()).isEqualTo(data) + assertThat(data.toConfig()).isEqualTo(config("values" to listOf("a", "b"))) assertThat(empty().parseAs().values).isEmpty() } - @Test - fun x500Name() { - testPropertyType(CordaX500Name(organisation = "Mock Party", locality = "London", country = "GB"), CordaX500Name(organisation = "Mock Party 2", locality = "London", country = "GB"), valuesToString = true) - } - @Test fun `multi property data class`() { - val data = config( + val config = config( "b" to true, "i" to 123, "l" to listOf("a", "b")) - .parseAs() + val data = config.parseAs() assertThat(data.i).isEqualTo(123) assertThat(data.b).isTrue() assertThat(data.l).containsExactly("a", "b") + assertThat(data.toConfig()).isEqualTo(config) } @Test @@ -133,6 +145,7 @@ class ConfigParsingTest { "value" to "nested")) val data = NestedData(StringData("nested")) assertThat(config.parseAs()).isEqualTo(data) + assertThat(data.toConfig()).isEqualTo(config) } @Test @@ -143,12 +156,14 @@ class ConfigParsingTest { mapOf("value" to "2"))) val data = DataListData(listOf(StringData("1"), StringData("2"))) assertThat(config.parseAs()).isEqualTo(data) + assertThat(data.toConfig()).isEqualTo(config) } @Test fun `default value property`() { assertThat(config("a" to 3).parseAs()).isEqualTo(DefaultData(3, 2)) assertThat(config("a" to 3, "defaultOfTwo" to 3).parseAs()).isEqualTo(DefaultData(3, 3)) + assertThat(DefaultData(3).toConfig()).isEqualTo(config("a" to 3, "defaultOfTwo" to 2)) } @Test @@ -156,12 +171,14 @@ class ConfigParsingTest { assertThat(empty().parseAs().nullable).isNull() assertThat(config("nullable" to null).parseAs().nullable).isNull() assertThat(config("nullable" to "not null").parseAs().nullable).isEqualTo("not null") + assertThat(NullableData(null).toConfig()).isEqualTo(empty()) } @Test fun `old config property`() { assertThat(config("oldValue" to "old").parseAs().newValue).isEqualTo("old") assertThat(config("newValue" to "new").parseAs().newValue).isEqualTo("new") + assertThat(OldData("old").toConfig()).isEqualTo(config("newValue" to "old")) } private inline fun , reified L : ListData, V : Any> testPropertyType( @@ -177,6 +194,7 @@ class ConfigParsingTest { val config = config("value" to if (valueToString) value.toString() else value) val data = constructor.call(value) assertThat(config.parseAs().value).isEqualTo(data.value) + assertThat(data.toConfig()).isEqualTo(config) } private inline fun , V : Any> testListProperty(value1: V, value2: V, valuesToString: Boolean) { @@ -187,6 +205,7 @@ class ConfigParsingTest { val config = config("values" to configValues.take(n)) val data = constructor.call(rawValues.take(n)) assertThat(config.parseAs().values).isEqualTo(data.values) + assertThat(data.toConfig()).isEqualTo(config) } assertThat(empty().parseAs().values).isEmpty() } @@ -228,8 +247,8 @@ class ConfigParsingTest { data class PathListData(override val values: List) : ListData data class URLData(override val value: URL) : SingleData data class URLListData(override val values: List) : ListData - data class X500NameData(override val value: CordaX500Name) : SingleData - data class X500NameListData(override val values: List) : ListData + data class CordaX500NameData(override val value: CordaX500Name) : SingleData + data class CordaX500NameListData(override val values: List) : ListData data class PropertiesData(override val value: Properties) : SingleData data class PropertiesListData(override val values: List) : ListData data class MultiPropertyData(val i: Int, val b: Boolean, val l: List) @@ -242,5 +261,4 @@ class ConfigParsingTest { val newValue: String) enum class TestEnum { Value1, Value2 } - } \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index e2d9648556..211f0e1f15 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -7,14 +7,14 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.startFlow import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.minutes import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User +import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver @@ -103,10 +103,10 @@ class NodePerformanceTests { @Test fun `self pay rate`() { driver(startNodesInProcess = true) { - val a = startNode( - rpcUsers = listOf(User("A", "A", setOf(startFlowPermission(), startFlowPermission()))), - advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)) - ).get() + val a = startNotaryNode( + DUMMY_NOTARY.name, + rpcUsers = listOf(User("A", "A", setOf(startFlowPermission(), startFlowPermission()))) + ).getOrThrow() a as NodeHandle.InProcess val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics) a.rpcClientToNode().use("A", "A") { connection -> diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index fd803ee9ef..bb177abfd9 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -18,9 +18,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.loggerFor import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_NOTARY import net.corda.testing.TestDependencyInjectionBase @@ -120,7 +118,7 @@ class AttachmentLoadingTests : TestDependencyInjectionBase() { val nodes = listOf( startNode(providedName = bankAName, rpcUsers = listOf(adminUser)), startNode(providedName = bankBName, rpcUsers = listOf(adminUser)), - startNode(providedName = notaryName, rpcUsers = listOf(adminUser), advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) + startNotaryNode(providedName = notaryName, rpcUsers = listOf(adminUser), validating = false) ).transpose().getOrThrow() // Wait for all nodes to start up. nodes.forEach { it.rpc.waitUntilNetworkReady().getOrThrow() } return nodes diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index c014788530..9a6a5c3112 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -10,6 +10,7 @@ import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryFlow import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party +import net.corda.core.internal.deleteIfExists import net.corda.core.internal.div import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder @@ -18,11 +19,11 @@ import net.corda.core.utilities.Try import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode import net.corda.node.services.config.BFTSMaRtConfiguration +import net.corda.node.services.config.NotaryConfig import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand @@ -30,14 +31,13 @@ import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Test -import java.nio.file.Files +import java.nio.file.Paths import kotlin.test.assertEquals import kotlin.test.assertTrue class BFTNotaryServiceTests { companion object { - private val serviceType = BFTNonValidatingNotaryService.type - private val clusterName = CordaX500Name(serviceType.id, "BFT", "Zurich", "CH") + private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH") } private val mockNet = MockNetwork() @@ -49,20 +49,17 @@ class BFTNotaryServiceTests { } private fun bftNotaryCluster(clusterSize: Int, exposeRaces: Boolean = false) { - Files.deleteIfExists("config" / "currentView") // XXX: Make config object warn if this exists? + (Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists? val replicaIds = (0 until clusterSize) ServiceIdentityGenerator.generateToDisk( replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) }, clusterName) - val bftNotaryService = ServiceInfo(serviceType, clusterName) - val notaryClusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) } + val clusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) } replicaIds.forEach { replicaId -> - mockNet.createNode( - advertisedServices = bftNotaryService, - configOverrides = { - whenever(it.bftSMaRt).thenReturn(BFTSMaRtConfiguration(replicaId, false, exposeRaces)) - whenever(it.notaryClusterAddresses).thenReturn(notaryClusterAddresses) - }) + mockNet.createNode(configOverrides = { + val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces)) + whenever(it.notary).thenReturn(notary) + }) } mockNet.runNetwork() // Exchange initial network map registration messages. } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt index 69915138dd..06ed089e16 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt @@ -39,10 +39,9 @@ class DistributedServiceTests : DriverBasedTest() { ) val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)) val notariesFuture = startNotaryCluster( - DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.type.id), + DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.id), rpcUsers = listOf(testUser), - clusterSize = clusterSize, - type = RaftValidatingNotaryService.type + clusterSize = clusterSize ) alice = aliceFuture.get() diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index c81b86195c..0b5d72d858 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -14,8 +14,6 @@ import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.testing.* -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.node.NodeBasedTest import org.junit.After @@ -26,7 +24,7 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith class RaftNotaryServiceTests : NodeBasedTest() { - private val notaryName = CordaX500Name(RaftValidatingNotaryService.type.id, "RAFT Notary Service", "London", "GB") + private val notaryName = CordaX500Name(RaftValidatingNotaryService.id, "RAFT Notary Service", "London", "GB") @Before fun setup() { diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 786e5c0af8..5503d5ca19 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -17,9 +17,6 @@ import net.corda.core.utilities.seconds import net.corda.node.internal.StartedNode import net.corda.node.services.messaging.* import net.corda.node.services.transactions.RaftValidatingNotaryService -import net.corda.node.services.transactions.SimpleNotaryService -import net.corda.node.utilities.ServiceIdentityGenerator -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.node.NodeBasedTest import org.assertj.core.api.Assertions.assertThat @@ -32,8 +29,7 @@ import java.util.concurrent.atomic.AtomicInteger class P2PMessagingTest : NodeBasedTest() { private companion object { - val DISTRIBUTED_SERVICE_NAME = CordaX500Name(RaftValidatingNotaryService.type.id, "DistributedService", "London", "GB") - val SERVICE_2_NAME = CordaX500Name(organisation = "Service 2", locality = "London", country = "GB") + val DISTRIBUTED_SERVICE_NAME = CordaX500Name(RaftValidatingNotaryService.id, "DistributedService", "London", "GB") } @Test @@ -49,46 +45,6 @@ class P2PMessagingTest : NodeBasedTest() { startNodes().getOrThrow(timeout = startUpDuration * 3) } - // https://github.com/corda/corda/issues/71 - @Test - fun `communicating with a service running on the network map node`() { - startNetworkMapNode(advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) - networkMapNode.respondWith("Hello") - val alice = startNode(ALICE.name).getOrThrow() - val serviceAddress = alice.services.networkMapCache.run { - val notaryParty = notaryIdentities.randomOrNull()!! - alice.network.getAddressOfParty(getPartyInfo(notaryParty)!!) - } - val received = alice.receiveFrom(serviceAddress).getOrThrow(10.seconds) - assertThat(received).isEqualTo("Hello") - } - - // TODO Use a dummy distributed service - @Test - fun `communicating with a distributed service which the network map node is part of`() { - ServiceIdentityGenerator.generateToDisk( - listOf(DUMMY_MAP.name, SERVICE_2_NAME).map { baseDirectory(it) }, - DISTRIBUTED_SERVICE_NAME) - - val distributedService = ServiceInfo(RaftValidatingNotaryService.type, DISTRIBUTED_SERVICE_NAME) - val notaryClusterAddress = freeLocalHostAndPort() - startNetworkMapNode( - DUMMY_MAP.name, - advertisedServices = setOf(distributedService), - configOverrides = mapOf("notaryNodeAddress" to notaryClusterAddress.toString())) - val (serviceNode2, alice) = listOf( - startNode( - SERVICE_2_NAME, - advertisedServices = setOf(distributedService), - configOverrides = mapOf( - "notaryNodeAddress" to freeLocalHostAndPort().toString(), - "notaryClusterAddresses" to listOf(notaryClusterAddress.toString()))), - startNode(ALICE.name) - ).transpose().getOrThrow() - - assertAllNodesAreUsed(listOf(networkMapNode, serviceNode2), DISTRIBUTED_SERVICE_NAME, alice) - } - @Ignore @Test fun `communicating with a distributed service which we're part of`() { diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index efd07f1a55..0e8f062c9c 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -18,8 +18,6 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow import net.corda.node.services.FlowPermissions -import net.corda.node.services.transactions.SimpleNotaryService -import net.corda.nodeapi.internal.ServiceInfo import net.corda.nodeapi.User import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity @@ -38,8 +36,7 @@ class NodeStatePersistenceTests { val user = User("mark", "dadada", setOf(FlowPermissions.startFlowPermission())) val message = Message("Hello world!") driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) { - - startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))).getOrThrow() + startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow() var nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() val nodeName = nodeHandle.nodeInfo.chooseIdentity().name nodeHandle.rpcClientToNode().start(user.username, user.password).use { 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 ba3f1d06b6..8e9d219d66 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -1,7 +1,6 @@ package net.corda.node.internal import com.codahale.metrics.MetricRegistry -import com.google.common.collect.Lists import com.google.common.collect.MutableClassToInstanceMap import com.google.common.util.concurrent.MoreExecutors import net.corda.confidential.SwapIdentitiesFlow @@ -41,7 +40,9 @@ import net.corda.node.services.ContractUpgradeHandler import net.corda.node.services.FinalityHandler import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.api.* +import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.config.NodeConfiguration +import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.events.NodeSchedulerService import net.corda.node.services.events.ScheduledActivityObserver @@ -66,7 +67,6 @@ import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.utilities.* import net.corda.node.utilities.AddOrRemove.ADD import net.corda.nodeapi.internal.ServiceInfo -import net.corda.nodeapi.internal.ServiceType import org.apache.activemq.artemis.utils.ReusableLatch import org.slf4j.Logger import rx.Observable @@ -456,7 +456,7 @@ abstract class AbstractNode(config: NodeConfiguration, _services = ServiceHubInternalImpl(schemaService) attachments = NodeAttachmentService(services.monitoringService.metrics) cordappProvider.start(attachments) - legalIdentity = obtainIdentity() + legalIdentity = obtainIdentity(notaryConfig = null) network = makeMessagingService(legalIdentity) info = makeInfo(legalIdentity) val networkMapCache = services.networkMapCache @@ -500,19 +500,10 @@ abstract class AbstractNode(config: NodeConfiguration, } /** - * A service entry contains the advertised [ServiceInfo] along with the service identity. The identity *name* is - * taken from the configuration or, if non specified, generated by combining the node's legal name and the service id. - * Used only for notary identities. + * Obtain the node's notary identity if it's configured to be one. If part of a distributed notary then this will be + * the distributed identity shared across all the nodes of the cluster. */ - protected open fun getNotaryIdentity(): PartyAndCertificate? { - return advertisedServices.singleOrNull { it.type.isNotary() }?.let { - it.name?.let { - require(it.commonName != null) {"Common name in '$it' must not be null for notary service, use service type id as common name."} - require(ServiceType.parse(it.commonName!!).isNotary()) {"Common name for notary service in '$it' must be the notary service type id."} - } - obtainIdentity(it) - } - } + protected fun getNotaryIdentity(): PartyAndCertificate? = configuration.notary?.let { obtainIdentity(it) } @VisibleForTesting protected open fun acceptableLiveFiberCountOnStop(): Int = 0 @@ -558,22 +549,14 @@ abstract class AbstractNode(config: NodeConfiguration, } private fun makeNetworkServices(network: MessagingService, networkMapCache: NetworkMapCacheInternal, tokenizableServices: MutableList) { - val serviceTypes = advertisedServices.map { it.type } inNodeNetworkMapService = if (configuration.networkMapService == null) makeNetworkMapService(network, networkMapCache) else NullNetworkMapService - val notaryServiceType = serviceTypes.singleOrNull { it.isNotary() } - if (notaryServiceType != null) { - val service = makeCoreNotaryService(notaryServiceType) - if (service != null) { - service.apply { - tokenizableServices.add(this) - runOnStop += this::stop - start() - } - installCoreFlow(NotaryFlow.Client::class, service::createServiceFlow) - } else { - log.info("Notary type ${notaryServiceType.id} does not match any built-in notary types. " + - "It is expected to be loaded via a CorDapp") - } + configuration.notary?.let { + val notaryService = makeCoreNotaryService(it) + tokenizableServices.add(notaryService) + runOnStop += notaryService::stop + installCoreFlow(NotaryFlow.Client::class, notaryService::createServiceFlow) + log.info("Running core notary: ${notaryService.javaClass.name}") + notaryService.start() } } @@ -640,15 +623,33 @@ abstract class AbstractNode(config: NodeConfiguration, abstract protected fun makeNetworkMapService(network: MessagingService, networkMapCache: NetworkMapCacheInternal): NetworkMapService - open protected fun makeCoreNotaryService(type: ServiceType): NotaryService? { - check(myNotaryIdentity != null) { "No notary identity initialized when creating a notary service" } - return when (type) { - SimpleNotaryService.type -> SimpleNotaryService(services, myNotaryIdentity!!.owningKey) - ValidatingNotaryService.type -> ValidatingNotaryService(services, myNotaryIdentity!!.owningKey) - RaftNonValidatingNotaryService.type -> RaftNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey) - RaftValidatingNotaryService.type -> RaftValidatingNotaryService(services, myNotaryIdentity!!.owningKey) - BFTNonValidatingNotaryService.type -> BFTNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey) - else -> null + private fun makeCoreNotaryService(notaryConfig: NotaryConfig): NotaryService { + val notaryKey = myNotaryIdentity?.owningKey ?: throw IllegalArgumentException("No notary identity initialized when creating a notary service") + return if (notaryConfig.validating) { + if (notaryConfig.raft != null) { + RaftValidatingNotaryService(services, notaryKey, notaryConfig.raft) + } else if (notaryConfig.bftSMaRt != null) { + throw IllegalArgumentException("Validating BFTSMaRt notary not supported") + } else { + ValidatingNotaryService(services, notaryKey) + } + } else { + if (notaryConfig.raft != null) { + RaftNonValidatingNotaryService(services, notaryKey, notaryConfig.raft) + } else if (notaryConfig.bftSMaRt != null) { + val cluster = makeBFTCluster(notaryKey, notaryConfig.bftSMaRt) + BFTNonValidatingNotaryService(services, notaryKey, notaryConfig.bftSMaRt, cluster) + } else { + SimpleNotaryService(services, notaryKey) + } + } + } + + protected open fun makeBFTCluster(notaryKey: PublicKey, bftSMaRtConfig: BFTSMaRtConfiguration): BFTSMaRt.Cluster { + return object : BFTSMaRt.Cluster { + override fun waitUntilAllReplicasHaveInitialized() { + log.warn("A BFT replica may still be initializing, in which case the upcoming consensus change may cause it to spin.") + } } } @@ -691,29 +692,32 @@ abstract class AbstractNode(config: NodeConfiguration, protected abstract fun startMessagingService(rpcOps: RPCOps) - private fun obtainIdentity(serviceInfo: ServiceInfo? = null): PartyAndCertificate { - // Load the private identity key, creating it if necessary. The identity key is a long term well known key that - // is distributed to other peers and we use it (or a key signed by it) when we need to do something - // "permissioned". The identity file is what gets distributed and contains the node's legal name along with - // the public key. Obviously in a real system this would need to be a certificate chain of some kind to ensure - // the legal name is actually validated in some way. + private fun obtainIdentity(notaryConfig: NotaryConfig?): PartyAndCertificate { val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) - val (id, name) = if (serviceInfo == null) { - // Create node identity if service info = null + val (id, singleName) = if (notaryConfig == null) { + // Node's main identity Pair("identity", myLegalName) } else { - val name = serviceInfo.name ?: myLegalName.copy(commonName = serviceInfo.type.id) - Pair(serviceInfo.type.id, name) + val notaryId = notaryConfig.run { NotaryService.constructId(validating, raft != null, bftSMaRt != null) } + if (notaryConfig.bftSMaRt == null && notaryConfig.raft == null) { + // Node's notary identity + Pair(notaryId, myLegalName.copy(commonName = notaryId)) + } else { + // The node is part of a distributed notary whose identity must already be generated beforehand + Pair(notaryId, null) + } } // TODO: Integrate with Key management service? val privateKeyAlias = "$id-private-key" if (!keyStore.containsAlias(privateKeyAlias)) { + singleName ?: throw IllegalArgumentException( + "Unable to find in the key store the identity of the distributed notary ($id) the node is part of") // TODO: Remove use of [ServiceIdentityGenerator.generateToDisk]. log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!") - keyStore.signAndSaveNewKeyPair(name, privateKeyAlias, generateKeyPair()) + keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair()) } val (x509Cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias) @@ -726,7 +730,7 @@ abstract class AbstractNode(config: NodeConfiguration, // We have to create the certificate chain for the composite key manually, this is because we don't have a keystore // provider that understand compositeKey-privateKey combo. The cert chain is created using the composite key certificate + // the tail of the private key certificates, as they are both signed by the same certificate chain. - Lists.asList(certificate, keyStore.getCertificateChain(privateKeyAlias).drop(1).toTypedArray()) + listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1) } else { keyStore.getCertificateChain(privateKeyAlias).let { check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" } @@ -736,8 +740,11 @@ abstract class AbstractNode(config: NodeConfiguration, val nodeCert = certificates[0] as? X509Certificate ?: throw ConfigurationException("Node certificate must be an X.509 certificate") val subject = CordaX500Name.build(nodeCert.subjectX500Principal) - if (subject != name) - throw ConfigurationException("The name '$name' for $id doesn't match what's in the key store: $subject") + // TODO Include the name of the distributed notary, which the node is part of, in the notary config so that we + // can cross-check the identity we get from the key store + if (singleName != null && subject != singleName) { + throw ConfigurationException("The name '$singleName' for $id doesn't match what's in the key store: $subject") + } partyKeys += keys return PartyAndCertificate(CertificateFactory.getInstance("X509").generateCertPath(certificates)) diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 6413daa3ee..631361cd04 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -99,7 +99,7 @@ open class NodeStartup(val args: Array) { return } val startedNode = node.start() - printPluginsAndServices(startedNode.internals) + Node.printBasicNodeInfo("Loaded CorDapps", startedNode.internals.cordappProvider.cordapps.joinToString { it.name }) startedNode.internals.nodeReadyFuture.thenMatch({ val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0 val name = startedNode.info.legalIdentitiesAndCerts.first().name.organisation @@ -165,7 +165,7 @@ open class NodeStartup(val args: Array) { } open protected fun banJavaSerialisation(conf: FullNodeConfiguration) { - SerialFilter.install(if (conf.bftSMaRt.isValid()) ::bftSMaRtSerialFilter else ::defaultSerialFilter) + SerialFilter.install(if (conf.notary?.bftSMaRt != null) ::bftSMaRtSerialFilter else ::defaultSerialFilter) } open protected fun getVersionInfo(): VersionInfo { @@ -263,13 +263,6 @@ open class NodeStartup(val args: Array) { } } - private fun printPluginsAndServices(node: Node) { - node.configuration.extraAdvertisedServiceIds.filter { it.startsWith("corda.notary.") }.let { - if (it.isNotEmpty()) Node.printBasicNodeInfo("Providing additional services", it.joinToString()) - } - Node.printBasicNodeInfo("Loaded CorDapps", node.cordappProvider.cordapps.joinToString { it.name }) - } - open fun drawBanner(versionInfo: VersionInfo) { // This line makes sure ANSI escapes work on Windows, where they aren't supported out of the box. AnsiConsole.systemInstall() diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index ba865ecaba..b15e768f19 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -6,17 +6,11 @@ import net.corda.node.internal.NetworkMapInfo import net.corda.node.services.messaging.CertificateChainCheckPolicy import net.corda.nodeapi.User import net.corda.nodeapi.config.NodeSSLConfiguration -import net.corda.nodeapi.config.OldConfig import net.corda.nodeapi.internal.ServiceInfo import java.net.URL import java.nio.file.Path import java.util.* -/** @param exposeRaces for testing only, so its default is not in reference.conf but here. */ -data class BFTSMaRtConfiguration(val replicaId: Int, val debug: Boolean, val exposeRaces: Boolean = false) { - fun isValid() = replicaId >= 0 -} - interface NodeConfiguration : NodeSSLConfiguration { // myLegalName should be only used in the initial network registration, we should use the name from the certificate instead of this. // TODO: Remove this so we don't accidentally use this identity in the code? @@ -37,17 +31,31 @@ interface NodeConfiguration : NodeSSLConfiguration { val certificateChainCheckPolicies: List val verifierType: VerifierType val messageRedeliveryDelaySeconds: Int - val bftSMaRt: BFTSMaRtConfiguration - val notaryNodeAddress: NetworkHostAndPort? - val notaryClusterAddresses: List + val notary: NotaryConfig? val activeMQServer: ActiveMqServerConfiguration } -data class BridgeConfiguration( - val retryIntervalMs: Long, - val maxRetryIntervalMin: Long, - val retryIntervalMultiplier: Double -) +data class NotaryConfig(val validating: Boolean, val raft: RaftConfig? = null, val bftSMaRt: BFTSMaRtConfiguration? = null) { + init { + require(raft == null || bftSMaRt == null) { "raft and bftSMaRt configs cannot be specified together" } + } +} + +data class RaftConfig(val nodeAddress: NetworkHostAndPort, val clusterAddresses: List) + +/** @param exposeRaces for testing only, so its default is not in reference.conf but here. */ +data class BFTSMaRtConfiguration constructor(val replicaId: Int, + val clusterAddresses: List, + val debug: Boolean = false, + val exposeRaces: Boolean = false) { + init { + require(replicaId >= 0) { "replicaId cannot be negative" } + } +} + +data class BridgeConfiguration(val retryIntervalMs: Long, + val maxRetryIntervalMin: Long, + val retryIntervalMultiplier: Double) data class ActiveMqServerConfiguration(val bridge: BridgeConfiguration) @@ -67,16 +75,13 @@ data class FullNodeConfiguration( override val verifierType: VerifierType, override val messageRedeliveryDelaySeconds: Int = 30, val useHTTPS: Boolean, - @OldConfig("artemisAddress") val p2pAddress: NetworkHostAndPort, val rpcAddress: NetworkHostAndPort?, // TODO This field is slightly redundant as p2pAddress is sufficient to hold the address of the node's MQ broker. // Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one val messagingServerAddress: NetworkHostAndPort?, val extraAdvertisedServiceIds: List, - override val bftSMaRt: BFTSMaRtConfiguration, - override val notaryNodeAddress: NetworkHostAndPort?, - override val notaryClusterAddresses: List, + override val notary: NotaryConfig?, override val certificateChainCheckPolicies: List, override val devMode: Boolean = false, val useTestClock: Boolean = false, diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index 8b27a9a396..322a2c9299 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -13,6 +13,7 @@ import net.corda.core.messaging.DataFeed import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache.MapChange +import net.corda.core.node.services.NotaryService import net.corda.core.node.services.PartyInfo import net.corda.core.schemas.NodeInfoSchemaV1 import net.corda.core.serialization.SingletonSerializeAsToken @@ -81,10 +82,8 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) // Notary certificates have to be signed by the doorman directly it.legalIdentities } - .filter { - it.name.toString().contains("corda.notary", true) - } - .distinct() // Distinct, because of distributed service nodes + .filter { it.name.commonName?.startsWith(NotaryService.ID_PREFIX) ?: false } + .toSet() // Distinct, because of distributed service nodes .sortedBy { it.name.toString() } } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt index 807bcc9415..8ea6e6af09 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt @@ -20,10 +20,12 @@ import net.corda.core.serialization.serialize import net.corda.core.transactions.FilteredTransaction import net.corda.core.utilities.* import net.corda.node.services.api.ServiceHubInternal +import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.NODE_DATABASE_PREFIX import java.security.PublicKey import javax.persistence.Entity +import javax.persistence.Table import kotlin.concurrent.thread /** @@ -33,24 +35,19 @@ import kotlin.concurrent.thread */ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey, - cluster: BFTSMaRt.Cluster = distributedCluster) : NotaryService() { + private val bftSMaRtConfig: BFTSMaRtConfiguration, + cluster: BFTSMaRt.Cluster) : NotaryService() { companion object { - val type = SimpleNotaryService.type.getSubType("bft") + val id = constructId(validating = false, bft = true) private val log = loggerFor() - private val distributedCluster = object : BFTSMaRt.Cluster { - override fun waitUntilAllReplicasHaveInitialized() { - log.warn("A replica may still be initializing, in which case the upcoming consensus change may cause it to spin.") - } - } } private val client: BFTSMaRt.Client private val replicaHolder = SettableFuture.create() init { - require(services.configuration.bftSMaRt.isValid()) { "bftSMaRt replicaId must be specified in the configuration" } - client = BFTSMaRtConfig(services.configuration.notaryClusterAddresses, services.configuration.bftSMaRt.debug, services.configuration.bftSMaRt.exposeRaces).use { - val replicaId = services.configuration.bftSMaRt.replicaId + client = BFTSMaRtConfig(bftSMaRtConfig.clusterAddresses, bftSMaRtConfig.debug, bftSMaRtConfig.exposeRaces).use { + val replicaId = bftSMaRtConfig.replicaId val configHandle = it.handle() // Replica startup must be in parallel with other replicas, otherwise the constructor may not return: thread(name = "BFT SMaRt replica $replicaId init", isDaemon = true) { @@ -66,7 +63,7 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, } fun waitUntilReplicaHasInitialized() { - log.debug { "Waiting for replica ${services.configuration.bftSMaRt.replicaId} to initialize." } + log.debug { "Waiting for replica ${bftSMaRtConfig.replicaId} to initialize." } replicaHolder.getOrThrow() // It's enough to wait for the ServiceReplica constructor to return. } @@ -96,36 +93,37 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, } @Entity - @javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}bft_smart_notary_committed_states") + @Table(name = "${NODE_DATABASE_PREFIX}bft_smart_notary_committed_states") class PersistedCommittedState(id: PersistentStateRef, consumingTxHash: String, consumingIndex: Int, party: PersistentUniquenessProvider.PersistentParty) : PersistentUniquenessProvider.PersistentUniqueness(id, consumingTxHash, consumingIndex, party) - fun createMap(): AppendOnlyPersistentMap = - AppendOnlyPersistentMap( - toPersistentEntityKey = { PersistentStateRef(it.txhash.toString(), it.index) }, - fromPersistentEntity = { - //TODO null check will become obsolete after making DB/JPA columns not nullable - val txId = it.id.txId ?: throw IllegalStateException("DB returned null SecureHash transactionId") - val index = it.id.index ?: throw IllegalStateException("DB returned null SecureHash index") - Pair(StateRef(txhash = SecureHash.parse(txId), index = index), + private fun createMap(): AppendOnlyPersistentMap { + return AppendOnlyPersistentMap( + toPersistentEntityKey = { PersistentStateRef(it.txhash.toString(), it.index) }, + fromPersistentEntity = { + //TODO null check will become obsolete after making DB/JPA columns not nullable + val txId = it.id.txId ?: throw IllegalStateException("DB returned null SecureHash transactionId") + val index = it.id.index ?: throw IllegalStateException("DB returned null SecureHash index") + Pair(StateRef(txhash = SecureHash.parse(txId), index = index), UniquenessProvider.ConsumingTx( id = SecureHash.parse(it.consumingTxHash), inputIndex = it.consumingIndex, requestingParty = Party( name = CordaX500Name.parse(it.party.name), owningKey = parsePublicKeyBase58(it.party.owningKey)))) - }, - toPersistentEntity = { (txHash, index) : StateRef, (id, inputIndex, requestingParty): UniquenessProvider.ConsumingTx -> - PersistedCommittedState( - id = PersistentStateRef(txHash.toString(), index), - consumingTxHash = id.toString(), - consumingIndex = inputIndex, - party = PersistentUniquenessProvider.PersistentParty(requestingParty.name.toString(), - requestingParty.owningKey.toBase58String()) - ) - }, - persistentEntityClass = PersistedCommittedState::class.java - ) + }, + toPersistentEntity = { (txHash, index) : StateRef, (id, inputIndex, requestingParty): UniquenessProvider.ConsumingTx -> + PersistedCommittedState( + id = PersistentStateRef(txHash.toString(), index), + consumingTxHash = id.toString(), + consumingIndex = inputIndex, + party = PersistentUniquenessProvider.PersistentParty(requestingParty.name.toString(), + requestingParty.owningKey.toBase58String()) + ) + }, + persistentEntityClass = PersistedCommittedState::class.java + ) + } private class Replica(config: BFTSMaRtConfig, replicaId: Int, diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt index 6e0916e3cc..797f9aa7de 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt @@ -5,18 +5,23 @@ import net.corda.core.flows.NotaryFlow import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.node.services.api.ServiceHubInternal +import net.corda.node.services.config.RaftConfig import java.security.PublicKey /** A non-validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */ -class RaftNonValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { +class RaftNonValidatingNotaryService(override val services: ServiceHubInternal, + override val notaryIdentityKey: PublicKey, + raftConfig: RaftConfig) : TrustedAuthorityNotaryService() { companion object { - val type = SimpleNotaryService.type.getSubType("raft") + val id = constructId(validating = false, raft = true) } override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock) - override val uniquenessProvider: RaftUniquenessProvider = RaftUniquenessProvider(services) + override val uniquenessProvider: RaftUniquenessProvider = RaftUniquenessProvider(services, raftConfig) - override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = NonValidatingNotaryFlow(otherPartySession, this) + override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service { + return NonValidatingNotaryFlow(otherPartySession, this) + } override fun start() { uniquenessProvider.start() diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt index ed349f1cc6..93daf5a23a 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt @@ -26,16 +26,14 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.loggerFor import net.corda.node.services.api.ServiceHubInternal +import net.corda.node.services.config.RaftConfig import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.CordaPersistence import net.corda.nodeapi.config.SSLConfiguration import java.nio.file.Path import java.util.concurrent.CompletableFuture import javax.annotation.concurrent.ThreadSafe -import javax.persistence.Column -import javax.persistence.Entity -import javax.persistence.Id -import javax.persistence.Lob +import javax.persistence.* /** * A uniqueness provider that records committed input states in a distributed collection replicated and @@ -46,7 +44,7 @@ import javax.persistence.Lob * to the cluster leader to be actioned. */ @ThreadSafe -class RaftUniquenessProvider(private val services: ServiceHubInternal) : UniquenessProvider, SingletonSerializeAsToken() { +class RaftUniquenessProvider(private val services: ServiceHubInternal, private val raftConfig: RaftConfig) : UniquenessProvider, SingletonSerializeAsToken() { companion object { private val log = loggerFor() @@ -67,7 +65,7 @@ class RaftUniquenessProvider(private val services: ServiceHubInternal) : Uniquen } @Entity - @javax.persistence.Table(name = "notary_committed_states") + @Table(name = "notary_committed_states") class RaftState( @Id @Column @@ -81,13 +79,6 @@ class RaftUniquenessProvider(private val services: ServiceHubInternal) : Uniquen /** Directory storing the Raft log and state machine snapshots */ private val storagePath: Path = services.configuration.baseDirectory /** Address of the Copycat node run by this Corda node */ - private val myAddress = services.configuration.notaryNodeAddress - ?: throw IllegalArgumentException("notaryNodeAddress must be specified in configuration") - /** - * List of node addresses in the existing Copycat cluster. At least one active node must be - * provided to join the cluster. If empty, a new cluster will be bootstrapped. - */ - private val clusterAddresses = services.configuration.notaryClusterAddresses /** The database to store the state machine state in */ private val db: CordaPersistence = services.database /** SSL configuration */ @@ -96,7 +87,6 @@ class RaftUniquenessProvider(private val services: ServiceHubInternal) : Uniquen private lateinit var _clientFuture: CompletableFuture private lateinit var server: CopycatServer - /** * Copycat clients are responsible for connecting to the cluster and submitting commands and queries that operate * on the cluster's replicated state machine. @@ -108,7 +98,7 @@ class RaftUniquenessProvider(private val services: ServiceHubInternal) : Uniquen log.info("Creating Copycat server, log stored in: ${storagePath.toFile()}") val stateMachineFactory = { DistributedImmutableMap(db, RaftUniquenessProvider.Companion::createMap) } - val address = Address(myAddress.host, myAddress.port) + val address = raftConfig.nodeAddress.let { Address(it.host, it.port) } val storage = buildStorage(storagePath) val transport = buildTransport(transportConfiguration) val serializer = Serializer().apply { @@ -142,9 +132,9 @@ class RaftUniquenessProvider(private val services: ServiceHubInternal) : Uniquen .withSerializer(serializer) .build() - val serverFuture = if (clusterAddresses.isNotEmpty()) { - log.info("Joining an existing Copycat cluster at $clusterAddresses") - val cluster = clusterAddresses.map { Address(it.host, it.port) } + val serverFuture = if (raftConfig.clusterAddresses.isNotEmpty()) { + log.info("Joining an existing Copycat cluster at ${raftConfig.clusterAddresses}") + val cluster = raftConfig.clusterAddresses.map { Address(it.host, it.port) } server.join(cluster) } else { log.info("Bootstrapping a Copycat cluster at $address") diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt index 10da5581fe..4af9e2be74 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt @@ -5,18 +5,23 @@ import net.corda.core.flows.NotaryFlow import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.node.services.api.ServiceHubInternal +import net.corda.node.services.config.RaftConfig import java.security.PublicKey /** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */ -class RaftValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { +class RaftValidatingNotaryService(override val services: ServiceHubInternal, + override val notaryIdentityKey: PublicKey, + raftConfig: RaftConfig) : TrustedAuthorityNotaryService() { companion object { - val type = ValidatingNotaryService.type.getSubType("raft") + val id = constructId(validating = true, raft = true) } override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock) - override val uniquenessProvider: RaftUniquenessProvider = RaftUniquenessProvider(services) + override val uniquenessProvider: RaftUniquenessProvider = RaftUniquenessProvider(services, raftConfig) - override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = ValidatingNotaryFlow(otherPartySession, this) + override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service { + return ValidatingNotaryFlow(otherPartySession, this) + } override fun start() { uniquenessProvider.start() diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt index e62bcecb85..cb4401cae5 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt @@ -4,16 +4,11 @@ import net.corda.core.flows.FlowSession import net.corda.core.flows.NotaryFlow import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService -import net.corda.nodeapi.internal.ServiceType import net.corda.node.services.api.ServiceHubInternal import java.security.PublicKey /** A simple Notary service that does not perform transaction validation */ class SimpleNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { - companion object { - val type = ServiceType.notary.getSubType("simple") - } - override val timeWindowChecker = TimeWindowChecker(services.clock) override val uniquenessProvider = PersistentUniquenessProvider() diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt index c13b9186f1..6c7e36046b 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt @@ -4,16 +4,14 @@ import net.corda.core.flows.FlowSession import net.corda.core.flows.NotaryFlow import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService -import net.corda.nodeapi.internal.ServiceType import net.corda.node.services.api.ServiceHubInternal import java.security.PublicKey /** A Notary service that validates the transaction chain of the submitted transaction before committing it */ class ValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { companion object { - val type = ServiceType.notary.getSubType("validating") + val id = constructId(validating = true) } - override val timeWindowChecker = TimeWindowChecker(services.clock) override val uniquenessProvider = PersistentUniquenessProvider() diff --git a/node/src/main/resources/reference.conf b/node/src/main/resources/reference.conf index 37a91c5e5e..37ff984da6 100644 --- a/node/src/main/resources/reference.conf +++ b/node/src/main/resources/reference.conf @@ -19,10 +19,6 @@ useHTTPS = false h2port = 0 useTestClock = false verifierType = InMemory -bftSMaRt = { - replicaId = -1 - debug = false -} activeMQServer = { bridge = { retryIntervalMs = 5000 diff --git a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt b/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt index a4bfc6d12b..dbb62d3bac 100644 --- a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt +++ b/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt @@ -33,7 +33,7 @@ class CordappSmokeTest { p2pPort = port.andIncrement, rpcPort = port.andIncrement, webPort = port.andIncrement, - extraServices = emptyList(), + isNotary = false, users = listOf(user) ) diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index ce3f6f3a6b..a33dadfcea 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -28,9 +28,7 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT import net.corda.node.services.messaging.RpcContext -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode @@ -68,7 +66,7 @@ class CordaRPCOpsImplTest { mockNet = MockNetwork() aliceNode = mockNet.createNode() - notaryNode = mockNet.createNode(advertisedServices = ServiceInfo(SimpleNotaryService.type)) + notaryNode = mockNet.createNotaryNode(validating = false) rpc = CordaRPCOpsImpl(aliceNode.services, aliceNode.smm, aliceNode.database) CURRENT_RPC_CONTEXT.set(RpcContext(User("user", "pwd", permissions = setOf( startFlowPermission(), diff --git a/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt b/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt index bbe685017a..1a08a04e76 100644 --- a/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/CordaServiceTest.kt @@ -13,8 +13,6 @@ import net.corda.core.utilities.ProgressTracker import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.cordapp.DummyRPCFlow -import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.DUMMY_NOTARY import net.corda.testing.node.MockNetwork import net.corda.testing.setCordappPackages @@ -23,7 +21,10 @@ import org.junit.After import org.junit.Before import org.junit.Test import java.util.concurrent.atomic.AtomicInteger -import kotlin.test.* +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue @StartableByService class DummyServiceFlow : FlowLogic() { @@ -86,9 +87,7 @@ class CordaServiceTest { fun start() { setCordappPackages("net.corda.node.internal","net.corda.finance") mockNet = MockNetwork(threadPerNode = true) - notaryNode = mockNet.createNode( - legalName = DUMMY_NOTARY.name, - advertisedServices = *arrayOf(ServiceInfo(ValidatingNotaryService.type))) + notaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name, validating = true) nodeA = mockNet.createNode() mockNet.startNodes() } diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index 4bb4b6aa62..a758655ff8 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -12,8 +12,6 @@ import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.internal.StartedNode -import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork @@ -39,12 +37,10 @@ class NotaryChangeTests { fun setUp() { setCordappPackages("net.corda.testing.contracts") mockNet = MockNetwork() - oldNotaryNode = mockNet.createNode( - legalName = DUMMY_NOTARY.name, - advertisedServices = *arrayOf(ServiceInfo(ValidatingNotaryService.type))) + oldNotaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name) clientNodeA = mockNet.createNode() clientNodeB = mockNet.createNode() - newNotaryNode = mockNet.createNode(advertisedServices = ServiceInfo(ValidatingNotaryService.type)) + newNotaryNode = mockNet.createNotaryNode() mockNet.runNetwork() // Clear network map registration messages oldNotaryNode.internals.ensureRegistered() newNotaryParty = newNotaryNode.info.legalIdentities[1] diff --git a/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt index b4cce7d46f..2935053699 100644 --- a/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt @@ -30,9 +30,7 @@ class FullNodeConfigurationTest { rpcAddress = NetworkHostAndPort("localhost", 1), messagingServerAddress = null, extraAdvertisedServiceIds = emptyList(), - bftSMaRt = BFTSMaRtConfiguration(-1, false), - notaryNodeAddress = null, - notaryClusterAddresses = emptyList(), + notary = null, certificateChainCheckPolicies = emptyList(), devMode = true, activeMQServer = ActiveMqServerConfiguration(BridgeConfiguration(0, 0, 0.0))) diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index 399f58f196..bb7c0d3e92 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -16,8 +16,6 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode import net.corda.node.services.statemachine.StateMachineManager -import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork @@ -95,9 +93,7 @@ class ScheduledFlowTests { fun setup() { setCordappPackages("net.corda.testing.contracts") mockNet = MockNetwork(threadPerNode = true) - notaryNode = mockNet.createNode( - legalName = DUMMY_NOTARY.name, - advertisedServices = *arrayOf(ServiceInfo(ValidatingNotaryService.type))) + notaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name) val a = mockNet.createUnstartedNode() val b = mockNet.createUnstartedNode() diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 34f5e256a5..42b504a65e 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -6,10 +6,8 @@ import co.paralleluniverse.strands.concurrent.Semaphore import net.corda.core.concurrent.CordaFuture import net.corda.core.contracts.ContractState import net.corda.core.contracts.StateAndRef -import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.random63BitValue import net.corda.core.flows.* -import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.concurrent.flatMap import net.corda.core.internal.concurrent.map @@ -21,23 +19,16 @@ import net.corda.core.serialization.serialize import net.corda.core.toFuture import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder -import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker.Change import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap -import net.corda.finance.DOLLARS -import net.corda.finance.flows.CashIssueFlow -import net.corda.finance.flows.CashPaymentFlow import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode import net.corda.node.services.persistence.checkpoints -import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState -import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork @@ -69,10 +60,8 @@ class FlowFrameworkTests { private val receivedSessionMessages = ArrayList() private lateinit var node1: StartedNode private lateinit var node2: StartedNode - private lateinit var notary1: StartedNode - private lateinit var notary2: StartedNode - private lateinit var notary1Identity: Party - private lateinit var notary2Identity: Party + private lateinit var notary: StartedNode + private lateinit var notaryIdentity: Party @Before fun start() { @@ -85,19 +74,13 @@ class FlowFrameworkTests { node1.internals.ensureRegistered() // We intentionally create our own notary and ignore the one provided by the network - val notaryKeyPair = generateKeyPair() - val notaryService = ServiceInfo(ValidatingNotaryService.type, CordaX500Name(commonName = ValidatingNotaryService.type.id, organisation = "Notary service 2000", locality = "London", country = "GB")) - val notaryIdentityOverride = Pair(notaryService, notaryKeyPair) // Note that these notaries don't operate correctly as they don't share their state. They are only used for testing // service addressing. - notary1 = mockNet.createNotaryNode(notaryIdentity = notaryIdentityOverride, serviceName = notaryService.name) - notary2 = mockNet.createNotaryNode(notaryIdentity = notaryIdentityOverride, serviceName = notaryService.name) + notary = mockNet.createNotaryNode() receivedSessionMessagesObservable().forEach { receivedSessionMessages += it } mockNet.runNetwork() - - notary1Identity = notary1.services.myInfo.legalIdentities[1] - notary2Identity = notary2.services.myInfo.legalIdentities[1] + notaryIdentity = notary.services.myInfo.legalIdentities[1] } @After @@ -335,62 +318,6 @@ class FlowFrameworkTests { ) } - @Test - fun `different notaries are picked when addressing shared notary identity`() { - assertEquals(notary1Identity, notary2Identity) - assertThat(node1.services.networkMapCache.notaryIdentities.size == 1) - node1.services.startFlow(CashIssueFlow( - 2000.DOLLARS, - OpaqueBytes.of(0x01), - notary1Identity)).resultFuture.getOrThrow() - // We pay a couple of times, the notary picking should go round robin - for (i in 1..3) { - val flow = node1.services.startFlow(CashPaymentFlow(500.DOLLARS, node2.info.chooseIdentity())) - mockNet.runNetwork() - flow.resultFuture.getOrThrow() - } - val endpoint = mockNet.messagingNetwork.endpoint(notary1.network.myAddress as InMemoryMessagingNetwork.PeerHandle)!! - val party1Info = notary1.services.networkMapCache.getPartyInfo(notary1Identity)!! - assertTrue(party1Info is PartyInfo.DistributedNode) - val notary1Address: MessageRecipients = endpoint.getAddressOfParty(notary1.services.networkMapCache.getPartyInfo(notary1Identity)!!) - assertThat(notary1Address).isInstanceOf(InMemoryMessagingNetwork.ServiceHandle::class.java) - assertEquals(notary1Address, endpoint.getAddressOfParty(notary2.services.networkMapCache.getPartyInfo(notary2Identity)!!)) - receivedSessionMessages.expectEvents(isStrict = false) { - sequence( - // First Pay - expect(match = { it.message is SessionInit && it.message.initiatingFlowClass == NotaryFlow.Client::class.java.name }) { - it.message as SessionInit - assertEquals(node1.internals.id, it.from) - assertEquals(notary1Address, it.to) - }, - expect(match = { it.message is SessionConfirm }) { - it.message as SessionConfirm - assertEquals(notary1.internals.id, it.from) - }, - // Second pay - expect(match = { it.message is SessionInit && it.message.initiatingFlowClass == NotaryFlow.Client::class.java.name }) { - it.message as SessionInit - assertEquals(node1.internals.id, it.from) - assertEquals(notary1Address, it.to) - }, - expect(match = { it.message is SessionConfirm }) { - it.message as SessionConfirm - assertEquals(notary2.internals.id, it.from) - }, - // Third pay - expect(match = { it.message is SessionInit && it.message.initiatingFlowClass == NotaryFlow.Client::class.java.name }) { - it.message as SessionInit - assertEquals(node1.internals.id, it.from) - assertEquals(notary1Address, it.to) - }, - expect(match = { it.message is SessionConfirm }) { - it.message as SessionConfirm - assertEquals(it.from, notary1.internals.id) - } - ) - } - } - @Test fun `other side ends before doing expected send`() { node2.registerFlowFactory(ReceiveFlow::class) { NoOpFlow() } @@ -604,7 +531,7 @@ class FlowFrameworkTests { @Test fun `wait for transaction`() { - val ptx = TransactionBuilder(notary = notary1Identity) + val ptx = TransactionBuilder(notary = notaryIdentity) .addOutputState(DummyState(), DummyContract.PROGRAM_ID) .addCommand(dummyCommand(node1.info.chooseIdentity().owningKey)) val stx = node1.services.signInitialTransaction(ptx) @@ -619,7 +546,7 @@ class FlowFrameworkTests { @Test fun `committer throws exception before calling the finality flow`() { - val ptx = TransactionBuilder(notary = notary1Identity) + val ptx = TransactionBuilder(notary = notaryIdentity) .addOutputState(DummyState(), DummyContract.PROGRAM_ID) .addCommand(dummyCommand()) val stx = node1.services.signInitialTransaction(ptx) @@ -636,7 +563,7 @@ class FlowFrameworkTests { @Test fun `verify vault query service is tokenizable by force checkpointing within a flow`() { - val ptx = TransactionBuilder(notary = notary1Identity) + val ptx = TransactionBuilder(notary = notaryIdentity) .addOutputState(DummyState(), DummyContract.PROGRAM_ID) .addCommand(dummyCommand(node1.info.chooseIdentity().owningKey)) val stx = node1.services.signInitialTransaction(ptx) diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index e395ce739e..579f653018 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -13,7 +13,6 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.internal.StartedNode -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork @@ -36,9 +35,7 @@ class NotaryServiceTests { fun setup() { setCordappPackages("net.corda.testing.contracts") mockNet = MockNetwork() - notaryNode = mockNet.createNode( - legalName = DUMMY_NOTARY.name, - advertisedServices = *arrayOf(ServiceInfo(SimpleNotaryService.type))) + notaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name, validating = false) clientNode = mockNet.createNode() mockNet.runNetwork() // Clear network map registration messages notaryNode.internals.ensureRegistered() diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt index fb4741e373..87410fecb8 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt @@ -14,7 +14,6 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode import net.corda.node.services.issueInvalidState -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork @@ -36,10 +35,7 @@ class ValidatingNotaryServiceTests { fun setup() { setCordappPackages("net.corda.testing.contracts") mockNet = MockNetwork() - notaryNode = mockNet.createNode( - legalName = DUMMY_NOTARY.name, - advertisedServices = *arrayOf(ServiceInfo(ValidatingNotaryService.type)) - ) + notaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name) clientNode = mockNet.createNode() mockNet.runNetwork() // Clear network map registration messages notaryNode.internals.ensureRegistered() diff --git a/samples/attachment-demo/build.gradle b/samples/attachment-demo/build.gradle index 1ca4d85d3d..379fcf6af8 100644 --- a/samples/attachment-demo/build.gradle +++ b/samples/attachment-demo/build.gradle @@ -40,7 +40,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { networkMap "O=Notary Service,L=Zurich,C=CH" node { name "O=Notary Service,L=Zurich,C=CH" - advertisedServices["corda.notary.validating"] + notary = [validating : true] + advertisedServices = [] p2pPort 10002 rpcPort 10003 cordapps = [] diff --git a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt index 3f3458997b..d0cdb64932 100644 --- a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt +++ b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt @@ -2,9 +2,7 @@ package net.corda.attachmentdemo import net.corda.core.utilities.getOrThrow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_NOTARY @@ -22,7 +20,7 @@ class AttachmentDemoTest { val (nodeA, nodeB) = listOf( startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser, maximumHeapSize = "1g"), startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser, maximumHeapSize = "1g"), - startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)))) + startNotaryNode(DUMMY_NOTARY.name, validating = false)) .map { it.getOrThrow() } startWebserver(nodeB).getOrThrow() diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt index b331dbacc7..f63d1c68a7 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt @@ -1,12 +1,10 @@ package net.corda.attachmentdemo import net.corda.core.internal.div -import net.corda.nodeapi.internal.ServiceInfo +import net.corda.nodeapi.User import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_NOTARY -import net.corda.node.services.transactions.SimpleNotaryService -import net.corda.nodeapi.User import net.corda.testing.driver.driver /** @@ -16,7 +14,7 @@ import net.corda.testing.driver.driver fun main(args: Array) { val demoUser = listOf(User("demo", "demo", setOf("StartFlow.net.corda.flows.FinalityFlow"))) driver(isDebug = true, driverDirectory = "build" / "attachment-demo-nodes") { - startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) + startNotaryNode(DUMMY_NOTARY.name, validating = false) startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser) startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser) waitForAllNodesToFinish() diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle index dcdbe408d0..c673a10da1 100644 --- a/samples/bank-of-corda-demo/build.gradle +++ b/samples/bank-of-corda-demo/build.gradle @@ -53,7 +53,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { networkMap "O=Notary Service,L=Zurich,C=CH" node { name "O=Notary Service,L=Zurich,C=CH" - advertisedServices = ["corda.notary.validating"] + notary = [validating : true] + advertisedServices = [] p2pPort 10002 rpcPort 10003 cordapps = ["net.corda:finance:$corda_release_version"] diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt index 22e33c41b9..8077105e41 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt @@ -3,8 +3,6 @@ package net.corda.bank import net.corda.bank.api.BankOfCordaClientApi import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams import net.corda.core.utilities.getOrThrow -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.BOC import net.corda.testing.driver.driver import net.corda.testing.notary @@ -16,7 +14,7 @@ class BankOfCordaHttpAPITest { fun `issuer flow via Http`() { driver(extraCordappPackagesToScan = listOf("net.corda.finance"), dsl = { val bigCorpNodeFuture = startNode(providedName = BIGCORP_LEGAL_NAME) - val nodeBankOfCordaFuture = startNode(providedName = BOC.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) + val nodeBankOfCordaFuture = startNotaryNode(BOC.name, validating = false) val (nodeBankOfCorda) = listOf(nodeBankOfCordaFuture, bigCorpNodeFuture).map { it.getOrThrow() } val nodeBankOfCordaApiAddr = startWebserver(nodeBankOfCorda).getOrThrow().listenAddress val notaryName = notary().node.nodeInfo.legalIdentities[1].name diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index 157b2c0fd6..d379e3b98a 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -8,8 +8,6 @@ import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.driver @@ -22,7 +20,7 @@ class BankOfCordaRPCClientTest { val bocManager = User("bocManager", "password1", permissions = setOf( startFlowPermission())) val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet()) - val nodeBankOfCordaFuture = startNode(providedName = BOC.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)), rpcUsers = listOf(bocManager)) + val nodeBankOfCordaFuture = startNotaryNode(BOC.name, rpcUsers = listOf(bocManager), validating = false) val nodeBigCorporationFuture = startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpCFO)) val (nodeBankOfCorda, nodeBigCorporation) = listOf(nodeBankOfCordaFuture, nodeBigCorporationFuture).map { it.getOrThrow() } diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt index e1b75e3f1b..ac2e890f09 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt @@ -9,8 +9,7 @@ import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.node.services.transactions.SimpleNotaryService +import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.BOC import net.corda.testing.DUMMY_NOTARY @@ -55,7 +54,7 @@ private class BankOfCordaDriver { val role = options.valueOf(roleArg)!! val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME, - "1", BOC.name, DUMMY_NOTARY.name.copy(commonName = "corda.notary.validating")) + "1", BOC.name, DUMMY_NOTARY.name.copy(commonName = ValidatingNotaryService.id)) try { when (role) { @@ -70,8 +69,7 @@ private class BankOfCordaDriver { val bigCorpUser = User(BIGCORP_USERNAME, "test", permissions = setOf( startFlowPermission())) - startNode(providedName = DUMMY_NOTARY.name, - advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) + startNotaryNode(DUMMY_NOTARY.name, validating = false) val bankOfCorda = startNode( providedName = BOC.name, rpcUsers = listOf(bankUser)) diff --git a/samples/irs-demo/build.gradle b/samples/irs-demo/build.gradle index 6d9b908823..bc482b26d7 100644 --- a/samples/irs-demo/build.gradle +++ b/samples/irs-demo/build.gradle @@ -53,7 +53,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { networkMap "O=Notary Service,L=Zurich,C=CH" node { name "O=Notary Service,L=Zurich,C=CH" - advertisedServices = ["corda.notary.validating", "corda.interest_rates"] + notary = [validating : true] + advertisedServices = ["corda.interest_rates"] p2pPort 10002 rpcPort 10003 webPort 10004 diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index 4d3c92b7dc..a598595f0b 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -21,9 +21,7 @@ import net.corda.finance.plugin.registerFinanceJSONMappers import net.corda.irs.contract.InterestRateSwap import net.corda.irs.utilities.uploadFile import net.corda.node.services.config.FullNodeConfiguration -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.driver.driver import net.corda.testing.http.HttpApi @@ -50,9 +48,7 @@ class IRSDemoTest : IntegrationTestCategory { fun `runs IRS demo`() { driver(useTestClock = true, isDebug = true) { val (controller, nodeA, nodeB) = listOf( - startNode( - providedName = DUMMY_NOTARY.name, - advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))), + startNotaryNode(DUMMY_NOTARY.name, validating = false), startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(rpcUser)), startNode(providedName = DUMMY_BANK_B.name)) .map { it.getOrThrow() } diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt index 6cd3425e58..d1e770b257 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt @@ -1,8 +1,6 @@ package net.corda.irs import net.corda.core.utilities.getOrThrow -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_NOTARY @@ -15,9 +13,7 @@ import net.corda.testing.driver.driver fun main(args: Array) { driver(dsl = { val (controller, nodeA, nodeB) = listOf( - startNode( - providedName = DUMMY_NOTARY.name, - advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))), + startNotaryNode(DUMMY_NOTARY.name, validating = false), startNode(providedName = DUMMY_BANK_A.name), startNode(providedName = DUMMY_BANK_B.name)) .map { it.getOrThrow() } diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt index ea09fa73c0..46a38d7f5e 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -11,18 +11,13 @@ import net.corda.irs.api.NodeInterestRates import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.statemachine.StateMachineManager -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.internal.ServiceInfo import net.corda.nodeapi.internal.ServiceType -import net.corda.testing.DUMMY_MAP -import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.DUMMY_REGULATOR +import net.corda.testing.* import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.MockNetwork import net.corda.testing.node.TestClock import net.corda.testing.node.setTo -import net.corda.testing.setCordappPackages -import net.corda.testing.testNodeConfiguration import rx.Observable import rx.subjects.PublishSubject import java.math.BigInteger @@ -103,10 +98,11 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, advertisedServices: Set, id: Int, notaryIdentity: Pair?, entropyRoot: BigInteger): SimulatedNode { - require(advertisedServices.containsType(SimpleNotaryService.type)) + requireNotNull(config.notary) val cfg = testNodeConfiguration( baseDirectory = config.baseDirectory, - myLegalName = DUMMY_NOTARY.name) + myLegalName = DUMMY_NOTARY.name, + notaryConfig = config.notary) return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, notaryIdentity, entropyRoot) } } @@ -153,7 +149,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, val mockNet = MockNetwork(networkSendManuallyPumped, runAsync) // This one must come first. val networkMap = mockNet.startNetworkMapNode(nodeFactory = NetworkMapNodeFactory) - val notary = mockNet.createNode(nodeFactory = NotaryNodeFactory, advertisedServices = ServiceInfo(SimpleNotaryService.type)) + val notary = mockNet.createNotaryNode(validating = false, nodeFactory = NotaryNodeFactory) val regulators = listOf(mockNet.createUnstartedNode(nodeFactory = RegulatorFactory)) val ratesOracle = mockNet.createUnstartedNode(nodeFactory = RatesOracleFactory) diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index 87c12cd663..477adecd20 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -5,10 +5,9 @@ import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div -import net.corda.core.internal.stream -import net.corda.core.internal.toTypedArray import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.internal.ServiceInfo +import net.corda.node.services.config.BFTSMaRtConfiguration +import net.corda.node.services.config.NotaryConfig import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator @@ -24,9 +23,7 @@ private val notaryNames = createNotaryNames(clusterSize) // This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO // NOT use this as a design to copy. object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].toString()) { - private val serviceType = BFTNonValidatingNotaryService.type - private val clusterName = CordaX500Name(serviceType.id, "BFT", "Zurich", "CH") - private val advertisedService = ServiceInfo(serviceType, clusterName) + private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH") init { node { @@ -40,12 +37,10 @@ object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", not p2pPort(10005) rpcPort(10006) } - val clusterAddresses = (0 until clusterSize).stream().mapToObj { NetworkHostAndPort("localhost", 11000 + it * 10) }.toTypedArray() + val clusterAddresses = (0 until clusterSize).map { NetworkHostAndPort("localhost", 11000 + it * 10) } fun notaryNode(replicaId: Int, configure: CordformNode.() -> Unit) = node { name(notaryNames[replicaId]) - advertisedServices(advertisedService) - notaryClusterAddresses(*clusterAddresses) - bftReplicaId(replicaId) + notary(NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses))) configure() } notaryNode(0) { diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index 2aa043364a..1d78c51266 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -6,7 +6,8 @@ import net.corda.cordform.CordformNode import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.internal.ServiceInfo +import net.corda.node.services.config.NotaryConfig +import net.corda.node.services.config.RaftConfig import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.ALICE @@ -22,9 +23,7 @@ private val notaryNames = createNotaryNames(3) // This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO // NOT use this as a design to copy. object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].toString()) { - private val serviceType = RaftValidatingNotaryService.type - private val clusterName = CordaX500Name(serviceType.id, "Raft", "Zurich", "CH") - private val advertisedService = ServiceInfo(serviceType, clusterName) + private val clusterName = CordaX500Name(RaftValidatingNotaryService.id, "Raft", "Zurich", "CH") init { node { @@ -38,28 +37,23 @@ object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", no p2pPort(10005) rpcPort(10006) } - fun notaryNode(index: Int, configure: CordformNode.() -> Unit) = node { + fun notaryNode(index: Int, nodePort: Int, clusterPort: Int? = null, configure: CordformNode.() -> Unit) = node { name(notaryNames[index]) - advertisedServices(advertisedService) + val clusterAddresses = if (clusterPort != null ) listOf(NetworkHostAndPort("localhost", clusterPort)) else emptyList() + notary(NotaryConfig(validating = true, raft = RaftConfig(NetworkHostAndPort("localhost", nodePort), clusterAddresses))) configure() } - notaryNode(0) { - notaryNodePort(10008) + notaryNode(0, 10008) { p2pPort(10009) rpcPort(10010) } - val clusterAddress = NetworkHostAndPort("localhost", 10008) // Otherwise each notary forms its own cluster. - notaryNode(1) { - notaryNodePort(10012) + notaryNode(1, 10012, 10008) { p2pPort(10013) rpcPort(10014) - notaryClusterAddresses(clusterAddress) } - notaryNode(2) { - notaryNodePort(10016) + notaryNode(2, 10016, 10008) { p2pPort(10017) rpcPort(10018) - notaryClusterAddresses(clusterAddress) } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt index e9c45fd7e4..beec66a8fc 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt @@ -1,17 +1,16 @@ package net.corda.notarydemo +import net.corda.cordform.CordformContext +import net.corda.cordform.CordformDefinition import net.corda.core.internal.div -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.DUMMY_NOTARY import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.node.services.transactions.ValidatingNotaryService +import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.User import net.corda.notarydemo.flows.DummyIssueAndMove import net.corda.notarydemo.flows.RPCStartableNotaryFlowClient -import net.corda.cordform.CordformDefinition -import net.corda.cordform.CordformContext -import net.corda.nodeapi.internal.ServiceInfo +import net.corda.testing.ALICE +import net.corda.testing.BOB +import net.corda.testing.DUMMY_NOTARY import net.corda.testing.internal.demorun.* fun main(args: Array) = SingleNotaryCordform.runNodes() @@ -37,7 +36,7 @@ object SingleNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", name(DUMMY_NOTARY.name) p2pPort(10009) rpcPort(10010) - advertisedServices(ServiceInfo(ValidatingNotaryService.type)) + notary(NotaryConfig(validating = true)) } } diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index 278608bd77..ffce26eb4f 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -1,5 +1,3 @@ -import org.apache.tools.ant.filters.FixCrLfFilter - buildscript { ext.strata_version = '1.1.2' } @@ -68,7 +66,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { networkMap "O=Notary Service,L=Zurich,C=CH" node { name "O=Notary Service,L=Zurich,C=CH" - advertisedServices = ["corda.notary.validating"] + notary = [validating : true] + advertisedServices = [] p2pPort 10002 cordapps = ["net.corda:finance:$corda_release_version"] } diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index a81b898920..e113f235b7 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -3,16 +3,9 @@ package net.corda.vega import com.opengamma.strata.product.common.BuySell import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.getOrThrow -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.node.services.transactions.SimpleNotaryService -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_BANK_B -import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.IntegrationTestCategory +import net.corda.testing.* import net.corda.testing.driver.driver import net.corda.testing.http.HttpApi -import net.corda.testing.setCordappPackages -import net.corda.testing.unsetCordappPackages import net.corda.vega.api.PortfolioApi import net.corda.vega.api.PortfolioApiUtils import net.corda.vega.api.SwapDataModel @@ -46,7 +39,7 @@ class SimmValuationTest : IntegrationTestCategory { @Test fun `runs SIMM valuation demo`() { driver(isDebug = true) { - startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))).getOrThrow() + startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow() val nodeAFuture = startNode(providedName = nodeALegalName) val nodeBFuture = startNode(providedName = nodeBLegalName) val (nodeA, nodeB) = listOf(nodeAFuture, nodeBFuture).map { it.getOrThrow() } diff --git a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt index ee855f6ffc..9cc72b5af0 100644 --- a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt +++ b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt @@ -1,12 +1,10 @@ package net.corda.vega import net.corda.core.utilities.getOrThrow -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_BANK_C import net.corda.testing.DUMMY_NOTARY -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.driver.driver /** @@ -16,7 +14,7 @@ import net.corda.testing.driver.driver */ fun main(args: Array) { driver(dsl = { - val notaryFuture = startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) + val notaryFuture = startNotaryNode(DUMMY_NOTARY.name, validating = false) val nodeAFuture = startNode(providedName = DUMMY_BANK_A.name) val nodeBFuture = startNode(providedName = DUMMY_BANK_B.name) val nodeCFuture = startNode(providedName = DUMMY_BANK_C.name) diff --git a/samples/trader-demo/build.gradle b/samples/trader-demo/build.gradle index 5d2a1750eb..5d6f861e8a 100644 --- a/samples/trader-demo/build.gradle +++ b/samples/trader-demo/build.gradle @@ -54,7 +54,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { networkMap "O=Notary Service,L=Zurich,C=CH" node { name "O=Notary Service,L=Zurich,C=CH" - advertisedServices = ["corda.notary.validating"] + notary = [validating : true] + advertisedServices = [] p2pPort 10002 cordapps = ["net.corda:finance:$corda_release_version"] } diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index 93034badfe..0efa101ec7 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -9,9 +9,7 @@ import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.CommercialPaperSchemaV1 import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.driver.poll import net.corda.testing.node.NodeBasedTest @@ -43,7 +41,7 @@ class TraderDemoTest : NodeBasedTest() { startFlowPermission(), startFlowPermission(), startFlowPermission())) - val notaryFuture = startNode(DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) + val notaryFuture = startNotaryNode(DUMMY_NOTARY.name, validating = false) val nodeAFuture = startNode(DUMMY_BANK_A.name, rpcUsers = listOf(demoUser)) val nodeBFuture = startNode(DUMMY_BANK_B.name, rpcUsers = listOf(demoUser)) val bankNodeFuture = startNode(BOC.name, rpcUsers = listOf(bankUser)) diff --git a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt index 5b41ae9ec3..4a032576f5 100644 --- a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt @@ -3,8 +3,6 @@ package net.corda.traderdemo import net.corda.core.internal.div import net.corda.finance.flows.CashIssueFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.BOC import net.corda.testing.DUMMY_BANK_A @@ -27,7 +25,7 @@ fun main(args: Array) { val user = User("user1", "test", permissions = setOf(startFlowPermission(), startFlowPermission(), startFlowPermission())) - startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) + startNotaryNode(DUMMY_NOTARY.name, validating = false) startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser) startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser) startNode(providedName = BOC.name, rpcUsers = listOf(user)) diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt index f9fc3f3002..ae31e3bbb5 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt @@ -9,9 +9,7 @@ import net.corda.core.internal.read import net.corda.core.messaging.startFlow import net.corda.core.serialization.CordaSerializable import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.driver.driver import net.corda.testing.node.MockNetwork import org.junit.Ignore @@ -292,11 +290,7 @@ class FlowStackSnapshotTest { @Test fun `flowStackSnapshot object is serializable`() { val mockNet = MockNetwork(threadPerNode = true) - val notaryService = ServiceInfo(ValidatingNotaryService.type) - val notaryNode = mockNet.createNode( - legalName = DUMMY_NOTARY.name, - notaryIdentity = notaryService to DUMMY_NOTARY_KEY, - advertisedServices = *arrayOf(notaryService)) + mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name) val node = mockNet.createPartyNode() node.internals.registerInitiatedFlow(DummyFlow::class.java) node.services.startFlow(FlowStackSnapshotSerializationTestingFlow()).resultFuture.get() diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index 43260d5500..c9cf4ae4a0 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -5,12 +5,10 @@ import net.corda.core.internal.div import net.corda.core.internal.list import net.corda.core.internal.readLines import net.corda.core.utilities.getOrThrow +import net.corda.node.internal.NodeStartup import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_REGULATOR -import net.corda.node.internal.NodeStartup -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.ProjectStructure.projectRootDir import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -40,7 +38,7 @@ class DriverTests { @Test fun `simple node startup and shutdown`() { val handles = driver { - val notary = startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) + val notary = startNotaryNode(DUMMY_NOTARY.name, validating = false) val regulator = startNode(providedName = DUMMY_REGULATOR.name) listOf(nodeMustBeUp(notary), nodeMustBeUp(regulator)) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt index 398cc9262c..1a17d02f3a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt @@ -5,8 +5,6 @@ package net.corda.testing import net.corda.core.identity.Party import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.CordaRPCOps -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.driver.DriverDSLExposedInterface @@ -19,9 +17,15 @@ import net.corda.testing.driver.DriverDSLExposedInterface * node construction won't start until you access the members. You can get one of these from the * [alice], [bob] and [aliceBobAndNotary] functions. */ -class PredefinedTestNode internal constructor(party: Party, driver: DriverDSLExposedInterface, services: Set) { +class PredefinedTestNode internal constructor(party: Party, driver: DriverDSLExposedInterface, ifNotaryIsValidating: Boolean?) { val rpcUsers = listOf(User("admin", "admin", setOf("ALL"))) // TODO: Randomize? - val nodeFuture by lazy { driver.startNode(providedName = party.name, rpcUsers = rpcUsers, advertisedServices = services) } + val nodeFuture by lazy { + if (ifNotaryIsValidating != null) { + driver.startNotaryNode(providedName = party.name, rpcUsers = rpcUsers, validating = ifNotaryIsValidating) + } else { + driver.startNode(providedName = party.name, rpcUsers = rpcUsers) + } + } val node by lazy { nodeFuture.get()!! } val rpc by lazy { node.rpcClientToNode() } @@ -34,17 +38,17 @@ class PredefinedTestNode internal constructor(party: Party, driver: DriverDSLExp * Returns a plain, entirely stock node pre-configured with the [ALICE] identity. Note that a random key will be generated * for it: you won't have [ALICE_KEY]. */ -fun DriverDSLExposedInterface.alice(): PredefinedTestNode = PredefinedTestNode(ALICE, this, emptySet()) +fun DriverDSLExposedInterface.alice(): PredefinedTestNode = PredefinedTestNode(ALICE, this, null) /** * Returns a plain, entirely stock node pre-configured with the [BOB] identity. Note that a random key will be generated * for it: you won't have [BOB_KEY]. */ -fun DriverDSLExposedInterface.bob(): PredefinedTestNode = PredefinedTestNode(BOB, this, emptySet()) +fun DriverDSLExposedInterface.bob(): PredefinedTestNode = PredefinedTestNode(BOB, this, null) /** * Returns a plain single node notary pre-configured with the [DUMMY_NOTARY] identity. Note that a random key will be generated * for it: you won't have [DUMMY_NOTARY_KEY]. */ -fun DriverDSLExposedInterface.notary(): PredefinedTestNode = PredefinedTestNode(DUMMY_NOTARY, this, setOf(ServiceInfo(ValidatingNotaryService.type))) +fun DriverDSLExposedInterface.notary(): PredefinedTestNode = PredefinedTestNode(DUMMY_NOTARY, this, true) /** * Returns plain, entirely stock nodes pre-configured with the [ALICE], [BOB] and [DUMMY_NOTARY] X.500 names in that diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt index 088eaaf6f0..7f8069e919 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt @@ -8,8 +8,8 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.node.ServiceHub import net.corda.core.transactions.TransactionBuilder import net.corda.node.services.config.NodeConfiguration +import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.VerifierType -import net.corda.testing.node.MockCordappProvider import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties @@ -51,7 +51,8 @@ import java.nio.file.Path fun testNodeConfiguration( baseDirectory: Path, - myLegalName: CordaX500Name): NodeConfiguration { + myLegalName: CordaX500Name, + notaryConfig: NotaryConfig? = null): NodeConfiguration { abstract class MockableNodeConfiguration : NodeConfiguration // Otherwise Mockito is defeated by val getters. val nc = spy() whenever(nc.baseDirectory).thenReturn(baseDirectory) @@ -60,6 +61,7 @@ fun testNodeConfiguration( whenever(nc.keyStorePassword).thenReturn("cordacadevpass") whenever(nc.trustStorePassword).thenReturn("trustpass") whenever(nc.rpcUsers).thenReturn(emptyList()) + whenever(nc.notary).thenReturn(notaryConfig) whenever(nc.dataSourceProperties).thenReturn(makeTestDataSourceProperties(myLegalName.organisation)) whenever(nc.database).thenReturn(makeTestDatabaseProperties()) whenever(nc.emailAddress).thenReturn("") diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 344244bfa4..9382ce4b3a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -26,12 +26,11 @@ import net.corda.node.internal.NodeStartup import net.corda.node.internal.StartedNode import net.corda.node.services.config.* import net.corda.node.services.network.NetworkMapService -import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.nodeapi.User import net.corda.nodeapi.config.parseAs +import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.internal.ServiceInfo -import net.corda.nodeapi.internal.ServiceType import net.corda.nodeapi.internal.addShutdownHook import net.corda.testing.* import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO @@ -96,6 +95,14 @@ interface DriverDSLExposedInterface : CordformContext { startInSameProcess: Boolean? = defaultParameters.startInSameProcess, maximumHeapSize: String = defaultParameters.maximumHeapSize): CordaFuture + // TODO This method has been added temporarily, to be deleted once the set of notaries is defined at the network level. + fun startNotaryNode(providedName: CordaX500Name, + rpcUsers: List = emptyList(), + verifierType: VerifierType = VerifierType.InMemory, + customOverrides: Map = emptyMap(), + //TODO Switch the default value + validating: Boolean = true): CordaFuture + /** * Helper function for starting a [node] with custom parameters from Java. * @@ -118,7 +125,6 @@ interface DriverDSLExposedInterface : CordformContext { * * @param notaryName The legal name of the advertised distributed notary service. * @param clusterSize Number of nodes to create for the cluster. - * @param type The advertised notary service type. Currently the only supported type is [RaftValidatingNotaryService.type]. * @param verifierType The type of transaction verifier to use. See: [VerifierType] * @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list. * @param startInSameProcess Determines if the node should be started inside the same process the Driver is running @@ -128,7 +134,6 @@ interface DriverDSLExposedInterface : CordformContext { fun startNotaryCluster( notaryName: CordaX500Name, clusterSize: Int = 3, - type: ServiceType = RaftValidatingNotaryService.type, verifierType: VerifierType = VerifierType.InMemory, rpcUsers: List = emptyList(), startInSameProcess: Boolean? = null): CordaFuture>> @@ -668,9 +673,9 @@ class DriverDSL( } } is NetworkMapStartStrategy.Nominated -> { - serviceConfig(networkMapCandidates.filter { + serviceConfig(networkMapCandidates.single { it.name == legalName.toString() - }.single().config.getString("p2pAddress").let(NetworkHostAndPort.Companion::parse)).let { + }.config.getString("p2pAddress").let(NetworkHostAndPort.Companion::parse)).let { { nodeName: CordaX500Name -> if (nodeName == legalName) null else it } } } @@ -715,6 +720,15 @@ class DriverDSL( return startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize) } + override fun startNotaryNode(providedName: CordaX500Name, + rpcUsers: List, + verifierType: VerifierType, + customOverrides: Map, + validating: Boolean): CordaFuture { + val config = customOverrides + NotaryConfig(validating).toConfigMap() + return startNode(providedName = providedName, rpcUsers = rpcUsers, verifierType = verifierType, customOverrides = config) + } + override fun startNodes(nodes: List, startInSameProcess: Boolean?, maximumHeapSize: String): List> { val networkMapServiceConfigLookup = networkMapServiceConfigLookup(nodes) return nodes.map { node -> @@ -722,50 +736,62 @@ class DriverDSL( val webAddress = portAllocation.nextHostAndPort() val name = CordaX500Name.parse(node.name) val rpcUsers = node.rpcUsers + val notary = if (node.notary != null) mapOf("notary" to node.notary) else emptyMap() val config = ConfigHelper.loadConfig( baseDirectory = baseDirectory(name), allowMissingConfig = true, - configOverrides = node.config + mapOf( + configOverrides = node.config + notary + mapOf( "extraAdvertisedServiceIds" to node.advertisedServices, "networkMapService" to networkMapServiceConfigLookup(name), - "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers, - "notaryClusterAddresses" to node.notaryClusterAddresses + "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers ) ) startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize) } } + // TODO This mapping is done is several plaecs including the gradle plugin. In general we need a better way of + // generating the configs for the nodes, probably making use of Any.toConfig() + private fun NotaryConfig.toConfigMap(): Map = mapOf("notary" to toConfig().root().unwrapped()) + override fun startNotaryCluster( notaryName: CordaX500Name, clusterSize: Int, - type: ServiceType, verifierType: VerifierType, rpcUsers: List, startInSameProcess: Boolean? ): CordaFuture>> { + fun notaryConfig(nodeAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): Map { + val clusterAddresses = if (clusterAddress != null) listOf(clusterAddress) else emptyList() + val config = NotaryConfig(validating = true, raft = RaftConfig(nodeAddress = nodeAddress, clusterAddresses = clusterAddresses)) + return config.toConfigMap() + } + val nodeNames = (0 until clusterSize).map { CordaX500Name("Notary Service $it", "Zurich", "CH") } val paths = nodeNames.map { baseDirectory(it) } ServiceIdentityGenerator.generateToDisk(paths, notaryName) - val advertisedServices = setOf(ServiceInfo(type, notaryName)) - val notaryClusterAddress = portAllocation.nextHostAndPort() + val clusterAddress = portAllocation.nextHostAndPort() // Start the first node that will bootstrap the cluster val firstNotaryFuture = startNode( providedName = nodeNames.first(), - advertisedServices = advertisedServices, rpcUsers = rpcUsers, verifierType = verifierType, - customOverrides = mapOf("notaryNodeAddress" to notaryClusterAddress.toString(), - "database.serverNameTablePrefix" to if (nodeNames.isNotEmpty()) nodeNames.first().toString().replace(Regex("[^0-9A-Za-z]+"), "") else ""), + customOverrides = notaryConfig(clusterAddress) + mapOf( + "database.serverNameTablePrefix" to if (nodeNames.isNotEmpty()) nodeNames.first().toString().replace(Regex("[^0-9A-Za-z]+"), "") else "" + ), startInSameProcess = startInSameProcess ) // All other nodes will join the cluster val restNotaryFutures = nodeNames.drop(1).map { val nodeAddress = portAllocation.nextHostAndPort() - val configOverride = mapOf("notaryNodeAddress" to nodeAddress.toString(), "notaryClusterAddresses" to listOf(notaryClusterAddress.toString()), - "database.serverNameTablePrefix" to it.toString().replace(Regex("[^0-9A-Za-z]+"), "")) - startNode(providedName = it, advertisedServices = advertisedServices, rpcUsers = rpcUsers, verifierType = verifierType, customOverrides = configOverride) + startNode( + providedName = it, + rpcUsers = rpcUsers, + verifierType = verifierType, + customOverrides = notaryConfig(nodeAddress, clusterAddress) + mapOf( + "database.serverNameTablePrefix" to it.toString().replace(Regex("[^0-9A-Za-z]+"), "") + )) } return firstNotaryFuture.flatMap { firstNotary -> diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt index c7006d02f4..80f078d2bf 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt @@ -5,9 +5,10 @@ package net.corda.testing.internal.demorun import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode import net.corda.core.identity.CordaX500Name -import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.internal.ServiceInfo +import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.User +import net.corda.nodeapi.config.toConfig +import net.corda.nodeapi.internal.ServiceInfo fun CordformDefinition.node(configure: CordformNode.() -> Unit) { addNode { cordformNode -> cordformNode.configure() } @@ -19,10 +20,10 @@ fun CordformNode.rpcUsers(vararg users: User) { rpcUsers = users.map { it.toMap() } } +fun CordformNode.notary(notaryConfig: NotaryConfig) { + notary = notaryConfig.toConfig().root().unwrapped() +} + fun CordformNode.advertisedServices(vararg services: ServiceInfo) { advertisedServices = services.map { it.toString() } } - -fun CordformNode.notaryClusterAddresses(vararg addresses: NetworkHostAndPort) { - notaryClusterAddresses = addresses.map { it.toString() } -} diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index e1d1f5c4c3..b1f02788c6 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -19,7 +19,6 @@ import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.services.IdentityService import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.NetworkMapCache -import net.corda.core.node.services.NotaryService import net.corda.core.serialization.SerializationWhitelist import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow @@ -28,10 +27,10 @@ import net.corda.finance.utils.WorldMapLocation import net.corda.node.internal.AbstractNode import net.corda.node.internal.StartedNode import net.corda.node.services.api.NetworkMapCacheInternal -import net.corda.nodeapi.internal.ServiceInfo -import net.corda.nodeapi.internal.ServiceType import net.corda.node.services.api.SchemaService +import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.config.NodeConfiguration +import net.corda.node.services.config.NotaryConfig import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.E2ETestKeyManagementService import net.corda.node.services.messaging.MessagingService @@ -40,18 +39,22 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.BFTSMaRt import net.corda.node.services.transactions.InMemoryTransactionVerifierService -import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.CertificateAndKeyPair -import net.corda.testing.* +import net.corda.nodeapi.internal.ServiceInfo +import net.corda.testing.DUMMY_KEY_1 +import net.corda.testing.initialiseTestSerialization import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties +import net.corda.testing.resetTestSerialization +import net.corda.testing.testNodeConfiguration import org.apache.activemq.artemis.utils.ReusableLatch import org.slf4j.Logger import java.math.BigInteger import java.nio.file.Path import java.security.KeyPair +import java.security.PublicKey import java.security.cert.X509Certificate import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicInteger @@ -220,17 +223,6 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, return InMemoryNetworkMapService(network, networkMapCache, 1) } - override fun getNotaryIdentity(): PartyAndCertificate? { - val defaultIdentity = super.getNotaryIdentity() - return if (notaryIdentity == null || !notaryIdentity.first.type.isNotary() || defaultIdentity == null) - defaultIdentity - else { - // Ensure that we always have notary in name and type of it. TODO It is temporary solution until we will have proper handling of NetworkParameters - myNotaryIdentity = getTestPartyAndCertificate(defaultIdentity.name, notaryIdentity.second.public) - myNotaryIdentity - } - } - // This is not thread safe, but node construction is done on a single thread, so that should always be fine override fun generateKeyPair(): KeyPair { counter = counter.add(BigInteger.ONE) @@ -277,12 +269,11 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, override fun acceptableLiveFiberCountOnStop(): Int = acceptableLiveFiberCountOnStop - override fun makeCoreNotaryService(type: ServiceType): NotaryService? { - if (type != BFTNonValidatingNotaryService.type) return super.makeCoreNotaryService(type) - return BFTNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey, object : BFTSMaRt.Cluster { + override fun makeBFTCluster(notaryKey: PublicKey, bftSMaRtConfig: BFTSMaRtConfiguration): BFTSMaRt.Cluster { + return object : BFTSMaRt.Cluster { override fun waitUntilAllReplicasHaveInitialized() { - val clusterNodes = mockNet.nodes.filter { myNotaryIdentity!!.owningKey in it.started!!.info.legalIdentities.map { it.owningKey } } - if (clusterNodes.size != configuration.notaryClusterAddresses.size) { + val clusterNodes = mockNet.nodes.filter { notaryKey in it.started!!.info.legalIdentities.map { it.owningKey } } + if (clusterNodes.size != bftSMaRtConfig.clusterAddresses.size) { throw IllegalStateException("Unable to enumerate all nodes in BFT cluster.") } clusterNodes.forEach { @@ -290,7 +281,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, notaryService.waitUntilReplicaHasInitialized() } } - }) + } } /** @@ -406,16 +397,19 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, } } - /** - * Construct a default notary node. - */ - fun createNotaryNode() = createNotaryNode(DUMMY_NOTARY.name, null, null) + @JvmOverloads + fun createNotaryNode(legalName: CordaX500Name? = null, validating: Boolean = true): StartedNode { + return createNode(legalName = legalName, configOverrides = { + whenever(it.notary).thenReturn(NotaryConfig(validating)) + }) + } - fun createNotaryNode(legalName: CordaX500Name? = null, - notaryIdentity: Pair? = null, - serviceName: CordaX500Name? = null): StartedNode { - return createNode(legalName = legalName, notaryIdentity = notaryIdentity, - advertisedServices = *arrayOf(ServiceInfo(ValidatingNotaryService.type, serviceName))) + fun createNotaryNode(legalName: CordaX500Name? = null, + validating: Boolean = true, + nodeFactory: Factory): StartedNode { + return createNode(legalName = legalName, nodeFactory = nodeFactory, configOverrides = { + whenever(it.notary).thenReturn(NotaryConfig(validating)) + }) } @JvmOverloads diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index 2a126e5d21..3a8f838ffb 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -5,19 +5,16 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.concurrent.* import net.corda.core.internal.createDirectories import net.corda.core.internal.div +import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.node.internal.Node import net.corda.node.internal.StartedNode -import net.corda.node.services.config.ConfigHelper -import net.corda.node.services.config.FullNodeConfiguration -import net.corda.node.services.config.configOf -import net.corda.node.services.config.plus -import net.corda.node.services.transactions.RaftValidatingNotaryService +import net.corda.node.services.config.* import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.nodeapi.User import net.corda.nodeapi.config.parseAs +import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.internal.ServiceInfo -import net.corda.nodeapi.internal.ServiceType import net.corda.testing.DUMMY_MAP import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.driver.addressMustNotBeBoundFuture @@ -129,30 +126,44 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { return if (waitForConnection) node.internals.nodeReadyFuture.map { node } else doneFuture(node) } - fun startNotaryCluster(notaryName: CordaX500Name, - clusterSize: Int, - serviceType: ServiceType = RaftValidatingNotaryService.type): CordaFuture>> { + // TODO This method has been added temporarily, to be deleted once the set of notaries is defined at the network level. + fun startNotaryNode(name: CordaX500Name, + rpcUsers: List = emptyList(), + validating: Boolean = true): CordaFuture> { + return startNode(name, rpcUsers = rpcUsers, configOverrides = mapOf("notary" to mapOf("validating" to validating))) + } + + fun startNotaryCluster(notaryName: CordaX500Name, clusterSize: Int): CordaFuture>> { + fun notaryConfig(nodeAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): Map { + val clusterAddresses = if (clusterAddress != null) listOf(clusterAddress) else emptyList() + val config = NotaryConfig(validating = true, raft = RaftConfig(nodeAddress = nodeAddress, clusterAddresses = clusterAddresses)) + return mapOf("notary" to config.toConfig().root().unwrapped()) + } + ServiceIdentityGenerator.generateToDisk( (0 until clusterSize).map { baseDirectory(notaryName.copy(organisation = "${notaryName.organisation}-$it")) }, notaryName) - val serviceInfo = ServiceInfo(serviceType, notaryName) - val nodeAddresses = getFreeLocalPorts("localhost", clusterSize).map { it.toString() } + val nodeAddresses = getFreeLocalPorts("localhost", clusterSize) val masterNodeFuture = startNode( CordaX500Name(organisation = "${notaryName.organisation}-0", locality = notaryName.locality, country = notaryName.country), - advertisedServices = setOf(serviceInfo), - configOverrides = mapOf("notaryNodeAddress" to nodeAddresses[0], - "database" to mapOf("serverNameTablePrefix" to if (clusterSize > 1) "${notaryName.organisation}0".replace(Regex("[^0-9A-Za-z]+"), "") else ""))) + configOverrides = notaryConfig(nodeAddresses[0]) + mapOf( + "database" to mapOf( + "serverNameTablePrefix" to if (clusterSize > 1) "${notaryName.organisation}0".replace(Regex("[^0-9A-Za-z]+"), "") else "" + ) + ) + ) val remainingNodesFutures = (1 until clusterSize).map { startNode( CordaX500Name(organisation = "${notaryName.organisation}-$it", locality = notaryName.locality, country = notaryName.country), - advertisedServices = setOf(serviceInfo), - configOverrides = mapOf( - "notaryNodeAddress" to nodeAddresses[it], - "notaryClusterAddresses" to listOf(nodeAddresses[0]), - "database" to mapOf("serverNameTablePrefix" to "${notaryName.organisation}$it".replace(Regex("[^0-9A-Za-z]+"), "")))) + configOverrides = notaryConfig(nodeAddresses[it], nodeAddresses[0]) + mapOf( + "database" to mapOf( + "serverNameTablePrefix" to "${notaryName.organisation}$it".replace(Regex("[^0-9A-Za-z]+"), "") + ) + ) + ) } return remainingNodesFutures.transpose().flatMap { remainingNodes -> diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt index ef0c1ff87b..cc003edc6b 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt @@ -13,7 +13,7 @@ class NodeConfig( val p2pPort: Int, val rpcPort: Int, val webPort: Int, - val extraServices: List, + val isNotary: Boolean, val users: List, var networkMap: NodeConfig? = null ) { @@ -27,18 +27,25 @@ class NodeConfig( * The configuration object depends upon the networkMap, * which is mutable. */ - fun toFileConfig(): Config = empty() - .withValue("myLegalName", valueFor(legalName.toString())) - .withValue("p2pAddress", addressValueFor(p2pPort)) - .withValue("extraAdvertisedServiceIds", valueFor(extraServices)) - .withFallback(optional("networkMapService", networkMap, { c, n -> - c.withValue("address", addressValueFor(n.p2pPort)) - .withValue("legalName", valueFor(n.legalName.toString())) - })) - .withValue("webAddress", addressValueFor(webPort)) - .withValue("rpcAddress", addressValueFor(rpcPort)) - .withValue("rpcUsers", valueFor(users.map(User::toMap).toList())) - .withValue("useTestClock", valueFor(true)) + //TODO Make use of Any.toConfig + private fun toFileConfig(): Config { + val config = empty() + .withValue("myLegalName", valueFor(legalName.toString())) + .withValue("p2pAddress", addressValueFor(p2pPort)) + .withFallback(optional("networkMapService", networkMap, { c, n -> + c.withValue("address", addressValueFor(n.p2pPort)) + .withValue("legalName", valueFor(n.legalName.toString())) + })) + .withValue("webAddress", addressValueFor(webPort)) + .withValue("rpcAddress", addressValueFor(rpcPort)) + .withValue("rpcUsers", valueFor(users.map(User::toMap).toList())) + .withValue("useTestClock", valueFor(true)) + return if (isNotary) { + config.withValue("notary", ConfigValueFactory.fromMap(mapOf("validating" to true))) + } else { + config + } + } fun toText(): String = toFileConfig().root().render(renderOptions) diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt index 141a9e1d68..03dda19564 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt @@ -8,7 +8,7 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardCopyOption -class NodeConfig( +class NodeConfig constructor( baseDir: Path, legalName: CordaX500Name, p2pPort: Int, @@ -42,20 +42,22 @@ class NodeConfig( * The configuration object depends upon the networkMap, * which is mutable. */ - fun toFileConfig(): Config = ConfigFactory.empty() - .withValue("myLegalName", valueFor(legalName.toString())) - .withValue("p2pAddress", addressValueFor(p2pPort)) - .withValue("extraAdvertisedServiceIds", valueFor(extraServices)) - .withFallback(optional("networkMapService", networkMap, { c, n -> - c.withValue("address", addressValueFor(n.p2pPort)) - .withValue("legalName", valueFor(n.legalName.toString())) - })) - .withValue("webAddress", addressValueFor(webPort)) - .withValue("rpcAddress", addressValueFor(rpcPort)) - .withValue("rpcUsers", valueFor(users.map(User::toMap).toList())) - .withValue("h2port", valueFor(h2Port)) - .withValue("useTestClock", valueFor(true)) - .withValue("detectPublicIp", valueFor(false)) + fun toFileConfig(): Config { + return ConfigFactory.empty() + .withValue("myLegalName", valueFor(legalName.toString())) + .withValue("p2pAddress", addressValueFor(p2pPort)) + .withValue("extraAdvertisedServiceIds", valueFor(extraServices)) + .withFallback(optional("networkMapService", networkMap, { c, n -> + c.withValue("address", addressValueFor(n.p2pPort)) + .withValue("legalName", valueFor(n.legalName.toString())) + })) + .withValue("webAddress", addressValueFor(webPort)) + .withValue("rpcAddress", addressValueFor(rpcPort)) + .withValue("rpcUsers", valueFor(users.map(User::toMap).toList())) + .withValue("h2port", valueFor(h2Port)) + .withValue("useTestClock", valueFor(true)) + .withValue("detectPublicIp", valueFor(false)) + } fun toText(): String = toFileConfig().root().render(renderOptions) diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/UserTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/UserTest.kt index e902bcfec9..5376c0915e 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/UserTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/UserTest.kt @@ -11,7 +11,7 @@ class UserTest { val user = toUser(emptyMap()) assertEquals("none", user.username) assertEquals("none", user.password) - assertEquals(emptySet(), user.permissions) + assertEquals(emptySet(), user.permissions) } @Test @@ -33,7 +33,7 @@ class UserTest { val map = user.toMap() assertEquals("MyName", map["username"]) assertEquals("MyPassword", map["password"]) - assertEquals(setOf("Flow.MyFlow"), map["permissions"]) + assertEquals(listOf("Flow.MyFlow"), map["permissions"]) } @Test diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index 706d5e3649..5dbb09753c 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -21,10 +21,9 @@ import net.corda.finance.flows.* import net.corda.finance.flows.CashExitFlow.ExitRequest import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.node.services.transactions.SimpleNotaryService +import net.corda.nodeapi.User import net.corda.nodeapi.internal.ServiceInfo import net.corda.nodeapi.internal.ServiceType -import net.corda.nodeapi.User import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY @@ -70,8 +69,7 @@ class ExplorerSimulation(val options: OptionSet) { val portAllocation = PortAllocation.Incremental(20000) driver(portAllocation = portAllocation, extraCordappPackagesToScan = listOf("net.corda.finance")) { // TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo. - val notary = startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)), - customOverrides = mapOf("nearestCity" to "Zurich")) + val notary = startNotaryNode(DUMMY_NOTARY.name, customOverrides = mapOf("nearestCity" to "Zurich"), validating = false) val alice = startNode(providedName = ALICE.name, rpcUsers = arrayListOf(user), advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("cash"))), customOverrides = mapOf("nearestCity" to "Milan")) diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt index 4ce985d0f5..6648fdb83d 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -10,12 +10,10 @@ import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.config.VerifierType -import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.ALICE import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.driver.NetworkMapStartStrategy import net.corda.testing.chooseIdentity +import net.corda.testing.driver.NetworkMapStartStrategy import org.junit.Test import java.util.* import java.util.concurrent.atomic.AtomicInteger @@ -117,7 +115,7 @@ class VerifierTests { extraCordappPackagesToScan = listOf("net.corda.finance.contracts") ) { val aliceFuture = startNode(providedName = ALICE.name) - val notaryFuture = startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type)), verifierType = VerifierType.OutOfProcess) + val notaryFuture = startNotaryNode(DUMMY_NOTARY.name, verifierType = VerifierType.OutOfProcess) val alice = aliceFuture.get() val notary = notaryFuture.get() val notaryIdentity = notary.nodeInfo.legalIdentities[1]