diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 314e8a7eb4..53fbedd770 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -41,6 +41,8 @@ Version 5.0 Version 4.2 ----------- +* The MockNet now supports setting a custom Notary class name, as was already supported by normal node config. See :doc:`tutorial-custom-notary`. + * Contract attachments are now automatically whitelisted by the node if another contract attachment is present with the same contract classes, signed by the same public keys, and uploaded by a trusted uploader. This allows the node to resolve transactions that use earlier versions of a contract without having to manually install that version, provided a newer version is installed. Similarly, non-contract attachments diff --git a/docs/source/tutorial-custom-notary.rst b/docs/source/tutorial-custom-notary.rst index 549e5effcf..e0830a00a7 100644 --- a/docs/source/tutorial-custom-notary.rst +++ b/docs/source/tutorial-custom-notary.rst @@ -32,4 +32,17 @@ To enable the service, add the following to the node configuration: notary : { validating : true # Set to false if your service is non-validating className : "net.corda.notarydemo.MyCustomValidatingNotaryService" # The fully qualified name of your service class - } \ No newline at end of file + } + +Testing your custom notary service +--------------------------------- + +To create a flow test that uses your custom notary service, you can set the class name of the custom notary service as follows in your flow test: + +.. literalinclude:: ../../testing/node-driver/src/test/kotlin/net/corda/testing/node/CustomNotaryTest.kt + :language: kotlin + :start-after: START 1 + :end-before: END 1 + +After this, your custom notary will be the default notary on the mock network, and can be used in the same way as described in :doc:`flow-testing`. + diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt index 6177173298..0165831553 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt @@ -123,8 +123,9 @@ data class MockNetworkParameters( * * @property name The name of the notary node. * @property validating Boolean for whether the notary is validating or non-validating. + * @property className String the optional name of a notary service class to load. If null, a builtin notary is loaded. */ -data class MockNetworkNotarySpec(val name: CordaX500Name, val validating: Boolean = true) { +data class MockNetworkNotarySpec(val name: CordaX500Name, val validating: Boolean = true, val className: String? = null) { constructor(name: CordaX500Name) : this(name, validating = true) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt index 6c5151a795..03f2bffdaf 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt @@ -249,10 +249,10 @@ open class InternalMockNetwork(cordappPackages: List = emptyList(), @VisibleForTesting internal open fun createNotaries(): List { - return notarySpecs.map { (name, validating) -> + return notarySpecs.map { (name, validating, className) -> createNode(InternalMockNodeParameters( legalName = name, - configOverrides = { doReturn(NotaryConfig(validating)).whenever(it).notary } + configOverrides = { doReturn(NotaryConfig(validating, className = className)).whenever(it).notary } )) } } diff --git a/testing/node-driver/src/test/kotlin/net/corda/testing/node/CustomNotaryTest.kt b/testing/node-driver/src/test/kotlin/net/corda/testing/node/CustomNotaryTest.kt new file mode 100644 index 0000000000..502700a3dc --- /dev/null +++ b/testing/node-driver/src/test/kotlin/net/corda/testing/node/CustomNotaryTest.kt @@ -0,0 +1,77 @@ +package net.corda.testing.node + +import net.corda.core.flows.FlowException +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.NotaryFlow +import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party +import net.corda.core.internal.notary.NotaryService +import net.corda.core.utilities.getOrThrow +import net.corda.node.services.api.ServiceHubInternal +import net.corda.testing.contracts.DummyContract +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.singleIdentity +import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP +import net.corda.testing.node.internal.enclosedCordapp +import org.junit.After +import org.junit.Before +import org.junit.Test +import java.security.PublicKey +import java.util.* + +class CustomNotaryTest { + private lateinit var mockNet: MockNetwork + private lateinit var notaryNode: StartedMockNode + private lateinit var aliceNode: StartedMockNode + private lateinit var notary: Party + private lateinit var alice: Party + + @Before + fun setup() { + // START 1 + mockNet = MockNetwork(MockNetworkParameters( + cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, enclosedCordapp()), + notarySpecs = listOf(MockNetworkNotarySpec( + name = CordaX500Name("Custom Notary", "Amsterdam", "NL"), + className = "net.corda.testing.node.CustomNotaryTest\$CustomNotaryService", + validating = false // Can also be validating if preferred. + )) + )) + // END 1 + aliceNode = mockNet.createPartyNode(ALICE_NAME) + notaryNode = mockNet.defaultNotaryNode + notary = mockNet.defaultNotaryIdentity + alice = aliceNode.info.singleIdentity() + } + + @After + fun tearDown() { + mockNet.stopNodes() + } + + @Test(expected = CustomNotaryException::class) + fun `custom notary service is active`() { + val tx = DummyContract.generateInitial(Random().nextInt(), notary, alice.ref(0)) + val stx = aliceNode.services.signInitialTransaction(tx) + val future = aliceNode.startFlow(NotaryFlow.Client(stx)) + mockNet.runNetwork() + future.getOrThrow() + } + + class CustomNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : NotaryService() { + + override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic = + object : FlowLogic() { + override fun call(): Void? { + throw CustomNotaryException("Proof that a custom notary service is running!") + } + } + + override fun start() {} + override fun stop() {} + } + + class CustomNotaryException(message: String) : FlowException(message) +} +