diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 62d77dd2d8..5aec9bd194 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,15 +1,11 @@
-Thank you for choosing to contribute to Corda.
+👮🏻👮🏻👮🏻 !!!! DESCRIBE YOUR CHANGES HERE !!!! DO NOT FORGET !!!! 👮🏻👮🏻👮🏻
-Your PR must be approved by one or more reviewers and all tests must be passed on TeamCity (https://ci.corda.r3cev.com) in order to be merged.
-Once you have submitted a PR you are responsible for keeping it up to date until the time it is merged.
+# PR Checklist:
-PR Checklist:
+- [ ] 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.
-1. Ensure any new code is tested as described in https://docs.corda.net/testing.html
-2. Ensure you have done any relevant automated testing and manual testing
-3. Add your changes to docs/source/changelog.rst
-4. Update any documentation in docs/source relating to your changes and learn how to build them in https://docs.corda.net/building-the-docs.html
-5. If you are contributing for the first time please read the agreement in CONTRIBUTING.md now and add to this Pull Request that you have read, and agreed to, the agreement.
-
-Please remove this message when you have read it.
+Thanks for your code, it's appreciated! :)
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 9e73df7661..25abc91c93 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -12,6 +12,8 @@
+
+
@@ -19,6 +21,9 @@
+
+
+
@@ -72,8 +77,12 @@
+
+
+
+
@@ -93,6 +102,13 @@
+
+
+
+
+
+
+
diff --git a/.idea/runConfigurations/explorer.xml b/.idea/runConfigurations/Explorer___GUI.xml
similarity index 80%
rename from .idea/runConfigurations/explorer.xml
rename to .idea/runConfigurations/Explorer___GUI.xml
index df90329e1c..98113d92f5 100644
--- a/.idea/runConfigurations/explorer.xml
+++ b/.idea/runConfigurations/Explorer___GUI.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/build.gradle b/build.gradle
index dd7a6af5da..468c1363a0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -36,7 +36,7 @@ buildscript {
ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion")
ext.fileupload_version = '1.3.2'
ext.junit_version = '4.12'
- ext.mockito_version = '1.10.19'
+ ext.mockito_version = '2.10.0'
ext.jopt_simple_version = '5.0.2'
ext.jansi_version = '1.14'
ext.hibernate_version = '5.2.6.Final'
@@ -146,10 +146,16 @@ allprojects {
maven { url 'https://jitpack.io' }
}
- configurations.compile {
- // We want to use SLF4J's version of these bindings: jcl-over-slf4j
- // Remove any transitive dependency on Apache's version.
- exclude group: 'commons-logging', module: 'commons-logging'
+ configurations {
+ compile {
+ // We want to use SLF4J's version of these bindings: jcl-over-slf4j
+ // Remove any transitive dependency on Apache's version.
+ exclude group: 'commons-logging', module: 'commons-logging'
+ }
+ runtime {
+ // We never want isolated.jar on classPath, since we want to test jar being dynamically loaded as an attachment
+ exclude module: 'isolated'
+ }
}
}
@@ -184,6 +190,7 @@ dependencies {
cordaRuntime project(':client:mock')
cordaRuntime project(':client:rpc')
cordaRuntime project(':core')
+ cordaRuntime project(':confidential-identities')
cordaRuntime project(':finance')
cordaRuntime project(':webserver')
testCompile project(':test-utils')
@@ -252,7 +259,7 @@ bintrayConfig {
projectUrl = 'https://github.com/corda/corda'
gpgSign = true
gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE')
- publications = ['corda-jfx', 'corda-mock', 'corda-rpc', 'corda-core', 'corda', 'corda-finance', 'corda-node', 'corda-node-api', 'corda-test-common', 'corda-test-utils', 'corda-jackson', 'corda-verifier', 'corda-webserver-impl', 'corda-webserver', 'corda-node-driver']
+ publications = ['corda-jfx', 'corda-mock', 'corda-rpc', 'corda-core', 'corda', 'corda-finance', 'corda-node', 'corda-node-api', 'corda-test-common', 'corda-test-utils', 'corda-jackson', 'corda-verifier', 'corda-webserver-impl', 'corda-webserver', 'corda-node-driver', 'corda-confidential-identities']
license {
name = 'Apache-2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0'
@@ -287,7 +294,7 @@ artifactory {
password = System.getenv('CORDA_ARTIFACTORY_PASSWORD')
}
defaults {
- publications('corda-jfx', 'corda-mock', 'corda-rpc', 'corda-core', 'corda', 'cordform-common', 'corda-finance', 'corda-node', 'corda-node-api', 'corda-test-common', 'corda-test-utils', 'corda-jackson', 'corda-verifier', 'corda-webserver-impl', 'corda-webserver', 'corda-node-driver')
+ publications('corda-jfx', 'corda-mock', 'corda-rpc', 'corda-core', 'corda', 'cordform-common', 'corda-finance', 'corda-node', 'corda-node-api', 'corda-test-common', 'corda-test-utils', 'corda-jackson', 'corda-verifier', 'corda-webserver-impl', 'corda-webserver', 'corda-node-driver', 'corda-confidential-identities')
}
}
}
diff --git a/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt b/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt
index a8418809da..a9935f12fc 100644
--- a/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt
+++ b/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt
@@ -15,6 +15,7 @@ import net.corda.core.crypto.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
+import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.node.NodeInfo
@@ -30,7 +31,6 @@ import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.parsePublicKeyBase58
import net.corda.core.utilities.toBase58String
import net.i2p.crypto.eddsa.EdDSAPublicKey
-import org.bouncycastle.asn1.x500.X500Name
import java.math.BigDecimal
import java.security.PublicKey
import java.util.*
@@ -46,25 +46,25 @@ object JacksonSupport {
// If you change this API please update the docs in the docsite (json.rst)
interface PartyObjectMapper {
- fun partyFromX500Name(name: X500Name): Party?
+ fun wellKnownPartyFromX500Name(name: CordaX500Name): Party?
fun partyFromKey(owningKey: PublicKey): Party?
fun partiesFromName(query: String): Set
}
class RpcObjectMapper(val rpc: CordaRPCOps, factory: JsonFactory, val fuzzyIdentityMatch: Boolean) : PartyObjectMapper, ObjectMapper(factory) {
- override fun partyFromX500Name(name: X500Name): Party? = rpc.partyFromX500Name(name)
+ override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = rpc.wellKnownPartyFromX500Name(name)
override fun partyFromKey(owningKey: PublicKey): Party? = rpc.partyFromKey(owningKey)
override fun partiesFromName(query: String) = rpc.partiesFromName(query, fuzzyIdentityMatch)
}
class IdentityObjectMapper(val identityService: IdentityService, factory: JsonFactory, val fuzzyIdentityMatch: Boolean) : PartyObjectMapper, ObjectMapper(factory) {
- override fun partyFromX500Name(name: X500Name): Party? = identityService.partyFromX500Name(name)
+ override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = identityService.wellKnownPartyFromX500Name(name)
override fun partyFromKey(owningKey: PublicKey): Party? = identityService.partyFromKey(owningKey)
override fun partiesFromName(query: String) = identityService.partiesFromName(query, fuzzyIdentityMatch)
}
class NoPartyObjectMapper(factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
- override fun partyFromX500Name(name: X500Name): Party? = throw UnsupportedOperationException()
+ override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = throw UnsupportedOperationException()
override fun partyFromKey(owningKey: PublicKey): Party? = throw UnsupportedOperationException()
override fun partiesFromName(query: String) = throw UnsupportedOperationException()
}
@@ -105,8 +105,8 @@ object JacksonSupport {
addSerializer(OpaqueBytes::class.java, OpaqueBytesSerializer)
// For X.500 distinguished names
- addDeserializer(X500Name::class.java, X500NameDeserializer)
- addSerializer(X500Name::class.java, X500NameSerializer)
+ addDeserializer(CordaX500Name::class.java, CordaX500NameDeserializer)
+ addSerializer(CordaX500Name::class.java, CordaX500NameSerializer)
// Mixins for transaction types to prevent some properties from being serialized
setMixInAnnotation(SignedTransaction::class.java, SignedTransactionMixin::class.java)
@@ -191,8 +191,8 @@ object JacksonSupport {
// Base58 keys never include an equals character, while X.500 names always will, so we use that to determine
// how to parse the content
return if (parser.text.contains("=")) {
- val principal = X500Name(parser.text)
- mapper.partyFromX500Name(principal) ?: throw JsonParseException(parser, "Could not find a Party with name $principal")
+ val principal = CordaX500Name.parse(parser.text)
+ mapper.wellKnownPartyFromX500Name(principal) ?: throw JsonParseException(parser, "Could not find a Party with name $principal")
} else {
val nameMatches = mapper.partiesFromName(parser.text)
if (nameMatches.isEmpty()) {
@@ -211,22 +211,22 @@ object JacksonSupport {
}
}
- object X500NameSerializer : JsonSerializer() {
- override fun serialize(obj: X500Name, generator: JsonGenerator, provider: SerializerProvider) {
+ object CordaX500NameSerializer : JsonSerializer() {
+ override fun serialize(obj: CordaX500Name, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeString(obj.toString())
}
}
- object X500NameDeserializer : JsonDeserializer() {
- override fun deserialize(parser: JsonParser, context: DeserializationContext): X500Name {
+ object CordaX500NameDeserializer : JsonDeserializer() {
+ override fun deserialize(parser: JsonParser, context: DeserializationContext): CordaX500Name {
if (parser.currentToken == JsonToken.FIELD_NAME) {
parser.nextToken()
}
return try {
- X500Name(parser.text)
+ CordaX500Name.parse(parser.text)
} catch(ex: IllegalArgumentException) {
- throw JsonParseException(parser, "Invalid X.500 name ${parser.text}: ${ex.message}", ex)
+ throw JsonParseException(parser, "Invalid Corda X.500 name ${parser.text}: ${ex.message}", ex)
}
}
}
diff --git a/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt b/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt
index fec036aeb2..961e2978ac 100644
--- a/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt
+++ b/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt
@@ -1,21 +1,24 @@
package net.corda.client.jackson
import com.fasterxml.jackson.databind.SerializationFeature
+import com.nhaarman.mockito_kotlin.mock
+import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.Amount
-import net.corda.finance.USD
-import net.corda.core.crypto.Crypto
-import net.corda.core.crypto.SignatureMetadata
-import net.corda.core.crypto.TransactionSignature
-import net.corda.core.crypto.generateKeyPair
+import net.corda.core.cordapp.CordappProvider
+import net.corda.core.crypto.*
+import net.corda.core.node.ServiceHub
import net.corda.core.transactions.SignedTransaction
+import net.corda.finance.USD
import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.MINI_CORP
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.contracts.DummyContract
import net.i2p.crypto.eddsa.EdDSAPublicKey
+import org.junit.Before
import org.junit.Test
import java.util.*
+import kotlin.reflect.jvm.jvmName
import kotlin.test.assertEquals
class JacksonSupportTest : TestDependencyInjectionBase() {
@@ -23,6 +26,16 @@ class JacksonSupportTest : TestDependencyInjectionBase() {
val mapper = JacksonSupport.createNonRpcMapper()
}
+ private lateinit var services: ServiceHub
+ private lateinit var cordappProvider: CordappProvider
+
+ @Before
+ fun setup() {
+ services = mock()
+ cordappProvider = mock()
+ whenever(services.cordappProvider).thenReturn(cordappProvider)
+ }
+
@Test
fun publicKeySerializingWorks() {
val publicKey = generateKeyPair().public
@@ -57,8 +70,12 @@ class JacksonSupportTest : TestDependencyInjectionBase() {
@Test
fun writeTransaction() {
+ val attachmentRef = SecureHash.randomSHA256()
+ whenever(cordappProvider.getContractAttachmentID(DummyContract.PROGRAM_ID))
+ .thenReturn(attachmentRef)
fun makeDummyTx(): SignedTransaction {
- val wtx = DummyContract.generateInitial(1, DUMMY_NOTARY, MINI_CORP.ref(1)).toWireTransaction()
+ val wtx = DummyContract.generateInitial(1, DUMMY_NOTARY, MINI_CORP.ref(1))
+ .toWireTransaction(services)
val signatures = TransactionSignature(
ByteArray(1),
ALICE_PUBKEY,
diff --git a/client/jfx/build.gradle b/client/jfx/build.gradle
index ef9602e810..7dd7878175 100644
--- a/client/jfx/build.gradle
+++ b/client/jfx/build.gradle
@@ -7,9 +7,6 @@ description 'Corda client JavaFX modules'
//noinspection GroovyAssignabilityCheck
configurations {
- // we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment
- runtime.exclude module: 'isolated'
-
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
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 20e9c8dda5..bf5140fc36 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
@@ -8,6 +8,8 @@ import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.keys
import net.corda.core.flows.FlowInitiator
import net.corda.core.flows.StateMachineRunId
+import net.corda.core.identity.CordaX500Name
+import net.corda.core.identity.Party
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.StateMachineTransactionMapping
@@ -15,7 +17,6 @@ import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.messaging.startFlow
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache
-import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.Vault
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.OpaqueBytes
@@ -25,21 +26,20 @@ import net.corda.finance.USD
import net.corda.finance.flows.CashExitFlow
import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
-import net.corda.node.services.network.NetworkMapService
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
import net.corda.testing.node.DriverBasedTest
-import org.bouncycastle.asn1.x500.X500Name
import org.junit.Test
import rx.Observable
class NodeMonitorModelTest : DriverBasedTest() {
lateinit var aliceNode: NodeInfo
lateinit var bobNode: NodeInfo
- lateinit var notaryNode: NodeInfo
+ lateinit var notaryParty: Party
lateinit var rpc: CordaRPCOps
lateinit var rpcBob: CordaRPCOps
@@ -50,20 +50,18 @@ class NodeMonitorModelTest : DriverBasedTest() {
lateinit var transactions: Observable
lateinit var vaultUpdates: Observable>
lateinit var networkMapUpdates: Observable
- lateinit var newNode: (X500Name) -> NodeInfo
+ lateinit var newNode: (CordaX500Name) -> NodeInfo
- override fun setup() = driver {
+ override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) {
val cashUser = User("user1", "test", permissions = setOf(
startFlowPermission(),
startFlowPermission(),
startFlowPermission())
)
val aliceNodeFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser))
- val notaryNodeFuture = startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)))
+ val notaryHandle = startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))).getOrThrow()
val aliceNodeHandle = aliceNodeFuture.getOrThrow()
- val notaryNodeHandle = notaryNodeFuture.getOrThrow()
aliceNode = aliceNodeHandle.nodeInfo
- notaryNode = notaryNodeHandle.nodeInfo
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
val monitor = NodeMonitorModel()
stateMachineTransactionMapping = monitor.stateMachineTransactionMapping.bufferUntilSubscribed()
@@ -75,6 +73,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password, initialiseSerialization = false)
rpc = monitor.proxyObservable.value!!
+ notaryParty = notaryHandle.nodeInfo.legalIdentities[1]
val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow()
bobNode = bobNodeHandle.nodeInfo
@@ -87,20 +86,20 @@ class NodeMonitorModelTest : DriverBasedTest() {
@Test
fun `network map update`() {
- newNode(CHARLIE.name)
- networkMapUpdates.filter { !it.node.advertisedServices.any { it.info.type.isNotary() } }
- .filter { !it.node.advertisedServices.any { it.info.type == NetworkMapService.type } }
+ val charlieNode = newNode(CHARLIE.name)
+ val nonServiceIdentities = aliceNode.legalIdentitiesAndCerts + bobNode.legalIdentitiesAndCerts + charlieNode.legalIdentitiesAndCerts
+ networkMapUpdates.filter { it.node.legalIdentitiesAndCerts.any { it in nonServiceIdentities } }
.expectEvents(isStrict = false) {
sequence(
// TODO : Add test for remove when driver DSL support individual node shutdown.
expect { output: NetworkMapCache.MapChange ->
- require(output.node.legalIdentity.name == ALICE.name) { "Expecting : ${ALICE.name}, Actual : ${output.node.legalIdentity.name}" }
+ require(output.node.chooseIdentity().name == ALICE.name) { "Expecting : ${ALICE.name}, Actual : ${output.node.chooseIdentity().name}" }
},
expect { output: NetworkMapCache.MapChange ->
- require(output.node.legalIdentity.name == BOB.name) { "Expecting : ${BOB.name}, Actual : ${output.node.legalIdentity.name}" }
+ require(output.node.chooseIdentity().name == BOB.name) { "Expecting : ${BOB.name}, Actual : ${output.node.chooseIdentity().name}" }
},
expect { output: NetworkMapCache.MapChange ->
- require(output.node.legalIdentity.name == CHARLIE.name) { "Expecting : ${CHARLIE.name}, Actual : ${output.node.legalIdentity.name}" }
+ require(output.node.chooseIdentity().name == CHARLIE.name) { "Expecting : ${CHARLIE.name}, Actual : ${output.node.chooseIdentity().name}" }
}
)
}
@@ -111,7 +110,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
rpc.startFlow(::CashIssueFlow,
Amount(100, USD),
OpaqueBytes(ByteArray(1, { 1 })),
- notaryNode.notaryIdentity
+ notaryParty
)
vaultUpdates.expectEvents(isStrict = false) {
@@ -132,9 +131,8 @@ class NodeMonitorModelTest : DriverBasedTest() {
@Test
fun `cash issue and move`() {
- val anonymous = false
- rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryNode.notaryIdentity).returnValue.getOrThrow()
- rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.legalIdentity, anonymous).returnValue.getOrThrow()
+ val (_, issueIdentity) = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryParty).returnValue.getOrThrow()
+ rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.chooseIdentity()).returnValue.getOrThrow()
var issueSmId: StateMachineRunId? = null
var moveSmId: StateMachineRunId? = null
@@ -152,7 +150,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
require(remove.id == issueSmId)
},
// MOVE - N.B. There are other framework flows that happen in parallel for the remote resolve transactions flow
- expect(match = { it is StateMachineUpdate.Added && it.stateMachineInfo.flowLogicClassName == CashPaymentFlow::class.java.name }) { add: StateMachineUpdate.Added ->
+ expect(match = { it.stateMachineInfo.flowLogicClassName == CashPaymentFlow::class.java.name }) { add: StateMachineUpdate.Added ->
moveSmId = add.id
val initiator = add.stateMachineInfo.initiator
require(initiator is FlowInitiator.RPC && initiator.username == "user1")
@@ -167,7 +165,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
// MOVE
expect { add: StateMachineUpdate.Added ->
val initiator = add.stateMachineInfo.initiator
- require(initiator is FlowInitiator.Peer && initiator.party.name == aliceNode.legalIdentity.name)
+ require(initiator is FlowInitiator.Peer && aliceNode.isLegalIdentity(initiator.party))
}
)
}
@@ -180,7 +178,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
require(stx.tx.outputs.size == 1)
val signaturePubKeys = stx.sigs.map { it.by }.toSet()
// Only Alice signed
- val aliceKey = aliceNode.legalIdentity.owningKey
+ val aliceKey = aliceNode.chooseIdentity().owningKey
require(signaturePubKeys.size <= aliceKey.keys.size)
require(aliceKey.isFulfilledBy(signaturePubKeys))
issueTx = stx
@@ -191,8 +189,8 @@ class NodeMonitorModelTest : DriverBasedTest() {
require(stx.tx.outputs.size == 1)
val signaturePubKeys = stx.sigs.map { it.by }.toSet()
// Alice and Notary signed
- require(aliceNode.legalIdentity.owningKey.isFulfilledBy(signaturePubKeys))
- require(notaryNode.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys))
+ require(issueIdentity!!.owningKey.isFulfilledBy(signaturePubKeys))
+ require(notaryParty.owningKey.isFulfilledBy(signaturePubKeys))
moveTx = stx
}
)
diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/Models.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/Models.kt
index 57f939e672..71a6b66733 100644
--- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/Models.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/Models.kt
@@ -11,20 +11,16 @@ import rx.Observer
import rx.subjects.Subject
import java.util.*
import kotlin.reflect.KClass
-import kotlin.reflect.KProperty
/**
- * This file defines a global [Models] store and delegates to inject event streams/sinks. Note that all streams here
- * are global.
- *
- * This allows decoupling of UI logic from stream initialisation and provides us with a central place to inspect data
- * flows. It also allows detecting of looping logic by constructing a stream dependency graph TODO do this.
+ * Global models store to allow decoupling of UI logic from stream initialisation and provide a central place to
+ * inspect data flows. It also allows detecting of looping logic by constructing a stream dependency graph TODO do this.
*
* Usage:
* // Inject service -> client event stream
- * private val serviceToClient: EventStream by eventStream(WalletMonitorModel::serviceToClient)
+ * private val serviceToClient: EventStream by Models.eventStream(WalletMonitorModel::serviceToClient)
*
- * Each Screen code should have a code layout like this:
+ * Each `Screen` code should have a code layout like this:
*
* class Screen {
* val root = (..)
@@ -41,12 +37,13 @@ import kotlin.reflect.KProperty
* }
*
* For example if I wanted to display a list of all USD cash states:
+ *
* class USDCashStatesScreen {
* val root: Pane by fxml()
*
* val usdCashStatesListView: ListView by fxid("USDCashStatesListView")
*
- * val cashStates: ObservableList by observableList(ContractStateModel::cashStates)
+ * val cashStates: ObservableList by Models.observableList(ContractStateModel::cashStates)
*
* val usdCashStates = cashStates.filter { it.(..).currency == USD }
*
@@ -66,37 +63,6 @@ import kotlin.reflect.KProperty
* Another advantage of this separation is that once we start adding a lot of screens we can still track data dependencies
* in a central place as opposed to ad-hoc wiring up the observables.
*/
-
-inline fun observable(noinline observableProperty: (M) -> Observable) =
- TrackedDelegate.ObservableDelegate(M::class, observableProperty)
-
-inline fun observer(noinline observerProperty: (M) -> Observer) =
- TrackedDelegate.ObserverDelegate(M::class, observerProperty)
-
-inline fun subject(noinline subjectProperty: (M) -> Subject) =
- TrackedDelegate.SubjectDelegate(M::class, subjectProperty)
-
-inline fun eventStream(noinline streamProperty: (M) -> EventStream) =
- TrackedDelegate.EventStreamDelegate(M::class, streamProperty)
-
-inline fun eventSink(noinline sinkProperty: (M) -> EventSink) =
- TrackedDelegate.EventSinkDelegate(M::class, sinkProperty)
-
-inline fun observableValue(noinline observableValueProperty: (M) -> ObservableValue) =
- TrackedDelegate.ObservableValueDelegate(M::class, observableValueProperty)
-
-inline fun writableValue(noinline writableValueProperty: (M) -> WritableValue) =
- TrackedDelegate.WritableValueDelegate(M::class, writableValueProperty)
-
-inline fun objectProperty(noinline objectProperty: (M) -> ObjectProperty) =
- TrackedDelegate.ObjectPropertyDelegate(M::class, objectProperty)
-
-inline fun observableList(noinline observableListProperty: (M) -> ObservableList) =
- TrackedDelegate.ObservableListDelegate(M::class, observableListProperty)
-
-inline fun observableListReadOnly(noinline observableListProperty: (M) -> ObservableList) =
- TrackedDelegate.ObservableListReadOnlyDelegate(M::class, observableListProperty)
-
object Models {
private val modelStore = HashMap, Any>()
@@ -120,68 +86,3 @@ object Models {
inline fun get(origin: KClass<*>): M = get(M::class, origin)
}
-sealed class TrackedDelegate(val klass: KClass) {
- init {
- Models.initModel(klass)
- }
-
- class ObservableDelegate(klass: KClass, val observableProperty: (M) -> Observable) : TrackedDelegate(klass) {
- operator fun getValue(thisRef: Any, property: KProperty<*>): Observable {
- return observableProperty(Models.get(klass, thisRef.javaClass.kotlin))
- }
- }
-
- class ObserverDelegate(klass: KClass, val observerProperty: (M) -> Observer) : TrackedDelegate(klass) {
- operator fun getValue(thisRef: Any, property: KProperty<*>): Observer {
- return observerProperty(Models.get(klass, thisRef.javaClass.kotlin))
- }
- }
-
- class SubjectDelegate(klass: KClass, val subjectProperty: (M) -> Subject) : TrackedDelegate(klass) {
- operator fun getValue(thisRef: Any, property: KProperty<*>): Subject {
- return subjectProperty(Models.get(klass, thisRef.javaClass.kotlin))
- }
- }
-
- class EventStreamDelegate(klass: KClass, val eventStreamProperty: (M) -> org.reactfx.EventStream) : TrackedDelegate(klass) {
- operator fun getValue(thisRef: Any, property: KProperty<*>): org.reactfx.EventStream {
- return eventStreamProperty(Models.get(klass, thisRef.javaClass.kotlin))
- }
- }
-
- class EventSinkDelegate(klass: KClass, val eventSinkProperty: (M) -> org.reactfx.EventSink) : TrackedDelegate(klass) {
- operator fun getValue(thisRef: Any, property: KProperty<*>): org.reactfx.EventSink {
- return eventSinkProperty(Models.get(klass, thisRef.javaClass.kotlin))
- }
- }
-
- class ObservableValueDelegate(klass: KClass, val observableValueProperty: (M) -> ObservableValue) : TrackedDelegate(klass) {
- operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableValue {
- return observableValueProperty(Models.get(klass, thisRef.javaClass.kotlin))
- }
- }
-
- class WritableValueDelegate(klass: KClass, val writableValueProperty: (M) -> WritableValue) : TrackedDelegate(klass) {
- operator fun getValue(thisRef: Any, property: KProperty<*>): WritableValue {
- return writableValueProperty(Models.get(klass, thisRef.javaClass.kotlin))
- }
- }
-
- class ObservableListDelegate(klass: KClass, val observableListProperty: (M) -> ObservableList) : TrackedDelegate(klass) {
- operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableList {
- return observableListProperty(Models.get(klass, thisRef.javaClass.kotlin))
- }
- }
-
- class ObservableListReadOnlyDelegate(klass: KClass, val observableListReadOnlyProperty: (M) -> ObservableList) : TrackedDelegate(klass) {
- operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableList {
- return observableListReadOnlyProperty(Models.get(klass, thisRef.javaClass.kotlin))
- }
- }
-
- class ObjectPropertyDelegate(klass: KClass, val objectPropertyProperty: (M) -> ObjectProperty) : TrackedDelegate(klass) {
- operator fun getValue(thisRef: Any, property: KProperty<*>): ObjectProperty {
- return objectPropertyProperty(Models.get(klass, thisRef.javaClass.kotlin))
- }
- }
-}
diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ModelsUtils.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ModelsUtils.kt
new file mode 100644
index 0000000000..836e046887
--- /dev/null
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ModelsUtils.kt
@@ -0,0 +1,43 @@
+@file:JvmName("ModelsUtils")
+package net.corda.client.jfx.model
+
+import javafx.beans.property.ObjectProperty
+import javafx.beans.value.ObservableValue
+import javafx.beans.value.WritableValue
+import javafx.collections.ObservableList
+import org.reactfx.EventSink
+import org.reactfx.EventStream
+import rx.Observable
+import rx.Observer
+import rx.subjects.Subject
+import kotlin.reflect.KClass
+
+inline fun observable(noinline observableProperty: (M) -> Observable) =
+ TrackedDelegate.ObservableDelegate(M::class, observableProperty)
+
+inline fun observer(noinline observerProperty: (M) -> Observer) =
+ TrackedDelegate.ObserverDelegate(M::class, observerProperty)
+
+inline fun subject(noinline subjectProperty: (M) -> Subject) =
+ TrackedDelegate.SubjectDelegate(M::class, subjectProperty)
+
+inline fun eventStream(noinline streamProperty: (M) -> EventStream) =
+ TrackedDelegate.EventStreamDelegate(M::class, streamProperty)
+
+inline fun eventSink(noinline sinkProperty: (M) -> EventSink) =
+ TrackedDelegate.EventSinkDelegate(M::class, sinkProperty)
+
+inline fun observableValue(noinline observableValueProperty: (M) -> ObservableValue) =
+ TrackedDelegate.ObservableValueDelegate(M::class, observableValueProperty)
+
+inline fun writableValue(noinline writableValueProperty: (M) -> WritableValue) =
+ TrackedDelegate.WritableValueDelegate(M::class, writableValueProperty)
+
+inline fun objectProperty(noinline objectProperty: (M) -> ObjectProperty) =
+ TrackedDelegate.ObjectPropertyDelegate(M::class, objectProperty)
+
+inline fun observableList(noinline observableListProperty: (M) -> ObservableList) =
+ TrackedDelegate.ObservableListDelegate(M::class, observableListProperty)
+
+inline fun observableListReadOnly(noinline observableListProperty: (M) -> ObservableList) =
+ TrackedDelegate.ObservableListReadOnlyDelegate(M::class, observableListProperty)
\ No newline at end of file
diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt
index 923465b2b9..15cf636622 100644
--- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt
@@ -5,18 +5,20 @@ import com.google.common.cache.CacheLoader
import javafx.beans.value.ObservableValue
import javafx.collections.FXCollections
import javafx.collections.ObservableList
+import net.corda.client.jfx.utils.filterNotNull
import net.corda.client.jfx.utils.fold
import net.corda.client.jfx.utils.map
-import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
+import net.corda.core.identity.Party
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache.MapChange
+import net.corda.nodeapi.internal.ServiceType
import java.security.PublicKey
class NetworkIdentityModel {
private val networkIdentityObservable by observable(NodeMonitorModel::networkMap)
- val networkIdentities: ObservableList =
+ private val networkIdentities: ObservableList =
networkIdentityObservable.fold(FXCollections.observableArrayList()) { list, update ->
list.removeIf {
when (update) {
@@ -31,19 +33,17 @@ class NetworkIdentityModel {
private val rpcProxy by observableValue(NodeMonitorModel::proxyObservable)
private val identityCache = CacheBuilder.newBuilder()
- .build>(CacheLoader.from {
- publicKey ->
- publicKey?.let { rpcProxy.map { it?.nodeIdentityFromParty(AnonymousParty(publicKey)) } }
+ .build>(CacheLoader.from { publicKey ->
+ publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } }
})
- val parties: ObservableList = networkIdentities.filtered { !it.isCordaService() }
- val notaries: ObservableList = networkIdentities.filtered { it.advertisedServices.any { it.info.type.isNotary() } }
- val myIdentity = rpcProxy.map { it?.nodeIdentity() }
+ val notaries: ObservableList = networkIdentities.map {
+ it.legalIdentitiesAndCerts.find { it.name.commonName?.let { ServiceType.parse(it).isNotary() } ?: false }
+ }.map { it?.party }.filterNotNull()
- private fun NodeInfo.isCordaService(): Boolean {
- // TODO: better way to identify Corda service?
- return advertisedServices.any { it.info.type.isNetworkMap() || it.info.type.isNotary() }
- }
+ val notaryNodes: ObservableList = notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }.filterNotNull()
+ val parties: ObservableList = networkIdentities.filtered { it.legalIdentities.all { it !in notaries } }
+ val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party }
fun partyFromPublicKey(publicKey: PublicKey): ObservableValue = identityCache[publicKey]
}
diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt
index 7bd9623f52..3cde49536e 100644
--- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt
@@ -5,13 +5,17 @@ import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.core.contracts.ContractState
import net.corda.core.flows.StateMachineRunId
+import net.corda.core.identity.Party
import net.corda.core.messaging.*
import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.core.node.services.Vault
-import net.corda.core.node.services.vault.*
-import net.corda.core.utilities.seconds
+import net.corda.core.node.services.vault.DEFAULT_PAGE_NUM
+import net.corda.core.node.services.vault.MAX_PAGE_SIZE
+import net.corda.core.node.services.vault.PageSpecification
+import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort
+import net.corda.core.utilities.seconds
import rx.Observable
import rx.subjects.PublishSubject
@@ -45,6 +49,7 @@ class NodeMonitorModel {
val networkMap: Observable = networkMapSubject
val proxyObservable = SimpleObjectProperty()
+ lateinit var notaryIdentities: List
/**
* Register for updates to/from a given vault.
@@ -60,6 +65,7 @@ class NodeMonitorModel {
)
val connection = client.start(username, password)
val proxy = connection.proxy
+ notaryIdentities = proxy.notaryIdentities()
val (stateMachines, stateMachineUpdates) = proxy.stateMachinesFeed()
// Extract the flow tracking stream
@@ -83,8 +89,13 @@ class NodeMonitorModel {
stateMachineUpdates.startWith(currentStateMachines).subscribe(stateMachineUpdatesSubject)
// Vault snapshot (force single page load with MAX_PAGE_SIZE) + updates
- val (vaultSnapshot, vaultUpdates) = proxy.vaultTrackBy(QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL),
- PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE))
+ val (_, vaultUpdates) = proxy.vaultTrackBy(QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL),
+ PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE))
+
+ val vaultSnapshot = proxy.vaultQueryBy(QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED),
+ PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE))
+ // We have to fetch the snapshot separately since vault query API doesn't allow different criteria for snapshot and updates.
+ // TODO : This will create a small window of opportunity for inconsistent updates, might need to change the vault API to handle this case.
val initialVaultUpdate = Vault.Update(setOf(), vaultSnapshot.states.toSet())
vaultUpdates.startWith(initialVaultUpdate).subscribe(vaultUpdatesSubject)
diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TrackedDelegate.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TrackedDelegate.kt
new file mode 100644
index 0000000000..7ee819f2bb
--- /dev/null
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TrackedDelegate.kt
@@ -0,0 +1,79 @@
+package net.corda.client.jfx.model
+
+import javafx.beans.property.ObjectProperty
+import javafx.beans.value.ObservableValue
+import javafx.beans.value.WritableValue
+import javafx.collections.ObservableList
+import org.reactfx.EventSink
+import org.reactfx.EventStream
+import rx.Observable
+import rx.Observer
+import rx.subjects.Subject
+import kotlin.reflect.KClass
+import kotlin.reflect.KProperty
+
+sealed class TrackedDelegate(val klass: KClass) {
+ init {
+ Models.initModel(klass)
+ }
+
+ class ObservableDelegate(klass: KClass, val observableProperty: (M) -> Observable) : TrackedDelegate(klass) {
+ operator fun getValue(thisRef: Any, property: KProperty<*>): Observable {
+ return observableProperty(Models.get(klass, thisRef.javaClass.kotlin))
+ }
+ }
+
+ class ObserverDelegate(klass: KClass, val observerProperty: (M) -> Observer) : TrackedDelegate(klass) {
+ operator fun getValue(thisRef: Any, property: KProperty<*>): Observer {
+ return observerProperty(Models.get(klass, thisRef.javaClass.kotlin))
+ }
+ }
+
+ class SubjectDelegate(klass: KClass, val subjectProperty: (M) -> Subject) : TrackedDelegate(klass) {
+ operator fun getValue(thisRef: Any, property: KProperty<*>): Subject {
+ return subjectProperty(Models.get(klass, thisRef.javaClass.kotlin))
+ }
+ }
+
+ class EventStreamDelegate(klass: KClass, val eventStreamProperty: (M) -> EventStream) : TrackedDelegate(klass) {
+ operator fun getValue(thisRef: Any, property: KProperty<*>): EventStream {
+ return eventStreamProperty(Models.get(klass, thisRef.javaClass.kotlin))
+ }
+ }
+
+ class EventSinkDelegate(klass: KClass, val eventSinkProperty: (M) -> EventSink) : TrackedDelegate(klass) {
+ operator fun getValue(thisRef: Any, property: KProperty<*>): EventSink {
+ return eventSinkProperty(Models.get(klass, thisRef.javaClass.kotlin))
+ }
+ }
+
+ class ObservableValueDelegate(klass: KClass, val observableValueProperty: (M) -> ObservableValue) : TrackedDelegate(klass) {
+ operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableValue {
+ return observableValueProperty(Models.get(klass, thisRef.javaClass.kotlin))
+ }
+ }
+
+ class WritableValueDelegate(klass: KClass, val writableValueProperty: (M) -> WritableValue) : TrackedDelegate(klass) {
+ operator fun getValue(thisRef: Any, property: KProperty<*>): WritableValue {
+ return writableValueProperty(Models.get(klass, thisRef.javaClass.kotlin))
+ }
+ }
+
+ class ObservableListDelegate(klass: KClass, val observableListProperty: (M) -> ObservableList) : TrackedDelegate(klass) {
+ operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableList {
+ return observableListProperty(Models.get(klass, thisRef.javaClass.kotlin))
+ }
+ }
+
+ class ObservableListReadOnlyDelegate(klass: KClass, val observableListReadOnlyProperty: (M) -> ObservableList) : TrackedDelegate(klass) {
+ operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableList {
+ return observableListReadOnlyProperty(Models.get(klass, thisRef.javaClass.kotlin))
+ }
+ }
+
+ class ObjectPropertyDelegate(klass: KClass, val objectPropertyProperty: (M) -> ObjectProperty) : TrackedDelegate(klass) {
+ operator fun getValue(thisRef: Any, property: KProperty<*>): ObjectProperty {
+ return objectPropertyProperty(Models.get(klass, thisRef.javaClass.kotlin))
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TransactionDataModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TransactionDataModel.kt
index 899e5001da..e82e529262 100644
--- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TransactionDataModel.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TransactionDataModel.kt
@@ -54,7 +54,7 @@ data class PartiallyResolvedTransaction(
class TransactionDataModel {
private val transactions by observable(NodeMonitorModel::transactions)
private val collectedTransactions = transactions.recordInSequence()
- private val transactionMap = collectedTransactions.associateBy(SignedTransaction::id)
+ private val transactionMap = transactions.recordAsAssociation(SignedTransaction::id)
val partiallyResolvedTransactions = collectedTransactions.map {
PartiallyResolvedTransaction.fromSignedTransaction(it, transactionMap)
diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableUtilities.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableUtilities.kt
index 83cb40bf70..10ae4aeff6 100644
--- a/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableUtilities.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableUtilities.kt
@@ -353,4 +353,4 @@ fun DataFeed, Vault.Update>.toFXListOfState
*/
fun DataFeed, Vault.Update>.toFXListOfStates(): ObservableList {
return toFXListOfStateRefs().map { it.state.data }
-}
\ No newline at end of file
+}
diff --git a/client/mock/build.gradle b/client/mock/build.gradle
index 0c4a2efbb0..7432fdd312 100644
--- a/client/mock/build.gradle
+++ b/client/mock/build.gradle
@@ -5,12 +5,6 @@ apply plugin: 'com.jfrog.artifactory'
description 'Corda client mock modules'
-//noinspection GroovyAssignabilityCheck
-configurations {
- // we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment
- runtime.exclude module: 'isolated'
-}
-
// To find potential version conflicts, run "gradle htmlDependencyReport" and then look in
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
diff --git a/client/mock/src/main/kotlin/net/corda/client/mock/Generator.kt b/client/mock/src/main/kotlin/net/corda/client/mock/Generator.kt
index f16f07445b..3993383a1a 100644
--- a/client/mock/src/main/kotlin/net/corda/client/mock/Generator.kt
+++ b/client/mock/src/main/kotlin/net/corda/client/mock/Generator.kt
@@ -59,7 +59,7 @@ class Generator(val generate: (SplittableRandom) -> Try) {
fun generateOrFail(random: SplittableRandom, numberOfTries: Int = 1): A {
var error: Throwable? = null
- for (i in 0..numberOfTries - 1) {
+ for (i in 0 until numberOfTries) {
val result = generate(random)
error = when (result) {
is Try.Success -> return result.value
@@ -115,13 +115,14 @@ class Generator(val generate: (SplittableRandom) -> Try) {
fun frequency(vararg generators: Pair>) = frequency(generators.toList())
- fun sequence(generators: List>) = Generator> {
+ fun sequence(generators: List>) = Generator {
val result = mutableListOf()
for (generator in generators) {
val element = generator.generate(it)
+ @Suppress("UNCHECKED_CAST")
when (element) {
is Try.Success -> result.add(element.value)
- is Try.Failure -> return@Generator element
+ is Try.Failure -> return@Generator element as Try>
}
}
Try.Success(result)
@@ -135,12 +136,12 @@ class Generator(val generate: (SplittableRandom) -> Try) {
fun intRange(range: IntRange) = intRange(range.first, range.last)
fun intRange(from: Int, to: Int): Generator = Generator.success {
- (from + Math.abs(it.nextInt()) % (to - from + 1)).toInt()
+ (from + Math.abs(it.nextInt()) % (to - from + 1))
}
fun longRange(range: LongRange) = longRange(range.first, range.last)
fun longRange(from: Long, to: Long): Generator = Generator.success {
- (from + Math.abs(it.nextLong()) % (to - from + 1)).toLong()
+ (from + Math.abs(it.nextLong()) % (to - from + 1))
}
fun double() = Generator.success { it.nextDouble() }
@@ -153,7 +154,7 @@ class Generator(val generate: (SplittableRandom) -> Try) {
if (Character.isValidCodePoint(codePoint)) {
return@Generator Try.Success(codePoint.toChar())
} else {
- Try.Failure(IllegalStateException("Could not generate valid codepoint"))
+ Try.Failure(IllegalStateException("Could not generate valid codepoint"))
}
}
@@ -174,7 +175,7 @@ class Generator(val generate: (SplittableRandom) -> Try) {
}
- fun replicatePoisson(meanSize: Double, generator: Generator, atLeastOne: Boolean = false) = Generator> {
+ fun replicatePoisson(meanSize: Double, generator: Generator, atLeastOne: Boolean = false) = Generator {
val chance = (meanSize - 1) / meanSize
val result = mutableListOf()
var finish = false
@@ -190,7 +191,8 @@ class Generator(val generate: (SplittableRandom) -> Try) {
}
}
if (res is Try.Failure) {
- return@Generator res
+ @Suppress("UNCHECKED_CAST")
+ return@Generator res as Try>
}
}
Try.Success(result)
@@ -200,11 +202,11 @@ class Generator(val generate: (SplittableRandom) -> Try) {
fun pickN(number: Int, list: List) = Generator> {
val mask = BitSet(list.size)
val size = Math.min(list.size, number)
- for (i in 0..size - 1) {
+ for (i in 0 until size) {
// mask[i] = 1 desugars into mask.set(i, 1), which sets a range instead of a bit
mask[i] = true
}
- for (i in 0..list.size - 1) {
+ for (i in 0 until list.size) {
val bit = mask[i]
val swapIndex = i + it.nextInt(size - i)
mask[i] = mask[swapIndex]
diff --git a/client/rpc/build.gradle b/client/rpc/build.gradle
index 2b3c020f49..2f36d1c315 100644
--- a/client/rpc/build.gradle
+++ b/client/rpc/build.gradle
@@ -7,9 +7,6 @@ description 'Corda client RPC modules'
//noinspection GroovyAssignabilityCheck
configurations {
- // we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment
- runtime.exclude module: 'isolated'
-
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
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 5fd5ec83b3..90e4b8faca 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
@@ -5,14 +5,17 @@ import net.corda.core.concurrent.CordaFuture;
import net.corda.core.contracts.Amount;
import net.corda.core.messaging.CordaRPCOps;
import net.corda.core.messaging.FlowHandle;
-import net.corda.core.node.services.ServiceInfo;
import net.corda.core.utilities.OpaqueBytes;
import net.corda.finance.flows.AbstractCashFlow;
import net.corda.finance.flows.CashIssueFlow;
import net.corda.finance.flows.CashPaymentFlow;
+import net.corda.finance.schemas.*;
import net.corda.node.internal.Node;
+import net.corda.node.internal.StartedNode;
import net.corda.node.services.transactions.ValidatingNotaryService;
+import net.corda.nodeapi.internal.ServiceInfo;
import net.corda.nodeapi.User;
+import net.corda.testing.CoreTestUtils;
import net.corda.testing.node.NodeBasedTest;
import org.junit.After;
import org.junit.Before;
@@ -22,14 +25,14 @@ 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.Collections.*;
import static java.util.Objects.requireNonNull;
import static kotlin.test.AssertionsKt.assertEquals;
import static net.corda.client.rpc.CordaRPCClientConfiguration.getDefault;
-import static net.corda.finance.CurrencyUtils.DOLLARS;
+import static net.corda.finance.Currencies.DOLLARS;
import static net.corda.finance.contracts.GetBalances.getCashBalance;
import static net.corda.node.services.FlowPermissions.startFlowPermission;
+import static net.corda.testing.CoreTestUtils.*;
import static net.corda.testing.TestConstants.getALICE;
public class CordaRPCJavaClientTest extends NodeBasedTest {
@@ -37,7 +40,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
private Set permSet = new HashSet<>(perms);
private User rpcUser = new User("user1", "test", permSet);
- private Node node;
+ private StartedNode node;
private CordaRPCClient client;
private RPCClient.RPCConnection connection = null;
private CordaRPCOps rpcProxy;
@@ -49,15 +52,18 @@ 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 = startNode(getALICE().getName(), 1, services, singletonList(rpcUser), emptyMap());
node = nodeFuture.get();
- client = new CordaRPCClient(requireNonNull(node.getConfiguration().getRpcAddress()), null, getDefault(), false);
+ node.getInternals().registerCustomSchemas(Collections.singleton(CashSchemaV1.INSTANCE));
+ client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress()), getDefault(), false);
}
@After
public void done() throws IOException {
connection.close();
+ unsetCordappPackages();
}
@Test
@@ -71,7 +77,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
FlowHandle flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
DOLLARS(123), OpaqueBytes.of("1".getBytes()),
- node.info.getLegalIdentity());
+ CoreTestUtils.chooseIdentity(node.getInfo()));
System.out.println("Started issuing cash, waiting on result");
flowHandle.getReturnValue().get();
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 6c8a7d7a8c..b4c10fa808 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
@@ -2,8 +2,10 @@ package net.corda.client.rpc
import net.corda.core.crypto.random63BitValue
import net.corda.core.flows.FlowInitiator
-import net.corda.core.messaging.*
-import net.corda.core.node.services.ServiceInfo
+import net.corda.core.messaging.FlowProgressHandle
+import net.corda.core.messaging.StateMachineUpdate
+import net.corda.core.messaging.startFlow
+import net.corda.core.messaging.startTrackedFlow
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS
@@ -13,12 +15,18 @@ import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashException
import net.corda.finance.flows.CashIssueFlow
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.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.ALICE
+import net.corda.testing.chooseIdentity
import net.corda.testing.node.NodeBasedTest
+import net.corda.testing.setCordappPackages
+import net.corda.testing.unsetCordappPackages
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.After
@@ -33,7 +41,7 @@ class CordaRPCClientTest : NodeBasedTest() {
startFlowPermission(),
startFlowPermission()
))
- private lateinit var node: Node
+ private lateinit var node: StartedNode
private lateinit var client: CordaRPCClient
private var connection: CordaRPCConnection? = null
@@ -43,13 +51,16 @@ class CordaRPCClientTest : NodeBasedTest() {
@Before
fun setUp() {
+ setCordappPackages("net.corda.finance.contracts")
node = startNode(ALICE.name, rpcUsers = listOf(rpcUser), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))).getOrThrow()
- client = CordaRPCClient(node.configuration.rpcAddress!!, initialiseSerialization = false)
+ node.internals.registerCustomSchemas(setOf(CashSchemaV1))
+ client = CordaRPCClient(node.internals.configuration.rpcAddress!!, initialiseSerialization = false)
}
@After
fun done() {
connection?.close()
+ unsetCordappPackages()
}
@Test
@@ -78,7 +89,7 @@ class CordaRPCClientTest : NodeBasedTest() {
println("Creating proxy")
println("Starting flow")
val flowHandle = connection!!.proxy.startTrackedFlow(::CashIssueFlow,
- 20.DOLLARS, OpaqueBytes.of(0), node.info.legalIdentity
+ 20.DOLLARS, OpaqueBytes.of(0), node.info.chooseIdentity()
)
println("Started flow, waiting on result")
flowHandle.progress.subscribe {
@@ -90,7 +101,7 @@ class CordaRPCClientTest : NodeBasedTest() {
@Test
fun `sub-type of FlowException thrown by flow`() {
login(rpcUser.username, rpcUser.password)
- val handle = connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.legalIdentity)
+ val handle = connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.chooseIdentity())
assertThatExceptionOfType(CashException::class.java).isThrownBy {
handle.returnValue.getOrThrow()
}
@@ -99,9 +110,8 @@ class CordaRPCClientTest : NodeBasedTest() {
@Test
fun `check basic flow has no progress`() {
login(rpcUser.username, rpcUser.password)
- connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.legalIdentity).use {
+ connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.chooseIdentity()).use {
assertFalse(it is FlowProgressHandle<*>)
- assertTrue(it is FlowHandle<*>)
}
}
@@ -113,7 +123,7 @@ class CordaRPCClientTest : NodeBasedTest() {
assertTrue(startCash.isEmpty(), "Should not start with any cash")
val flowHandle = proxy.startFlow(::CashIssueFlow,
- 123.DOLLARS, OpaqueBytes.of(0), node.info.legalIdentity
+ 123.DOLLARS, OpaqueBytes.of(0), node.info.chooseIdentity()
)
println("Started issuing cash, waiting on result")
flowHandle.returnValue.get()
@@ -138,7 +148,7 @@ class CordaRPCClientTest : NodeBasedTest() {
countShellFlows++
}
}
- val nodeIdentity = node.info.legalIdentity
+ val nodeIdentity = node.info.chooseIdentity()
node.services.startFlow(CashIssueFlow(2000.DOLLARS, OpaqueBytes.of(0), nodeIdentity), FlowInitiator.Shell).resultFuture.getOrThrow()
proxy.startFlow(::CashIssueFlow,
123.DOLLARS,
diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt
index a0a4a7ea1f..e5ac356d76 100644
--- a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt
+++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt
@@ -1,18 +1,13 @@
package net.corda.client.rpc
+import net.corda.client.rpc.internal.KryoClientSerializationScheme
import net.corda.client.rpc.internal.RPCClient
import net.corda.client.rpc.internal.RPCClientConfiguration
-import net.corda.client.rpc.serialization.KryoClientSerializationScheme
import net.corda.core.messaging.CordaRPCOps
-import net.corda.core.serialization.SerializationDefaults
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
import net.corda.nodeapi.ConnectionDirection
-import net.corda.nodeapi.config.SSLConfiguration
-import net.corda.nodeapi.internal.serialization.AMQPClientSerializationScheme
-import net.corda.nodeapi.internal.serialization.KRYO_P2P_CONTEXT
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
-import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
import java.time.Duration
/** @see RPCClient.RPCConnection */
@@ -38,9 +33,9 @@ data class CordaRPCClientConfiguration(
}
/** @see RPCClient */
+//TODO Add SSL support
class CordaRPCClient(
hostAndPort: NetworkHostAndPort,
- sslConfiguration: SSLConfiguration? = null,
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default,
initialiseSerialization: Boolean = true
) {
@@ -49,12 +44,12 @@ class CordaRPCClient(
// others having registered first.
// TODO: allow clients to have serialization factory etc injected and align with RPC protocol version?
if (initialiseSerialization) {
- initialiseSerialization()
+ KryoClientSerializationScheme.initialiseSerialization()
}
}
private val rpcClient = RPCClient(
- tcpTransport(ConnectionDirection.Outbound(), hostAndPort, sslConfiguration),
+ tcpTransport(ConnectionDirection.Outbound(), hostAndPort, config = null),
configuration.toRpcClientConfiguration(),
KRYO_RPC_CLIENT_CONTEXT
)
@@ -66,21 +61,4 @@ class CordaRPCClient(
inline fun use(username: String, password: String, block: (CordaRPCConnection) -> A): A {
return start(username, password).use(block)
}
-
- companion object {
- fun initialiseSerialization() {
- try {
- SerializationDefaults.SERIALIZATION_FACTORY = SerializationFactoryImpl().apply {
- registerScheme(KryoClientSerializationScheme())
- registerScheme(AMQPClientSerializationScheme())
- }
- SerializationDefaults.P2P_CONTEXT = KRYO_P2P_CONTEXT
- SerializationDefaults.RPC_CLIENT_CONTEXT = KRYO_RPC_CLIENT_CONTEXT
- } catch(e: IllegalStateException) {
- // Check that it's registered as we expect
- check(SerializationDefaults.SERIALIZATION_FACTORY is SerializationFactoryImpl) { "RPC client encountered conflicting configuration of serialization subsystem." }
- check((SerializationDefaults.SERIALIZATION_FACTORY as SerializationFactoryImpl).alreadyRegisteredSchemes.any { it is KryoClientSerializationScheme }) { "RPC client encountered conflicting configuration of serialization subsystem." }
- }
- }
- }
}
\ No newline at end of file
diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/PermissionException.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/PermissionException.kt
new file mode 100644
index 0000000000..0498801989
--- /dev/null
+++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/PermissionException.kt
@@ -0,0 +1,10 @@
+package net.corda.client.rpc
+
+import net.corda.core.serialization.CordaSerializable
+
+/**
+ * Thrown to indicate that the calling user does not have permission for something they have requested (for example
+ * calling a method).
+ */
+@CordaSerializable
+class PermissionException(msg: String) : RuntimeException(msg)
diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCException.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCException.kt
new file mode 100644
index 0000000000..32ea9928be
--- /dev/null
+++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCException.kt
@@ -0,0 +1,11 @@
+package net.corda.client.rpc
+
+import net.corda.core.CordaRuntimeException
+
+/**
+ * Thrown to indicate a fatal error in the RPC system itself, as opposed to an error generated by the invoked
+ * method.
+ */
+open class RPCException(message: String?, cause: Throwable?) : CordaRuntimeException(message, cause) {
+ constructor(msg: String) : this(msg, null)
+}
diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCSinceVersion.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCSinceVersion.kt
new file mode 100644
index 0000000000..262d009427
--- /dev/null
+++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCSinceVersion.kt
@@ -0,0 +1,6 @@
+package net.corda.client.rpc
+
+/** Records the protocol version in which this RPC was added. */
+@Target(AnnotationTarget.FUNCTION)
+@MustBeDocumented
+annotation class RPCSinceVersion(val version: Int)
diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/KryoClientSerializationScheme.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/KryoClientSerializationScheme.kt
new file mode 100644
index 0000000000..66ddb33b1d
--- /dev/null
+++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/KryoClientSerializationScheme.kt
@@ -0,0 +1,41 @@
+package net.corda.client.rpc.internal
+
+import com.esotericsoftware.kryo.pool.KryoPool
+import net.corda.core.serialization.SerializationContext
+import net.corda.core.serialization.SerializationDefaults
+import net.corda.core.utilities.ByteSequence
+import net.corda.nodeapi.internal.serialization.*
+
+class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
+ override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean {
+ return byteSequence == KryoHeaderV0_1 && (target == SerializationContext.UseCase.RPCClient || target == SerializationContext.UseCase.P2P)
+ }
+
+ override fun rpcClientKryoPool(context: SerializationContext): KryoPool {
+ return KryoPool.Builder {
+ DefaultKryoCustomizer.customize(RPCKryo(RpcClientObservableSerializer, context)).apply {
+ classLoader = context.deserializationClassLoader
+ }
+ }.build()
+ }
+
+ // We're on the client and don't have access to server classes.
+ override fun rpcServerKryoPool(context: SerializationContext): KryoPool = throw UnsupportedOperationException()
+
+ companion object {
+ fun initialiseSerialization() {
+ try {
+ SerializationDefaults.SERIALIZATION_FACTORY = SerializationFactoryImpl().apply {
+ registerScheme(KryoClientSerializationScheme())
+ registerScheme(AMQPClientSerializationScheme())
+ }
+ SerializationDefaults.P2P_CONTEXT = KRYO_P2P_CONTEXT
+ SerializationDefaults.RPC_CLIENT_CONTEXT = KRYO_RPC_CLIENT_CONTEXT
+ } catch(e: IllegalStateException) {
+ // Check that it's registered as we expect
+ check(SerializationDefaults.SERIALIZATION_FACTORY is SerializationFactoryImpl) { "RPC client encountered conflicting configuration of serialization subsystem." }
+ check((SerializationDefaults.SERIALIZATION_FACTORY as SerializationFactoryImpl).alreadyRegisteredSchemes.any { it is KryoClientSerializationScheme }) { "RPC client encountered conflicting configuration of serialization subsystem." }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt
index cca836d858..f0fc941668 100644
--- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt
+++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt
@@ -1,5 +1,6 @@
package net.corda.client.rpc.internal
+import net.corda.client.rpc.RPCException
import net.corda.core.crypto.random63BitValue
import net.corda.core.internal.logElapsedTime
import net.corda.core.messaging.RPCOps
@@ -12,7 +13,6 @@ import net.corda.core.utilities.seconds
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.RPCApi
-import net.corda.nodeapi.RPCException
import net.corda.nodeapi.config.SSLConfiguration
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.TransportConfiguration
diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt
index e0a0c21ca0..10e901eb70 100644
--- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt
+++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt
@@ -10,6 +10,8 @@ import com.google.common.cache.RemovalCause
import com.google.common.cache.RemovalListener
import com.google.common.util.concurrent.SettableFuture
import com.google.common.util.concurrent.ThreadFactoryBuilder
+import net.corda.client.rpc.RPCException
+import net.corda.client.rpc.RPCSinceVersion
import net.corda.core.crypto.random63BitValue
import net.corda.core.internal.LazyPool
import net.corda.core.internal.LazyStickyPool
diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/serialization/SerializationScheme.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/serialization/SerializationScheme.kt
deleted file mode 100644
index 817d741c64..0000000000
--- a/client/rpc/src/main/kotlin/net/corda/client/rpc/serialization/SerializationScheme.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package net.corda.client.rpc.serialization
-
-import com.esotericsoftware.kryo.pool.KryoPool
-import net.corda.client.rpc.internal.RpcClientObservableSerializer
-import net.corda.core.serialization.SerializationContext
-import net.corda.core.utilities.ByteSequence
-import net.corda.nodeapi.RPCKryo
-import net.corda.nodeapi.internal.serialization.AbstractKryoSerializationScheme
-import net.corda.nodeapi.internal.serialization.DefaultKryoCustomizer
-import net.corda.nodeapi.internal.serialization.KryoHeaderV0_1
-
-class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
- override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean {
- return byteSequence == KryoHeaderV0_1 && (target == SerializationContext.UseCase.RPCClient || target == SerializationContext.UseCase.P2P)
- }
-
- override fun rpcClientKryoPool(context: SerializationContext): KryoPool {
- return KryoPool.Builder {
- DefaultKryoCustomizer.customize(RPCKryo(RpcClientObservableSerializer, context)).apply { classLoader = context.deserializationClassLoader }
- }.build()
- }
-
- // We're on the client and don't have access to server classes.
- override fun rpcServerKryoPool(context: SerializationContext): KryoPool {
- throw UnsupportedOperationException()
- }
-}
\ No newline at end of file
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 5a8a8349eb..bc522e42f0 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
@@ -2,11 +2,12 @@ package net.corda.java.rpc;
import net.corda.client.rpc.CordaRPCConnection;
import net.corda.core.contracts.Amount;
+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.core.utilities.X500NameUtils;
import net.corda.finance.flows.AbstractCashFlow;
import net.corda.finance.flows.CashIssueFlow;
import net.corda.nodeapi.User;
@@ -41,9 +42,10 @@ public class StandaloneCordaRPCJavaClientTest {
private CordaRPCOps rpcProxy;
private CordaRPCConnection connection;
private NodeInfo notaryNode;
+ private Party notaryNodeIdentity;
private NodeConfig notaryConfig = new NodeConfig(
- X500NameUtils.getX500Name("Notary Service", "Zurich", "CH"),
+ new CordaX500Name("Notary Service", "Zurich", "CH"),
port.getAndIncrement(),
port.getAndIncrement(),
port.getAndIncrement(),
@@ -60,6 +62,7 @@ public class StandaloneCordaRPCJavaClientTest {
connection = notary.connect();
rpcProxy = connection.getProxy();
notaryNode = fetchNotaryIdentity();
+ notaryNodeIdentity = rpcProxy.nodeInfo().getLegalIdentities().get(0);
}
@After
@@ -106,7 +109,7 @@ public class StandaloneCordaRPCJavaClientTest {
FlowHandle flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
dollars123, OpaqueBytes.of("1".getBytes()),
- notaryNode.getLegalIdentity());
+ notaryNodeIdentity);
System.out.println("Started issuing cash, waiting on result");
flowHandle.getReturnValue().get();
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 4fab64c88f..6da6aa3dc4 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
@@ -4,12 +4,17 @@ import com.google.common.hash.Hashing
import com.google.common.hash.HashingInputStream
import net.corda.client.rpc.CordaRPCConnection
import net.corda.core.crypto.SecureHash
+import net.corda.core.identity.CordaX500Name
+import net.corda.core.identity.Party
import net.corda.core.internal.*
import net.corda.core.messaging.*
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.Vault
import net.corda.core.node.services.vault.*
-import net.corda.core.utilities.*
+import net.corda.core.utilities.OpaqueBytes
+import net.corda.core.utilities.getOrThrow
+import net.corda.core.utilities.loggerFor
+import net.corda.core.utilities.seconds
import net.corda.finance.DOLLARS
import net.corda.finance.POUNDS
import net.corda.finance.SWISS_FRANCS
@@ -52,9 +57,10 @@ class StandaloneCordaRPClientTest {
private lateinit var rpcProxy: CordaRPCOps
private lateinit var connection: CordaRPCConnection
private lateinit var notaryNode: NodeInfo
+ private lateinit var notaryNodeIdentity: Party
private val notaryConfig = NodeConfig(
- legalName = getX500Name(O = "Notary Service", OU = "corda", L = "Zurich", C = "CH"),
+ legalName = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
webPort = port.andIncrement,
@@ -70,6 +76,7 @@ class StandaloneCordaRPClientTest {
connection = notary.connect()
rpcProxy = connection.proxy
notaryNode = fetchNotaryIdentity()
+ notaryNodeIdentity = rpcProxy.nodeInfo().legalIdentitiesAndCerts.first().party
}
@After
@@ -106,7 +113,7 @@ class StandaloneCordaRPClientTest {
@Test
fun `test starting flow`() {
- rpcProxy.startFlow(::CashIssueFlow, 127.POUNDS, OpaqueBytes.of(0), notaryNode.notaryIdentity)
+ rpcProxy.startFlow(::CashIssueFlow, 127.POUNDS, OpaqueBytes.of(0), notaryNodeIdentity)
.returnValue.getOrThrow(timeout)
}
@@ -114,7 +121,7 @@ class StandaloneCordaRPClientTest {
fun `test starting tracked flow`() {
var trackCount = 0
val handle = rpcProxy.startTrackedFlow(
- ::CashIssueFlow, 429.DOLLARS, OpaqueBytes.of(0), notaryNode.notaryIdentity
+ ::CashIssueFlow, 429.DOLLARS, OpaqueBytes.of(0), notaryNodeIdentity
)
val updateLatch = CountDownLatch(1)
handle.progress.subscribe { msg ->
@@ -129,7 +136,7 @@ class StandaloneCordaRPClientTest {
@Test
fun `test network map`() {
- assertEquals(notaryConfig.legalName, notaryNode.legalIdentity.name)
+ assertEquals(notaryConfig.legalName, notaryNodeIdentity.name)
}
@Test
@@ -148,7 +155,7 @@ class StandaloneCordaRPClientTest {
}
// Now issue some cash
- rpcProxy.startFlow(::CashIssueFlow, 513.SWISS_FRANCS, OpaqueBytes.of(0), notaryNode.notaryIdentity)
+ rpcProxy.startFlow(::CashIssueFlow, 513.SWISS_FRANCS, OpaqueBytes.of(0), notaryNodeIdentity)
.returnValue.getOrThrow(timeout)
updateLatch.await()
assertEquals(1, updateCount.get())
@@ -166,7 +173,7 @@ class StandaloneCordaRPClientTest {
}
// Now issue some cash
- rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNode.notaryIdentity)
+ rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNodeIdentity)
.returnValue.getOrThrow(timeout)
updateLatch.await()
@@ -180,7 +187,7 @@ class StandaloneCordaRPClientTest {
@Test
fun `test vault query by`() {
// Now issue some cash
- rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNode.notaryIdentity)
+ rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNodeIdentity)
.returnValue.getOrThrow(timeout)
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
@@ -191,7 +198,7 @@ class StandaloneCordaRPClientTest {
assertEquals(1, queryResults.totalStatesAvailable)
assertEquals(queryResults.states.first().state.data.amount.quantity, 629.POUNDS.quantity)
- rpcProxy.startFlow(::CashPaymentFlow, 100.POUNDS, notaryNode.legalIdentity).returnValue.getOrThrow()
+ rpcProxy.startFlow(::CashPaymentFlow, 100.POUNDS, notaryNodeIdentity).returnValue.getOrThrow()
val moreResults = rpcProxy.vaultQueryBy(criteria, paging, sorting)
assertEquals(3, moreResults.totalStatesAvailable) // 629 - 100 + 100
@@ -209,7 +216,7 @@ class StandaloneCordaRPClientTest {
println(startCash)
assertTrue(startCash.isEmpty(), "Should not start with any cash")
- val flowHandle = rpcProxy.startFlow(::CashIssueFlow, 629.DOLLARS, OpaqueBytes.of(0), notaryNode.legalIdentity)
+ val flowHandle = rpcProxy.startFlow(::CashIssueFlow, 629.DOLLARS, OpaqueBytes.of(0), notaryNodeIdentity)
println("Started issuing cash, waiting on result")
flowHandle.returnValue.get()
diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt
index e798438fa7..3502145ae0 100644
--- a/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt
+++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt
@@ -7,7 +7,6 @@ import net.corda.core.internal.concurrent.thenMatch
import net.corda.core.messaging.RPCOps
import net.corda.core.utilities.getOrThrow
import net.corda.node.services.messaging.getRpcContext
-import net.corda.nodeapi.RPCSinceVersion
import net.corda.testing.RPCDriverExposedDSLInterface
import net.corda.testing.rpcDriver
import net.corda.testing.rpcTestUser
diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt
index f31469bcb4..4411bbfd07 100644
--- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt
+++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt
@@ -3,7 +3,6 @@ package net.corda.client.rpc
import net.corda.core.messaging.RPCOps
import net.corda.node.services.messaging.getRpcContext
import net.corda.node.services.messaging.requirePermission
-import net.corda.nodeapi.PermissionException
import net.corda.nodeapi.User
import net.corda.testing.RPCDriverExposedDSLInterface
import net.corda.testing.rpcDriver
diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RepeatingBytesInputStream.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RepeatingBytesInputStream.kt
index 06ed23f1bb..8887aa2476 100644
--- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RepeatingBytesInputStream.kt
+++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RepeatingBytesInputStream.kt
@@ -14,11 +14,11 @@ class RepeatingBytesInputStream(val bytesToRepeat: ByteArray, val numberOfBytes:
}
}
override fun read(byteArray: ByteArray, offset: Int, length: Int): Int {
- val until = Math.min(Math.min(offset + length, byteArray.size), offset + bytesLeft)
- for (i in offset .. until - 1) {
+ val lastIdx = Math.min(Math.min(offset + length, byteArray.size), offset + bytesLeft)
+ for (i in offset until lastIdx) {
byteArray[i] = bytesToRepeat[(numberOfBytes - bytesLeft + i - offset) % bytesToRepeat.size]
}
- val bytesRead = until - offset
+ val bytesRead = lastIdx - offset
bytesLeft -= bytesRead
return if (bytesRead == 0 && bytesLeft == 0) -1 else bytesRead
}
diff --git a/confidential-identities/build.gradle b/confidential-identities/build.gradle
new file mode 100644
index 0000000000..04212b3df1
--- /dev/null
+++ b/confidential-identities/build.gradle
@@ -0,0 +1,42 @@
+// Experimental Confidential Identities support for 1.0
+// This contains the prototype SwapIdentitiesFlow and SwapIdentitiesHandler, which can be used
+// for exchanging confidential identities as part of a flow, until a permanent solution is prepared.
+// Expect this module to be removed and merged into core in a later release.
+apply plugin: 'kotlin'
+apply plugin: CanonicalizerPlugin
+apply plugin: 'net.corda.plugins.publish-utils'
+apply plugin: 'net.corda.plugins.quasar-utils'
+apply plugin: 'net.corda.plugins.cordformation'
+apply plugin: 'com.jfrog.artifactory'
+
+description 'Corda Experimental Confidential Identities'
+
+dependencies {
+ // Note the :confidential-identities module is a CorDapp in its own right
+ // and CorDapps using :confidential-identities features should use 'cordapp' not 'compile' linkage.
+ cordaCompile project(':core')
+
+ // Quasar, for suspendable fibres.
+ compileOnly "co.paralleluniverse:quasar-core:$quasar_version:jdk8"
+
+ testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
+ testCompile "junit:junit:$junit_version"
+
+ // Guava: Google test library (collections test suite)
+ testCompile "com.google.guava:guava-testlib:$guava_version"
+
+ // Bring in the MockNode infrastructure for writing protocol unit tests.
+ testCompile project(":node")
+ testCompile project(":node-driver")
+
+ // AssertJ: for fluent assertions for testing
+ testCompile "org.assertj:assertj-core:$assertj_version"
+}
+
+jar {
+ baseName 'corda-confidential-identities'
+}
+
+publish {
+ name jar.baseName
+}
diff --git a/core/src/main/kotlin/net/corda/core/flows/IdentitySyncFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt
similarity index 80%
rename from core/src/main/kotlin/net/corda/core/flows/IdentitySyncFlow.kt
rename to confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt
index a70667c2a6..01f520afda 100644
--- a/core/src/main/kotlin/net/corda/core/flows/IdentitySyncFlow.kt
+++ b/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt
@@ -1,9 +1,10 @@
-package net.corda.core.flows
+package net.corda.confidential
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.ContractState
+import net.corda.core.flows.FlowLogic
+import net.corda.core.flows.FlowSession
import net.corda.core.identity.AbstractParty
-import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ProgressTracker
@@ -21,10 +22,10 @@ object IdentitySyncFlow {
* @return a mapping of well known identities to the confidential identities used in the transaction.
*/
// TODO: Can this be triggered automatically from [SendTransactionFlow]
- class Send(val otherSides: Set,
+ class Send(val otherSideSessions: Set,
val tx: WireTransaction,
override val progressTracker: ProgressTracker) : FlowLogic() {
- constructor(otherSide: Party, tx: WireTransaction) : this(setOf(otherSide), tx, tracker())
+ constructor(otherSide: FlowSession, tx: WireTransaction) : this(setOf(otherSide), tx, tracker())
companion object {
object SYNCING_IDENTITIES : ProgressTracker.Step("Syncing identities")
@@ -39,14 +40,14 @@ object IdentitySyncFlow {
val identities: Set = states.flatMap { it.participants }.toSet()
// Filter participants down to the set of those not in the network map (are not well known)
val confidentialIdentities = identities
- .filter { serviceHub.networkMapCache.getNodeByLegalIdentityKey(it.owningKey) == null }
+ .filter { serviceHub.networkMapCache.getNodesByLegalIdentityKey(it.owningKey).isEmpty() }
.toList()
val identityCertificates: Map = identities
.map { Pair(it, serviceHub.identityService.certificateFromKey(it.owningKey)) }.toMap()
- otherSides.forEach { otherSide ->
- val requestedIdentities: List = sendAndReceive>(otherSide, confidentialIdentities).unwrap { req ->
- require(req.all { it in identityCertificates.keys }) { "${otherSide} requested a confidential identity not part of transaction: ${tx.id}" }
+ otherSideSessions.forEach { otherSideSession ->
+ val requestedIdentities: List = otherSideSession.sendAndReceive>(confidentialIdentities).unwrap { req ->
+ require(req.all { it in identityCertificates.keys }) { "${otherSideSession.counterparty} requested a confidential identity not part of transaction: ${tx.id}" }
req
}
val sendIdentities: List = requestedIdentities.map {
@@ -56,7 +57,7 @@ object IdentitySyncFlow {
else
throw IllegalStateException("Counterparty requested a confidential identity for which we do not have the certificate path: ${tx.id}")
}
- send(otherSide, sendIdentities)
+ otherSideSession.send(sendIdentities)
}
}
@@ -66,7 +67,7 @@ object IdentitySyncFlow {
* Handle an offer to provide proof of identity (in the form of certificate paths) for confidential identities which
* we do not yet know about.
*/
- class Receive(val otherSide: Party) : FlowLogic() {
+ class Receive(val otherSideSession: FlowSession) : FlowLogic() {
companion object {
object RECEIVING_IDENTITIES : ProgressTracker.Step("Receiving confidential identities")
object RECEIVING_CERTIFICATES : ProgressTracker.Step("Receiving certificates for unknown identities")
@@ -77,10 +78,10 @@ object IdentitySyncFlow {
@Suspendable
override fun call(): Unit {
progressTracker.currentStep = RECEIVING_IDENTITIES
- val allIdentities = receive>(otherSide).unwrap { it }
- val unknownIdentities = allIdentities.filter { serviceHub.identityService.partyFromAnonymous(it) == null }
+ val allIdentities = otherSideSession.receive>().unwrap { it }
+ val unknownIdentities = allIdentities.filter { serviceHub.identityService.wellKnownPartyFromAnonymous(it) == null }
progressTracker.currentStep = RECEIVING_CERTIFICATES
- val missingIdentities = sendAndReceive>(otherSide, unknownIdentities)
+ val missingIdentities = otherSideSession.sendAndReceive>(unknownIdentities)
// Batch verify the identities we've received, so we know they're all correct before we start storing them in
// the identity service
diff --git a/core/src/main/kotlin/net/corda/core/flows/TransactionKeyFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt
similarity index 56%
rename from core/src/main/kotlin/net/corda/core/flows/TransactionKeyFlow.kt
rename to confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt
index 120d6c6a56..7257620d19 100644
--- a/core/src/main/kotlin/net/corda/core/flows/TransactionKeyFlow.kt
+++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt
@@ -1,23 +1,27 @@
-package net.corda.core.flows
+package net.corda.confidential
import co.paralleluniverse.fibers.Suspendable
+import net.corda.core.flows.FlowLogic
+import net.corda.core.flows.InitiatingFlow
+import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.AnonymousParty
-import net.corda.core.identity.PartyAndCertificate
import net.corda.core.identity.Party
+import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.IdentityService
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.unwrap
/**
- * Very basic flow which exchanges transaction key and certificate paths between two parties in a transaction.
- * This is intended for use as a subflow of another flow.
+ * Very basic flow which generates new confidential identities for parties in a transaction and exchanges the transaction
+ * key and certificate paths between the parties. This is intended for use as a subflow of another flow which builds a
+ * transaction.
*/
@StartableByRPC
@InitiatingFlow
-class TransactionKeyFlow(val otherSide: Party,
- val revocationEnabled: Boolean,
+class SwapIdentitiesFlow(private val otherParty: Party,
+ private val revocationEnabled: Boolean,
override val progressTracker: ProgressTracker) : FlowLogic>() {
- constructor(otherSide: Party) : this(otherSide, false, tracker())
+ constructor(otherParty: Party) : this(otherParty, false, tracker())
companion object {
object AWAITING_KEY : ProgressTracker.Step("Awaiting key")
@@ -35,18 +39,19 @@ class TransactionKeyFlow(val otherSide: Party,
@Suspendable
override fun call(): LinkedHashMap {
progressTracker.currentStep = AWAITING_KEY
- val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentityAndCert, revocationEnabled)
+ val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, revocationEnabled)
// Special case that if we're both parties, a single identity is generated
val identities = LinkedHashMap()
- if (otherSide == serviceHub.myInfo.legalIdentity) {
- identities.put(otherSide, legalIdentityAnonymous.party.anonymise())
+ if (serviceHub.myInfo.isLegalIdentity(otherParty)) {
+ identities.put(otherParty, legalIdentityAnonymous.party.anonymise())
} else {
- val anonymousOtherSide = sendAndReceive(otherSide, legalIdentityAnonymous).unwrap { confidentialIdentity ->
- validateAndRegisterIdentity(serviceHub.identityService, otherSide, confidentialIdentity)
+ val otherSession = initiateFlow(otherParty)
+ val anonymousOtherSide = otherSession.sendAndReceive(legalIdentityAnonymous).unwrap { confidentialIdentity ->
+ validateAndRegisterIdentity(serviceHub.identityService, otherSession.counterparty, confidentialIdentity)
}
- identities.put(serviceHub.myInfo.legalIdentity, legalIdentityAnonymous.party.anonymise())
- identities.put(otherSide, anonymousOtherSide.party.anonymise())
+ identities.put(ourIdentity, legalIdentityAnonymous.party.anonymise())
+ identities.put(otherSession.counterparty, anonymousOtherSide.party.anonymise())
}
return identities
}
diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt
new file mode 100644
index 0000000000..753d9a3927
--- /dev/null
+++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt
@@ -0,0 +1,28 @@
+package net.corda.confidential
+
+import co.paralleluniverse.fibers.Suspendable
+import net.corda.core.flows.FlowLogic
+import net.corda.core.flows.FlowSession
+import net.corda.core.identity.PartyAndCertificate
+import net.corda.core.utilities.ProgressTracker
+import net.corda.core.utilities.unwrap
+
+class SwapIdentitiesHandler(val otherSideSession: FlowSession, val revocationEnabled: Boolean) : FlowLogic() {
+ constructor(otherSideSession: FlowSession) : this(otherSideSession, false)
+
+ companion object {
+ object SENDING_KEY : ProgressTracker.Step("Sending key")
+ }
+
+ override val progressTracker: ProgressTracker = ProgressTracker(SENDING_KEY)
+
+ @Suspendable
+ override fun call() {
+ val revocationEnabled = false
+ progressTracker.currentStep = SENDING_KEY
+ val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, revocationEnabled)
+ otherSideSession.sendAndReceive(legalIdentityAnonymous).unwrap { confidentialIdentity ->
+ SwapIdentitiesFlow.validateAndRegisterIdentity(serviceHub.identityService, otherSideSession.counterparty, confidentialIdentity)
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt
similarity index 67%
rename from core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt
rename to confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt
index 401b864fb1..f2907c7c7c 100644
--- a/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt
+++ b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt
@@ -1,6 +1,10 @@
-package net.corda.core.flows
+package net.corda.confidential
import co.paralleluniverse.fibers.Suspendable
+import net.corda.core.flows.FlowLogic
+import net.corda.core.flows.FlowSession
+import net.corda.core.flows.InitiatedBy
+import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.OpaqueBytes
@@ -9,9 +13,7 @@ import net.corda.core.utilities.unwrap
import net.corda.finance.DOLLARS
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueAndPaymentFlow
-import net.corda.testing.ALICE
-import net.corda.testing.BOB
-import net.corda.testing.DUMMY_NOTARY
+import net.corda.testing.*
import net.corda.testing.node.MockNetwork
import org.junit.After
import org.junit.Before
@@ -24,6 +26,7 @@ class IdentitySyncFlowTests {
@Before
fun before() {
+ setCordappPackages("net.corda.finance.contracts.asset")
// We run this in parallel threads to help catch any race conditions that may exist.
mockNet = MockNetwork(networkSendManuallyPumped = false, threadPerNode = true)
}
@@ -31,6 +34,7 @@ class IdentitySyncFlowTests {
@After
fun cleanUp() {
mockNet.stopNodes()
+ unsetCordappPackages()
}
@Test
@@ -39,25 +43,26 @@ class IdentitySyncFlowTests {
val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = mockNet.createPartyNode(notaryNode.network.myAddress, ALICE.name)
val bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name)
- val alice: Party = aliceNode.services.myInfo.legalIdentity
- val bob: Party = bobNode.services.myInfo.legalIdentity
- bobNode.registerInitiatedFlow(Receive::class.java)
+ val alice: Party = aliceNode.services.myInfo.chooseIdentity()
+ val bob: Party = bobNode.services.myInfo.chooseIdentity()
+ bobNode.internals.registerInitiatedFlow(Receive::class.java)
// Alice issues then pays some cash to a new confidential identity that Bob doesn't know about
val anonymous = true
val ref = OpaqueBytes.of(0x01)
- val issueFlow = aliceNode.services.startFlow(CashIssueAndPaymentFlow(1000.DOLLARS, ref, alice, anonymous, notaryNode.services.myInfo.notaryIdentity))
+ val notary = aliceNode.services.getDefaultNotary()
+ val issueFlow = aliceNode.services.startFlow(CashIssueAndPaymentFlow(1000.DOLLARS, ref, alice, anonymous, notary))
val issueTx = issueFlow.resultFuture.getOrThrow().stx
val confidentialIdentity = issueTx.tx.outputs.map { it.data }.filterIsInstance