Merge pull request #764 from corda/colljos-merge-230418

OS -> ENT merge
This commit is contained in:
josecoll 2018-04-23 13:26:15 +01:00 committed by GitHub
commit 5ae35b70dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 42 additions and 112 deletions

View File

@ -3228,7 +3228,7 @@ public static final class net.corda.core.transactions.FilteredTransaction$Compan
@org.jetbrains.annotations.NotNull public final List inputsOfType(Class) @org.jetbrains.annotations.NotNull public final List inputsOfType(Class)
public String toString() public String toString()
public final void verify() public final void verify()
public static final net.corda.core.transactions.LedgerTransaction$Companion Companion @java.lang.Deprecated public static final net.corda.core.transactions.LedgerTransaction$Companion Companion
## ##
public static final class net.corda.core.transactions.LedgerTransaction$InOutGroup extends java.lang.Object public static final class net.corda.core.transactions.LedgerTransaction$InOutGroup extends java.lang.Object
public <init>(List, List, Object) public <init>(List, List, Object)
@ -3334,7 +3334,7 @@ public static final class net.corda.core.transactions.NotaryChangeWireTransactio
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction withAdditionalSignature(java.security.KeyPair, net.corda.core.crypto.SignatureMetadata) @org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction withAdditionalSignature(java.security.KeyPair, net.corda.core.crypto.SignatureMetadata)
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction withAdditionalSignature(net.corda.core.crypto.TransactionSignature) @org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction withAdditionalSignature(net.corda.core.crypto.TransactionSignature)
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction withAdditionalSignatures(Iterable) @org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction withAdditionalSignatures(Iterable)
public static final net.corda.core.transactions.SignedTransaction$Companion Companion @java.lang.Deprecated public static final net.corda.core.transactions.SignedTransaction$Companion Companion
## ##
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.transactions.SignedTransaction$SignaturesMissingException extends java.security.SignatureException implements net.corda.core.CordaThrowable, net.corda.core.contracts.NamedByHash @net.corda.core.serialization.CordaSerializable public static final class net.corda.core.transactions.SignedTransaction$SignaturesMissingException extends java.security.SignatureException implements net.corda.core.CordaThrowable, net.corda.core.contracts.NamedByHash
public <init>(Set, List, net.corda.core.crypto.SecureHash) public <init>(Set, List, net.corda.core.crypto.SecureHash)

View File

@ -183,8 +183,8 @@ allprojects {
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions { kotlinOptions {
languageVersion = "1.1" languageVersion = "1.2"
apiVersion = "1.1" apiVersion = "1.2"
jvmTarget = "1.8" jvmTarget = "1.8"
javaParameters = true // Useful for reflection. javaParameters = true // Useful for reflection.
} }

View File

@ -474,3 +474,8 @@ fun NotarisationRequest.generateSignature(serviceHub: ServiceHub): NotarisationR
} }
val PublicKey.hash: SecureHash get() = encoded.sha256() val PublicKey.hash: SecureHash get() = encoded.sha256()
/**
* Extension method for providing a sumBy method that processes and returns a Long
*/
fun <T> Iterable<T>.sumByLong(selector: (T) -> Long): Long = this.map { selector(it) }.sum()

View File

@ -140,7 +140,3 @@ fun <V> Future<V>.getOrThrow(timeout: Duration? = null): V = try {
throw e.cause!! throw e.cause!!
} }
/**
* Extension method for providing a sumBy method that processes and returns a Long
*/
fun <T> Iterable<T>.sumByLong(selector: (T) -> Long): Long = this.map { selector(it) }.sum()

View File

@ -11,6 +11,7 @@
package net.corda.core.flows; package net.corda.core.flows;
import co.paralleluniverse.fibers.Suspendable; import co.paralleluniverse.fibers.Suspendable;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Primitives; import com.google.common.primitives.Primitives;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import net.corda.testing.core.TestConstants; import net.corda.testing.core.TestConstants;
@ -23,13 +24,12 @@ import org.junit.Test;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import static java.util.Collections.emptyList;
import static net.corda.testing.core.TestUtils.singleIdentity; import static net.corda.testing.core.TestUtils.singleIdentity;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
public class FlowsInJavaTest { public class FlowsInJavaTest {
private final MockNetwork mockNet = new MockNetwork(emptyList()); private final MockNetwork mockNet = new MockNetwork(ImmutableList.of("net.corda.core.flows"));
private StartedMockNode aliceNode; private StartedMockNode aliceNode;
private StartedMockNode bobNode; private StartedMockNode bobNode;
private Party bob; private Party bob;
@ -48,7 +48,6 @@ public class FlowsInJavaTest {
@Test @Test
public void suspendableActionInsideUnwrap() throws Exception { public void suspendableActionInsideUnwrap() throws Exception {
bobNode.registerInitiatedFlow(SendHelloAndThenReceive.class);
Future<String> result = aliceNode.startFlow(new SendInUnwrapFlow(bob)); Future<String> result = aliceNode.startFlow(new SendInUnwrapFlow(bob));
mockNet.runNetwork(); mockNet.runNetwork();
assertThat(result.get()).isEqualTo("Hello"); assertThat(result.get()).isEqualTo("Hello");

View File

@ -51,7 +51,7 @@ class CollectSignaturesFlowTests {
@Before @Before
fun setup() { fun setup() {
mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.core.flows"))
aliceNode = mockNet.createPartyNode(ALICE_NAME) aliceNode = mockNet.createPartyNode(ALICE_NAME)
bobNode = mockNet.createPartyNode(BOB_NAME) bobNode = mockNet.createPartyNode(BOB_NAME)
charlieNode = mockNet.createPartyNode(CHARLIE_NAME) charlieNode = mockNet.createPartyNode(CHARLIE_NAME)
@ -66,12 +66,6 @@ class CollectSignaturesFlowTests {
mockNet.stopNodes() mockNet.stopNodes()
} }
private fun registerFlowOnAllNodes(flowClass: KClass<out FlowLogic<*>>) {
listOf(aliceNode, bobNode, charlieNode).forEach {
it.registerInitiatedFlow(flowClass.java)
}
}
// With this flow, the initiator starts the "CollectTransactionFlow". It is then the responders responsibility to // With this flow, the initiator starts the "CollectTransactionFlow". It is then the responders responsibility to
// override "checkTransaction" and add whatever logic their require to verify the SignedTransaction they are // override "checkTransaction" and add whatever logic their require to verify the SignedTransaction they are
// receiving off the wire. // receiving off the wire.
@ -120,7 +114,6 @@ class CollectSignaturesFlowTests {
// Normally this is handled by TransactionKeyFlow, but here we have to manually let A know about the identity // Normally this is handled by TransactionKeyFlow, but here we have to manually let A know about the identity
aliceNode.services.identityService.verifyAndRegisterIdentity(bConfidentialIdentity) aliceNode.services.identityService.verifyAndRegisterIdentity(bConfidentialIdentity)
} }
registerFlowOnAllNodes(TestFlow.Responder::class)
val magicNumber = 1337 val magicNumber = 1337
val parties = listOf(alice, bConfidentialIdentity.party, charlie) val parties = listOf(alice, bConfidentialIdentity.party, charlie)
val state = DummyContract.MultiOwnerState(magicNumber, parties) val state = DummyContract.MultiOwnerState(magicNumber, parties)

View File

@ -48,12 +48,10 @@ class ResolveTransactionsFlowTest {
@Before @Before
fun setup() { fun setup() {
mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.core.internal"))
notaryNode = mockNet.defaultNotaryNode notaryNode = mockNet.defaultNotaryNode
megaCorpNode = mockNet.createPartyNode(CordaX500Name("MegaCorp", "London", "GB")) megaCorpNode = mockNet.createPartyNode(CordaX500Name("MegaCorp", "London", "GB"))
miniCorpNode = mockNet.createPartyNode(CordaX500Name("MiniCorp", "London", "GB")) miniCorpNode = mockNet.createPartyNode(CordaX500Name("MiniCorp", "London", "GB"))
megaCorpNode.registerInitiatedFlow(TestResponseFlow::class.java)
miniCorpNode.registerInitiatedFlow(TestResponseFlow::class.java)
notary = mockNet.defaultNotaryIdentity notary = mockNet.defaultNotaryIdentity
megaCorp = megaCorpNode.info.singleIdentity() megaCorp = megaCorpNode.info.singleIdentity()
miniCorp = miniCorpNode.info.singleIdentity() miniCorp = miniCorpNode.info.singleIdentity()

View File

@ -165,24 +165,6 @@ Nodes are created on the ``MockNetwork`` using:
} }
} }
Registering a node's initiated flows
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Regular Corda nodes automatically register any response flows defined in their installed CorDapps. When using a
``MockNetwork``, each ``StartedMockNode`` must manually register any responder flows it wishes to use.
Responder flows are registered as follows:
.. container:: codeset
.. sourcecode:: kotlin
nodeA.registerInitiatedFlow(ExampleFlow.Acceptor::class.java)
.. sourcecode:: java
nodeA.registerInitiatedFlow(ExampleFlow.Acceptor.class);
Running the network Running the network
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^

View File

@ -11,6 +11,7 @@
package net.corda.docs.tutorial.mocknetwork package net.corda.docs.tutorial.mocknetwork
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import com.google.common.collect.ImmutableList
import net.corda.core.contracts.requireThat import net.corda.core.contracts.requireThat
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession import net.corda.core.flows.FlowSession
@ -72,10 +73,9 @@ class TutorialMockNetwork {
@Before @Before
fun setUp() { fun setUp() {
mockNet = MockNetwork(emptyList()) mockNet = MockNetwork(ImmutableList.of("net.corda.docs.tutorial.mocknetwork"))
nodeA = mockNet.createPartyNode() nodeA = mockNet.createPartyNode()
nodeB = mockNet.createPartyNode() nodeB = mockNet.createPartyNode()
nodeB.registerInitiatedFlow(FlowB::class.java)
} }
@After @After

View File

@ -61,10 +61,6 @@ transactions are valid) inside a ``database.transaction``. All node flows run w
nodes themselves, but any time we need to use the database directly from a unit test, you need to provide a database nodes themselves, but any time we need to use the database directly from a unit test, you need to provide a database
transaction as shown here. transaction as shown here.
With regards to initiated flows (see :doc:`flow-state-machines` for information on initiated and initiating flows), the
full node automatically registers them by scanning the CorDapp jars. In a unit test environment this is not possible so
``MockNode`` has the ``registerInitiatedFlow`` method to manually register an initiated flow.
.. MockNetwork message manipulation .. MockNetwork message manipulation
.. -------------------------------- .. --------------------------------
.. The MockNetwork has the ability to manipulate message streams. You can use this to test your flows behaviour on corrupted, .. The MockNetwork has the ability to manipulate message streams. You can use this to test your flows behaviour on corrupted,

View File

@ -15,6 +15,7 @@ import net.corda.behave.file.currentDirectory
import net.corda.behave.logging.getLogger import net.corda.behave.logging.getLogger
import net.corda.behave.process.output.OutputListener import net.corda.behave.process.output.OutputListener
import rx.Observable import rx.Observable
import rx.Subscriber
import java.io.Closeable import java.io.Closeable
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@ -153,8 +154,9 @@ open class Command(
return exitCode return exitCode
} }
fun use(action: (Command, Observable<String>) -> Unit = { _, _ -> }): Int { fun use(subscriber: Subscriber<String>, action: (Command, Observable<String>) -> Unit = { _, _ -> }): Int {
try { try {
output.subscribe(subscriber)
start() start()
action(this, output) action(this, output)
} finally { } finally {

View File

@ -1,18 +0,0 @@
package net.corda.behave.scenarios
import cucumber.api.java.After
import cucumber.api.java.Before
@Suppress("KDocMissingDocumentation")
class ScenarioHooks(private val state: ScenarioState) {
@Before
fun beforeScenario() {
}
@After
fun afterScenario() {
state.stopNetwork()
}
}

View File

@ -4,7 +4,7 @@ import net.corda.behave.scenarios.ScenarioState
import net.corda.behave.scenarios.api.StepsBlock import net.corda.behave.scenarios.api.StepsBlock
import net.corda.behave.scenarios.helpers.Vault import net.corda.behave.scenarios.helpers.Vault
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.utilities.sumByLong import net.corda.core.internal.sumByLong
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
class VaultSteps : StepsBlock { class VaultSteps : StepsBlock {

View File

@ -31,8 +31,7 @@ class CommandTests {
@Test @Test
fun `output stream for command can be observed`() { fun `output stream for command can be observed`() {
val subscriber = TestSubscriber<String>() val subscriber = TestSubscriber<String>()
val exitCode = Command(listOf("ls", "/")).use { _, output -> val exitCode = Command(listOf("ls", "/")).use(subscriber) { _, output ->
output.subscribe(subscriber)
subscriber.awaitTerminalEvent() subscriber.awaitTerminalEvent()
subscriber.assertCompleted() subscriber.assertCompleted()
subscriber.assertNoErrors() subscriber.assertNoErrors()

View File

@ -1,29 +0,0 @@
/*
* R3 Proprietary and Confidential
*
* Copyright (c) 2018 R3 Limited. All rights reserved.
*
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
*
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package net.corda.behave.service
import net.corda.behave.service.database.PostgreSQLService
import org.assertj.core.api.Assertions.assertThat
import org.junit.Ignore
import org.junit.Test
class PostreSQLServiceTests {
@Ignore
@Test
fun `postgres can be started and stopped`() {
val service = PostgreSQLService("test-postgres", 12345, "postgres")
val didStart = service.start()
service.stop()
assertThat(didStart).isTrue()
}
}

View File

@ -13,40 +13,53 @@ package net.corda.nodeapi.internal
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
import net.corda.core.contracts.Contract import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractClassName import net.corda.core.contracts.ContractClassName
import net.corda.core.contracts.UpgradedContract
import net.corda.core.contracts.UpgradedContractWithLegacyConstraint
import net.corda.core.internal.copyTo import net.corda.core.internal.copyTo
import net.corda.core.internal.deleteIfExists import net.corda.core.internal.deleteIfExists
import net.corda.core.internal.logElapsedTime
import net.corda.core.internal.read import net.corda.core.internal.read
import org.slf4j.LoggerFactory
import java.io.InputStream import java.io.InputStream
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import java.net.URLClassLoader import java.net.URLClassLoader
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption
import java.util.Collections.singleton
// When scanning of the CorDapp Jar is performed without "corda-core.jar" being the in the classpath, there is no way to appreciate
// relationships between those interfaces, therefore they have to be listed explicitly.
val coreContractClasses = setOf(Contract::class, UpgradedContractWithLegacyConstraint::class, UpgradedContract::class)
/** /**
* Scans the jar for contracts. * Scans the jar for contracts.
* @returns: found contract class names or null if none found * @returns: found contract class names or null if none found
*/ */
fun scanJarForContracts(cordappJar: Path): List<ContractClassName> { fun scanJarForContracts(cordappJar: Path): List<ContractClassName> {
val currentClassLoader = Contract::class.java.classLoader
val scanResult = FastClasspathScanner() val scanResult = FastClasspathScanner()
.addClassLoader(currentClassLoader) // A set of a single element may look odd, but if this is removed "Path" which itself is an `Iterable`
.overrideClasspath(cordappJar, Paths.get(Contract::class.java.protectionDomain.codeSource.location.toURI())) // is getting broken into pieces to scan individually, which doesn't yield desired effect.
.overrideClasspath(singleton(cordappJar))
.scan() .scan()
val contracts = (scanResult.getNamesOfClassesImplementing(Contract::class.qualifiedName) ).distinct() val contracts = coreContractClasses.flatMap { contractClass -> scanResult.getNamesOfClassesImplementing(contractClass.qualifiedName) }.distinct()
// Only keep instantiable contracts // Only keep instantiable contracts
return URLClassLoader(arrayOf(cordappJar.toUri().toURL()), currentClassLoader).use { return URLClassLoader(arrayOf(cordappJar.toUri().toURL()), Contract::class.java.classLoader).use {
contracts.map(it::loadClass).filter { !it.isInterface && !Modifier.isAbstract(it.modifiers) } contracts.map(it::loadClass).filter { !it.isInterface && !Modifier.isAbstract(it.modifiers) }
}.map { it.name } }.map { it.name }
} }
private val logger = LoggerFactory.getLogger("ClassloaderUtils")
fun <T> withContractsInJar(jarInputStream: InputStream, withContracts: (List<ContractClassName>, InputStream) -> T): T { fun <T> withContractsInJar(jarInputStream: InputStream, withContracts: (List<ContractClassName>, InputStream) -> T): T {
val tempFile = Files.createTempFile("attachment", ".jar") val tempFile = Files.createTempFile("attachment", ".jar")
try { try {
jarInputStream.copyTo(tempFile, StandardCopyOption.REPLACE_EXISTING) jarInputStream.copyTo(tempFile, StandardCopyOption.REPLACE_EXISTING)
val contracts = scanJarForContracts(tempFile.toAbsolutePath()) val cordappJar = tempFile.toAbsolutePath()
val contracts = logElapsedTime("Contracts loading for '$cordappJar'", logger) {
scanJarForContracts(cordappJar)
}
return tempFile.read { withContracts(contracts, it) } return tempFile.read { withContracts(contracts, it) }
} finally { } finally {
tempFile.deleteIfExists() tempFile.deleteIfExists()

View File

@ -27,6 +27,7 @@ import net.corda.core.serialization.SerializeAsToken
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.internal.classloading.requireAnnotation
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.coreContractClasses
import net.corda.nodeapi.internal.serialization.DefaultWhitelist import net.corda.nodeapi.internal.serialization.DefaultWhitelist
import org.apache.commons.collections4.map.LRUMap import org.apache.commons.collections4.map.LRUMap
import java.io.File import java.io.File
@ -251,13 +252,7 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
} }
private fun findContractClassNames(scanResult: RestrictedScanResult): List<String> { private fun findContractClassNames(scanResult: RestrictedScanResult): List<String> {
return (scanResult.getNamesOfClassesImplementing(Contract::class) + return coreContractClasses.flatMap { scanResult.getNamesOfClassesImplementing(it) }.distinct()
scanResult.getNamesOfClassesImplementing(UpgradedContract::class) +
// Even though UpgradedContractWithLegacyConstraint implements UpgradedContract
// we need to specify it separately. Otherwise, classes implementing UpgradedContractWithLegacyConstraint
// don't get picked up.
scanResult.getNamesOfClassesImplementing(UpgradedContractWithLegacyConstraint::class))
.distinct()
} }
private fun findPlugins(cordappJarPath: RestrictedURL): List<SerializationWhitelist> { private fun findPlugins(cordappJarPath: RestrictedURL): List<SerializationWhitelist> {

View File

@ -59,7 +59,6 @@ class TraderDemoTest : IntegrationTest() {
startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser)), startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser)),
startNode(providedName = BOC_NAME, rpcUsers = listOf(bankUser)) startNode(providedName = BOC_NAME, rpcUsers = listOf(bankUser))
).map { (it.getOrThrow() as InProcess) } ).map { (it.getOrThrow() as InProcess) }
nodeA.registerInitiatedFlow(BuyerFlow::class.java)
val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map { val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map {
val client = CordaRPCClient(it.rpcAddress) val client = CordaRPCClient(it.rpcAddress)
client.start(demoUser.username, demoUser.password).proxy client.start(demoUser.username, demoUser.password).proxy