Resolved merge conflicts.

This commit is contained in:
szymonsztuka 2018-05-30 16:36:06 +01:00
commit 37cadbce69
10 changed files with 94 additions and 35 deletions

View File

@ -8,6 +8,8 @@ Unreleased
========== ==========
* Introduced a hierarchy of ``DatabaseMigrationException``s, allowing ``NodeStartup`` to gracefully inform users of problems related to database migrations before exiting with a non-zero code. * Introduced a hierarchy of ``DatabaseMigrationException``s, allowing ``NodeStartup`` to gracefully inform users of problems related to database migrations before exiting with a non-zero code.
* 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. * 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. * ``ServiceHub`` and ``CordaRPCOps`` can now safely be used from multiple threads without incurring in database transaction problems.

View File

@ -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 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 * Complete the pull-request checklist:
* [ ] 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.
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 <http://slack.corda.net/>`_ 4. Request a review from a member of the Corda platform team via the `#design channel <http://slack.corda.net/>`_

View File

@ -145,6 +145,9 @@ absolute path to the node's base directory.
:validating: Boolean to determine whether the notary is a validating or non-validating one. :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: :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 :nodeAddress: The host and port to which to bind the embedded Raft server. Note that the Raft cluster uses a

View File

@ -44,6 +44,7 @@ The most important fields regarding network configuration are:
* ``rpcAddress``: The address to which Artemis will bind for RPC calls. * ``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`` * ``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. 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 Bootstrapping the network
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -1,6 +1,7 @@
package net.corda.finance.flows package net.corda.finance.flows
import net.corda.core.contracts.* import net.corda.core.contracts.TransactionState
import net.corda.core.contracts.withoutIssuer
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -52,30 +53,57 @@ class CashSelectionTest : IntegrationTest() {
} }
} }
@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 @Test
fun `dont return extra coins if the selected amount has been reached`() { fun `dont return extra coins if the selected amount has been reached`() {
driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) {
val node = startNode().getOrThrow() as InProcessImpl val node = startNode().getOrThrow() as InProcessImpl
val nodeIdentity = node.services.myInfo.singleIdentity() val nodeIdentity = node.services.myInfo.singleIdentity()
//issue $1 coin twice
val issuer = nodeIdentity.ref(1) val issuer = nodeIdentity.ref(1)
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.database.transaction {
node.services.recordTransactions(transaction)
}
})
val exitedAmount = 1.DOLLARS
val builder = TransactionBuilder(notary = null)
val exitStates = node.database.transaction { 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 AbstractCashSelection
.getInstance { node.services.jdbcSession().metaData } .getInstance { node.services.jdbcSession().metaData }
.unconsumedCashStatesForSpending(node.services, exitedAmount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) .unconsumedCashStatesForSpending(node.services, exitedAmount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference))
@ -105,12 +133,12 @@ class CashSelectionTest : IntegrationTest() {
val issuedAmount = coins.reduce { sum, element -> sum + element }.withoutIssuer() val issuedAmount = coins.reduce { sum, element -> sum + element }.withoutIssuer()
val availableBalance = node.rpc.getCashBalance(issuedAmount.token) val availableBalance = node.rpc.getCashBalance(issuedAmount.token)
assertThat(availableBalance).isEqualTo(issuedAmount) assertThat(availableBalance).isEqualTo(issuedAmount)
val exitedAmount = 3.01.DOLLARS val exitedAmount = 3.01.DOLLARS
node.rpc.startFlow(::CashExitFlow, exitedAmount, OpaqueBytes.of(1)).returnValue.getOrThrow() node.rpc.startFlow(::CashExitFlow, exitedAmount, OpaqueBytes.of(1)).returnValue.getOrThrow()
val availableBalanceAfterExit = node.rpc.getCashBalance(issuedAmount.token) val availableBalanceAfterExit = node.rpc.getCashBalance(issuedAmount.token)

View File

@ -123,6 +123,7 @@ class CordaPersistence(
fun createSession(): Connection { fun createSession(): Connection {
// We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases. // We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases.
_contextDatabase.set(this) _contextDatabase.set(this)
currentDBSession().flush()
return contextTransaction.connection return contextTransaction.connection
} }

View File

@ -50,11 +50,9 @@ task buildExplorerJAR(type: FatCapsule, dependsOn: project(':tools:explorer').co
caplets = ['ExplorerCaplet'] caplets = ['ExplorerCaplet']
// JVM configuration: // 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. // - 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 = ['-XX:+UseG1GC']
jvmArgs = ['-Xmx512m', '-XX:+UseG1GC']
} }
} }

View File

@ -20,9 +20,12 @@ import org.crsh.cli.*;
import org.crsh.command.*; import org.crsh.command.*;
import org.crsh.text.*; import org.crsh.text.*;
import org.crsh.text.ui.TableElement; import org.crsh.text.ui.TableElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*; 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.runFlowByNameFragment;
import static net.corda.tools.shell.InteractiveShell.runStateMachinesView; import static net.corda.tools.shell.InteractiveShell.runStateMachinesView;
@ -34,12 +37,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." "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 { public class FlowShellCommand extends InteractiveShellCommand {
private static Logger logger = LoggerFactory.getLogger(FlowShellCommand.class);
@Command @Command
@Usage("Start a (work)flow on the node. This is how you can change the ledger.") @Usage("Start a (work)flow on the node. This is how you can change the ledger.")
public void start( public void start(
@Usage("The class name of the flow to run, or an unambiguous substring") @Argument String name, @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<String> input @Usage("The data to pass as input") @Argument(unquote = false) List<String> input
) { ) {
logger.info("Executing command \"flow start {} {}\",", name, input.stream().collect(joining(" ")));
startFlow(name, input, out, ops(), ansiProgressRenderer(), objectMapper()); startFlow(name, input, out, ops(), ansiProgressRenderer(), objectMapper());
} }
@ -47,6 +54,7 @@ public class FlowShellCommand extends InteractiveShellCommand {
@Command @Command
@Usage("watch information about state machines running on the node with result information") @Usage("watch information about state machines running on the node with result information")
public void watch(InvocationContext<TableElement> context) throws Exception { public void watch(InvocationContext<TableElement> context) throws Exception {
logger.info("Executing command \"flow watch\".");
runStateMachinesView(out, ops()); runStateMachinesView(out, ops());
} }
@ -67,6 +75,7 @@ public class FlowShellCommand extends InteractiveShellCommand {
@Command @Command
@Usage("list flows that user can start") @Usage("list flows that user can start")
public void list(InvocationContext<String> context) throws Exception { public void list(InvocationContext<String> context) throws Exception {
logger.info("Executing command \"flow list\".");
for (String name : ops().registeredFlows()) { for (String name : ops().registeredFlows()) {
context.provide(name + System.lineSeparator()); context.provide(name + System.lineSeparator());
} }

View File

@ -20,6 +20,8 @@ import org.crsh.cli.Man;
import org.crsh.cli.Usage; import org.crsh.cli.Usage;
import org.crsh.command.InvocationContext; import org.crsh.command.InvocationContext;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -27,11 +29,15 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import static java.util.Comparator.comparing; 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<Map<?, ?>> which // Note that this class cannot be converted to Kotlin because CRaSH does not understand InvocationContext<Map<?, ?>> which
// is the closest you can get in Kotlin to raw types. // is the closest you can get in Kotlin to raw types.
public class RunShellCommand extends InteractiveShellCommand { public class RunShellCommand extends InteractiveShellCommand {
private static Logger logger = LoggerFactory.getLogger(RunShellCommand.class);
@Command @Command
@Man( @Man(
"Runs a method from the CordaRPCOps interface, which is the same interface exposed to RPC clients.\n\n" + "Runs a method from the CordaRPCOps interface, which is the same interface exposed to RPC clients.\n\n" +
@ -40,10 +46,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" "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.") @Usage("runs a method from the CordaRPCOps interface on the node.")
public Object main( public Object main(InvocationContext<Map> context, @Usage("The command to run") @Argument(unquote = false) List<String> command) {
InvocationContext<Map> context, logger.info("Executing command \"run {}\",", command.stream().collect(joining(" ")));
@Usage("The command to run") @Argument(unquote = false) List<String> command
) {
StringToMethodCallParser<CordaRPCOps> parser = new StringToMethodCallParser<>(CordaRPCOps.class, objectMapper()); StringToMethodCallParser<CordaRPCOps> parser = new StringToMethodCallParser<>(CordaRPCOps.class, objectMapper());
if (command == null) { if (command == null) {

View File

@ -15,15 +15,24 @@ package net.corda.tools.shell;
import net.corda.tools.shell.utlities.ANSIProgressRenderer; import net.corda.tools.shell.utlities.ANSIProgressRenderer;
import net.corda.tools.shell.utlities.CRaSHANSIProgressRenderer; import net.corda.tools.shell.utlities.CRaSHANSIProgressRenderer;
import org.crsh.cli.*; import org.crsh.cli.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*; import java.util.*;
import static java.util.stream.Collectors.joining;
public class StartShellCommand extends InteractiveShellCommand { public class StartShellCommand extends InteractiveShellCommand {
private static Logger logger = LoggerFactory.getLogger(StartShellCommand.class);
@Command @Command
@Man("An alias for 'flow start'. Example: \"start Yo target: Some other company\"") @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, 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<String> input) { @Usage("The data to pass as input") @Argument(unquote = false) List<String> input) {
logger.info("Executing command \"start {} {}\",", name, input.stream().collect(joining(" ")));
ANSIProgressRenderer ansiProgressRenderer = ansiProgressRenderer(); ANSIProgressRenderer ansiProgressRenderer = ansiProgressRenderer();
FlowShellCommand.startFlow(name, input, out, ops(), ansiProgressRenderer != null ? ansiProgressRenderer : new CRaSHANSIProgressRenderer(out), objectMapper()); FlowShellCommand.startFlow(name, input, out, ops(), ansiProgressRenderer != null ? ansiProgressRenderer : new CRaSHANSIProgressRenderer(out), objectMapper());
} }
} }