From 3535633d4590a0cc1bd911f4f6be2ffff511b893 Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Wed, 30 May 2018 12:35:51 +0100 Subject: [PATCH 1/5] Add DCO requirement to contributing docs. (#3263) --- docs/source/contributing.rst | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst index 0baf3c6efc..04becd16b5 100644 --- a/docs/source/contributing.rst +++ b/docs/source/contributing.rst @@ -130,16 +130,20 @@ Merging the changes back into Corda 1. Create a pull request from your fork to the ``master`` branch of the Corda repo -2. In the PR comments box, complete the pull-request checklist: +2. In the PR comments box: - * [ ] Have you run the unit, integration and smoke tests as described here? https://docs.corda.net/head/testing.html - * [ ] If you added/changed public APIs, did you write/update the JavaDocs? - * [ ] If the changes are of interest to application developers, have you added them to the changelog, and potentially - release notes? - * [ ] If you are contributing for the first time, please read the agreement in CONTRIBUTING.md now and add to this - Pull Request that you agree to it. + * Complete the pull-request checklist: -3. In the PR comments box, also add a clear description of the purpose for the PR + * [ ] Have you run the unit, integration and smoke tests as described here? https://docs.corda.net/head/testing.html + * [ ] If you added/changed public APIs, did you write/update the JavaDocs? + * [ ] If the changes are of interest to application developers, have you added them to the changelog, and potentially + release notes? + * [ ] If you are contributing for the first time, please read the agreement in CONTRIBUTING.md now and add to this + Pull Request that you agree to it. + + * Add a clear description of the purpose of the PR + + * Add the following statement to confirm that your contribution is your own original work: "I hereby certify that my contribution is in accordance with the Developer Certificate of Origin (https://github.com/corda/corda/blob/master/CONTRIBUTING.md#developer-certificate-of-origin)." 4. Request a review from a member of the Corda platform team via the `#design channel `_ From 6f0363258ee56f34e26adabcdb7624abde80fb24 Mon Sep 17 00:00:00 2001 From: sollecitom Date: Wed, 30 May 2018 13:28:05 +0100 Subject: [PATCH 2/5] [CORDA-1552]: Log commands executed by the Shell. --- .../java/net/corda/tools/shell/FlowShellCommand.java | 9 +++++++++ .../java/net/corda/tools/shell/RunShellCommand.java | 12 ++++++++---- .../net/corda/tools/shell/StartShellCommand.java | 11 ++++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java b/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java index 1ed76de5f0..56607c948d 100644 --- a/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java +++ b/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java @@ -10,9 +10,12 @@ import org.crsh.cli.*; import org.crsh.command.*; import org.crsh.text.*; import org.crsh.text.ui.TableElement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.*; +import static java.util.stream.Collectors.joining; import static net.corda.tools.shell.InteractiveShell.runFlowByNameFragment; import static net.corda.tools.shell.InteractiveShell.runStateMachinesView; @@ -24,12 +27,16 @@ import static net.corda.tools.shell.InteractiveShell.runStateMachinesView; "flow constructors (the right one is picked automatically) are then specified using the same syntax as for the run command." ) public class FlowShellCommand extends InteractiveShellCommand { + + private static Logger logger = LoggerFactory.getLogger(FlowShellCommand.class); + @Command @Usage("Start a (work)flow on the node. This is how you can change the ledger.") public void start( @Usage("The class name of the flow to run, or an unambiguous substring") @Argument String name, @Usage("The data to pass as input") @Argument(unquote = false) List input ) { + logger.info("Executing command \"flow start {} {}\",", name, input.stream().collect(joining(" "))); startFlow(name, input, out, ops(), ansiProgressRenderer(), objectMapper()); } @@ -37,6 +44,7 @@ public class FlowShellCommand extends InteractiveShellCommand { @Command @Usage("watch information about state machines running on the node with result information") public void watch(InvocationContext context) throws Exception { + logger.info("Executing command \"flow watch\"."); runStateMachinesView(out, ops()); } @@ -57,6 +65,7 @@ public class FlowShellCommand extends InteractiveShellCommand { @Command @Usage("list flows that user can start") public void list(InvocationContext context) throws Exception { + logger.info("Executing command \"flow list\"."); for (String name : ops().registeredFlows()) { context.provide(name + System.lineSeparator()); } diff --git a/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java b/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java index 497a855a68..11ec5e8e76 100644 --- a/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java +++ b/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java @@ -10,6 +10,8 @@ import org.crsh.cli.Man; import org.crsh.cli.Usage; import org.crsh.command.InvocationContext; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; @@ -17,11 +19,15 @@ import java.util.Map; import java.util.Set; import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.joining; // Note that this class cannot be converted to Kotlin because CRaSH does not understand InvocationContext> which // is the closest you can get in Kotlin to raw types. public class RunShellCommand extends InteractiveShellCommand { + + private static Logger logger = LoggerFactory.getLogger(RunShellCommand.class); + @Command @Man( "Runs a method from the CordaRPCOps interface, which is the same interface exposed to RPC clients.\n\n" + @@ -30,10 +36,8 @@ public class RunShellCommand extends InteractiveShellCommand { "consulting the developer guide at https://docs.corda.net/api/kotlin/corda/net.corda.core.messaging/-corda-r-p-c-ops/index.html" ) @Usage("runs a method from the CordaRPCOps interface on the node.") - public Object main( - InvocationContext context, - @Usage("The command to run") @Argument(unquote = false) List command - ) { + public Object main(InvocationContext context, @Usage("The command to run") @Argument(unquote = false) List command) { + logger.info("Executing command \"run {}\",", command.stream().collect(joining(" "))); StringToMethodCallParser parser = new StringToMethodCallParser<>(CordaRPCOps.class, objectMapper()); if (command == null) { diff --git a/tools/shell/src/main/java/net/corda/tools/shell/StartShellCommand.java b/tools/shell/src/main/java/net/corda/tools/shell/StartShellCommand.java index 2f368128c1..6fd3ffae7e 100644 --- a/tools/shell/src/main/java/net/corda/tools/shell/StartShellCommand.java +++ b/tools/shell/src/main/java/net/corda/tools/shell/StartShellCommand.java @@ -5,15 +5,24 @@ package net.corda.tools.shell; import net.corda.tools.shell.utlities.ANSIProgressRenderer; import net.corda.tools.shell.utlities.CRaSHANSIProgressRenderer; import org.crsh.cli.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.*; +import static java.util.stream.Collectors.joining; + public class StartShellCommand extends InteractiveShellCommand { + + private static Logger logger = LoggerFactory.getLogger(StartShellCommand.class); + @Command @Man("An alias for 'flow start'. Example: \"start Yo target: Some other company\"") public void main(@Usage("The class name of the flow to run, or an unambiguous substring") @Argument String name, @Usage("The data to pass as input") @Argument(unquote = false) List input) { + + logger.info("Executing command \"start {} {}\",", name, input.stream().collect(joining(" "))); ANSIProgressRenderer ansiProgressRenderer = ansiProgressRenderer(); FlowShellCommand.startFlow(name, input, out, ops(), ansiProgressRenderer != null ? ansiProgressRenderer : new CRaSHANSIProgressRenderer(out), objectMapper()); } -} +} \ No newline at end of file From 26ef294d8d88c29ba0f4c803afcaf1078ece0b78 Mon Sep 17 00:00:00 2001 From: Michele Sollecito Date: Wed, 30 May 2018 15:04:24 +0100 Subject: [PATCH 3/5] [CORDA-1356]: OOM when using Demobench + Explorer (fix). (#3268) --- docs/source/changelog.rst | 2 ++ tools/explorer/capsule/build.gradle | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index bb9a4dc983..bce0ddeb4b 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -7,6 +7,8 @@ release, see :doc:`upgrade-notes`. Unreleased ========== +* Removed -xmx VM argument from Explorer's Capsule setup. This helps avoiding out of memory errors. + * Shell now kills an ongoing flow when CTRL+C is pressed in the terminal. * ``ServiceHub`` and ``CordaRPCOps`` can now safely be used from multiple threads without incurring in database transaction problems. diff --git a/tools/explorer/capsule/build.gradle b/tools/explorer/capsule/build.gradle index ac4ccd1e0e..7f99cd75f2 100644 --- a/tools/explorer/capsule/build.gradle +++ b/tools/explorer/capsule/build.gradle @@ -40,11 +40,9 @@ task buildExplorerJAR(type: FatCapsule, dependsOn: project(':tools:explorer').co caplets = ['ExplorerCaplet'] // JVM configuration: - // - Constrain to small heap sizes to ease development on low end devices. // - Switch to the G1 GC which is going to be the default in Java 9 and gives low pause times/string dedup. // - // If you change these flags, please also update Driver.kt - jvmArgs = ['-Xmx512m', '-XX:+UseG1GC'] + jvmArgs = ['-XX:+UseG1GC'] } } From 9418f9191ea261089a094be415bce353e9bef2a9 Mon Sep 17 00:00:00 2001 From: Thomas Schroeter Date: Wed, 30 May 2018 15:47:40 +0100 Subject: [PATCH 4/5] Document `notary.serviceLegalName` (#3265) --- docs/source/corda-configuration-file.rst | 3 +++ docs/source/setting-up-a-corda-network.rst | 1 + 2 files changed, 4 insertions(+) diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 7c5c00234b..128e92ed9a 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -125,6 +125,9 @@ absolute path to the node's base directory. :validating: Boolean to determine whether the notary is a validating or non-validating one. + :serviceLegalName: If the node is part of a distributed cluster, specify the legal name of the cluster. At runtime, Corda + checks whether this name matches the name of the certificate of the notary cluster. + :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 diff --git a/docs/source/setting-up-a-corda-network.rst b/docs/source/setting-up-a-corda-network.rst index 492bc4e24a..ce0d70a8b6 100644 --- a/docs/source/setting-up-a-corda-network.rst +++ b/docs/source/setting-up-a-corda-network.rst @@ -44,6 +44,7 @@ The most important fields regarding network configuration are: * ``rpcAddress``: The address to which Artemis will bind for RPC calls. * ``webAddress``: The address the webserver should bind. Note that the port must be distinct from that of ``p2pAddress`` and ``rpcAddress`` if they are on the same machine. +* ``notary.serviceLegalName``: The name of the notary service, required to setup distributed notaries with the network-bootstrapper. Bootstrapping the network ~~~~~~~~~~~~~~~~~~~~~~~~~ From ed70fea3a7e5c01b47e0561b95cd8b84f9726210 Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Wed, 30 May 2018 16:19:06 +0100 Subject: [PATCH 5/5] CORDA-1548 Hibernate session not flushed before handing over raw JDBC session to user code (e.g. coin selection) (#3266) * Hibernate session flushed before handing over raw JDBC session to user code + test - inserting and selecting cash in the same transaction * Additional two tests copied from Enterprise repo --- .../corda/finance/flows/CashSelectionTest.kt | 101 ++++++++++++++++++ .../internal/persistence/CordaPersistence.kt | 1 + 2 files changed, 102 insertions(+) diff --git a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt index 14a6531d25..f16e4122c5 100644 --- a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt +++ b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt @@ -1,10 +1,18 @@ package net.corda.finance.flows +import net.corda.core.contracts.TransactionState +import net.corda.core.contracts.withoutIssuer +import net.corda.core.identity.Party import net.corda.core.messaging.startFlow +import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS +import net.corda.finance.contracts.asset.Cash +import net.corda.finance.contracts.asset.cash.selection.AbstractCashSelection import net.corda.finance.contracts.getCashBalance +import net.corda.finance.issuedBy +import net.corda.testing.core.singleIdentity import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.driver import net.corda.testing.driver.internal.InProcessImpl @@ -34,4 +42,97 @@ class CashSelectionTest { assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount) } } + + @Test + fun `cash selection sees states added in the same transaction`() { + driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { + val node = startNode().getOrThrow() as InProcessImpl + val nodeIdentity = node.services.myInfo.singleIdentity() + val issuer = nodeIdentity.ref(1) + val coin = 1.DOLLARS.issuedBy(issuer) + val exitedAmount = 1.DOLLARS + val issuance = TransactionBuilder(null as Party?) + issuance.addOutputState(TransactionState(Cash.State(coin, nodeIdentity), Cash.PROGRAM_ID, defaultNotaryIdentity)) + issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) + + //insert ans select in the same transaction + val exitStates = node.database.transaction { + + val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) + node.services.recordTransactions(transaction) + + val builder = TransactionBuilder(notary = null) + AbstractCashSelection + .getInstance { node.services.jdbcSession().metaData } + .unconsumedCashStatesForSpending(node.services, exitedAmount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) + } + val returnedCoinsNumber = 1 + assertThat(exitStates.size).isEqualTo(returnedCoinsNumber) + } + } + + @Test + fun `dont return extra coins if the selected amount has been reached`() { + driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { + val node = startNode().getOrThrow() as InProcessImpl + val nodeIdentity = node.services.myInfo.singleIdentity() + + val issuer = nodeIdentity.ref(1) + + val exitStates = node.database.transaction { + //issue $1 coin twice + repeat(2, { + val coin = 1.DOLLARS.issuedBy(issuer) + val issuance = TransactionBuilder(null as Party?) + issuance.addOutputState(TransactionState(Cash.State(coin, nodeIdentity), Cash.PROGRAM_ID, defaultNotaryIdentity)) + issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) + + val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) + + node.services.recordTransactions(transaction) + }) + + val exitedAmount = 1.DOLLARS + val builder = TransactionBuilder(notary = null) + AbstractCashSelection + .getInstance { node.services.jdbcSession().metaData } + .unconsumedCashStatesForSpending(node.services, exitedAmount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) + } + val returnedCoinsNumber = 1 + assertThat(exitStates.size).isEqualTo(returnedCoinsNumber) + } + } + + @Test + fun `select cash states issued by single transaction and give change`() { + driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { + val node = startNode().getOrThrow() as InProcessImpl + val nodeIdentity = node.services.myInfo.singleIdentity() + + val coins = listOf(3.DOLLARS, 2.DOLLARS, 1.DOLLARS).map { it.issuedBy(nodeIdentity.ref(1)) } + + //create single transaction with 3 cash outputs + val issuance = TransactionBuilder(null as Party?) + coins.map { issuance.addOutputState(TransactionState(Cash.State(it, nodeIdentity), "net.corda.finance.contracts.asset.Cash", defaultNotaryIdentity)) } + issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) + + val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) + node.database.transaction { + node.services.recordTransactions(transaction) + } + + val issuedAmount = coins.reduce { sum, element -> sum + element }.withoutIssuer() + + val availableBalance = node.rpc.getCashBalance(issuedAmount.token) + + assertThat(availableBalance).isEqualTo(issuedAmount) + + val exitedAmount = 3.01.DOLLARS + node.rpc.startFlow(::CashExitFlow, exitedAmount, OpaqueBytes.of(1)).returnValue.getOrThrow() + + val availableBalanceAfterExit = node.rpc.getCashBalance(issuedAmount.token) + + assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount) + } + } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt index 1f63424f3f..850436efa6 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt @@ -105,6 +105,7 @@ class CordaPersistence( fun createSession(): Connection { // We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases. _contextDatabase.set(this) + currentDBSession().flush() return contextTransaction.connection }