mirror of
https://github.com/corda/corda.git
synced 2025-06-01 15:10:54 +00:00
Merge pull request #1205 from corda/os-merge-244167d
O/S merge from 244167d
This commit is contained in:
commit
300e0573fe
9
.idea/compiler.xml
generated
9
.idea/compiler.xml
generated
@ -54,6 +54,12 @@
|
|||||||
<module name="contracts-states_test" target="1.8" />
|
<module name="contracts-states_test" target="1.8" />
|
||||||
<module name="corda-core_integrationTest" target="1.8" />
|
<module name="corda-core_integrationTest" target="1.8" />
|
||||||
<module name="corda-core_smokeTest" target="1.8" />
|
<module name="corda-core_smokeTest" target="1.8" />
|
||||||
|
<module name="corda-enterprise-testing_main" target="1.8" />
|
||||||
|
<module name="corda-enterprise-testing_test" target="1.8" />
|
||||||
|
<module name="corda-enterprise-tools_main" target="1.8" />
|
||||||
|
<module name="corda-enterprise-tools_test" target="1.8" />
|
||||||
|
<module name="corda-enterprise_main" target="1.8" />
|
||||||
|
<module name="corda-enterprise_test" target="1.8" />
|
||||||
<module name="corda-finance_integrationTest" target="1.8" />
|
<module name="corda-finance_integrationTest" target="1.8" />
|
||||||
<module name="corda-project-testing_main" target="1.8" />
|
<module name="corda-project-testing_main" target="1.8" />
|
||||||
<module name="corda-project-testing_test" target="1.8" />
|
<module name="corda-project-testing_test" target="1.8" />
|
||||||
@ -93,6 +99,7 @@
|
|||||||
<module name="demobench_main" target="1.8" />
|
<module name="demobench_main" target="1.8" />
|
||||||
<module name="demobench_test" target="1.8" />
|
<module name="demobench_test" target="1.8" />
|
||||||
<module name="dist_binFiles" target="1.8" />
|
<module name="dist_binFiles" target="1.8" />
|
||||||
|
<module name="dist_installerFiles" target="1.8" />
|
||||||
<module name="dist_licenseFiles" target="1.8" />
|
<module name="dist_licenseFiles" target="1.8" />
|
||||||
<module name="dist_main" target="1.8" />
|
<module name="dist_main" target="1.8" />
|
||||||
<module name="dist_readmeFiles" target="1.8" />
|
<module name="dist_readmeFiles" target="1.8" />
|
||||||
@ -170,6 +177,8 @@
|
|||||||
<module name="mock_test" target="1.8" />
|
<module name="mock_test" target="1.8" />
|
||||||
<module name="network-bootstrapper_main" target="1.8" />
|
<module name="network-bootstrapper_main" target="1.8" />
|
||||||
<module name="network-bootstrapper_test" target="1.8" />
|
<module name="network-bootstrapper_test" target="1.8" />
|
||||||
|
<module name="network-verifier_main" target="1.8" />
|
||||||
|
<module name="network-verifier_test" target="1.8" />
|
||||||
<module name="network-visualiser_main" target="1.8" />
|
<module name="network-visualiser_main" target="1.8" />
|
||||||
<module name="network-visualiser_test" target="1.8" />
|
<module name="network-visualiser_test" target="1.8" />
|
||||||
<module name="node-api_main" target="1.8" />
|
<module name="node-api_main" target="1.8" />
|
||||||
|
@ -7,12 +7,10 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.node.NotaryInfo
|
import net.corda.core.node.NotaryInfo
|
||||||
import net.corda.core.node.services.AttachmentId
|
|
||||||
import net.corda.core.serialization.SerializationDefaults
|
import net.corda.core.serialization.SerializationDefaults
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.node.services.config.CertChainPolicyConfig
|
|
||||||
import net.corda.node.services.config.EnterpriseConfiguration
|
import net.corda.node.services.config.EnterpriseConfiguration
|
||||||
import net.corda.node.services.config.MutualExclusionConfiguration
|
import net.corda.node.services.config.MutualExclusionConfiguration
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
@ -102,7 +100,7 @@ class BridgeSmokeTest {
|
|||||||
bridgeJar.copyToDirectory(testDir)
|
bridgeJar.copyToDirectory(testDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createNetworkParams(baseDirectory: Path) {
|
private fun createNetworkParams(baseDirectory: Path) {
|
||||||
val dummyNotaryParty = TestIdentity(DUMMY_NOTARY_NAME)
|
val dummyNotaryParty = TestIdentity(DUMMY_NOTARY_NAME)
|
||||||
val notaryInfo = NotaryInfo(dummyNotaryParty.party, false)
|
val notaryInfo = NotaryInfo(dummyNotaryParty.party, false)
|
||||||
val copier = NetworkParametersCopier(NetworkParameters(
|
val copier = NetworkParametersCopier(NetworkParameters(
|
||||||
@ -112,18 +110,18 @@ class BridgeSmokeTest {
|
|||||||
maxMessageSize = 10485760,
|
maxMessageSize = 10485760,
|
||||||
maxTransactionSize = 40000,
|
maxTransactionSize = 40000,
|
||||||
epoch = 1,
|
epoch = 1,
|
||||||
whitelistedContractImplementations = emptyMap<String, List<AttachmentId>>()
|
whitelistedContractImplementations = emptyMap()
|
||||||
), overwriteFile = true)
|
), overwriteFile = true)
|
||||||
copier.install(baseDirectory)
|
copier.install(baseDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun SSLConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
|
private fun SSLConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
|
||||||
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
|
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
|
||||||
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
|
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
|
||||||
|
|
||||||
certificatesDirectory.createDirectories()
|
certificatesDirectory.createDirectories()
|
||||||
if (!trustStoreFile.exists()) {
|
if (!trustStoreFile.exists()) {
|
||||||
loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/${DEV_CA_TRUST_STORE_FILE}"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword)
|
loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_TRUST_STORE_FILE"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
||||||
@ -189,13 +187,13 @@ class BridgeSmokeTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun serverListening(host: String, port: Int): Boolean {
|
private fun serverListening(host: String, port: Int): Boolean {
|
||||||
var s: Socket? = null
|
var s: Socket? = null
|
||||||
try {
|
return try {
|
||||||
s = Socket(host, port)
|
s = Socket(host, port)
|
||||||
return true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return false
|
false
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
s?.close()
|
s?.close()
|
||||||
@ -212,7 +210,6 @@ class BridgeSmokeTest {
|
|||||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||||
doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
|
doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
|
||||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||||
doReturn(emptyList<CertChainPolicyConfig>()).whenever(it).certificateChainCheckPolicies
|
|
||||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
|
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
|
||||||
}
|
}
|
||||||
val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE)
|
val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE)
|
||||||
|
@ -17,7 +17,6 @@ import net.corda.bridge.createBridgeKeyStores
|
|||||||
import net.corda.bridge.createNetworkParams
|
import net.corda.bridge.createNetworkParams
|
||||||
import net.corda.bridge.services.artemis.BridgeArtemisConnectionServiceImpl
|
import net.corda.bridge.services.artemis.BridgeArtemisConnectionServiceImpl
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.config.CertChainPolicyConfig
|
|
||||||
import net.corda.node.services.config.EnterpriseConfiguration
|
import net.corda.node.services.config.EnterpriseConfiguration
|
||||||
import net.corda.node.services.config.MutualExclusionConfiguration
|
import net.corda.node.services.config.MutualExclusionConfiguration
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
@ -102,7 +101,6 @@ class ArtemisConnectionTest {
|
|||||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||||
doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
|
doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
|
||||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||||
doReturn(emptyList<CertChainPolicyConfig>()).whenever(it).certificateChainCheckPolicies
|
|
||||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
|
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
|
||||||
}
|
}
|
||||||
val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE)
|
val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE)
|
||||||
|
@ -188,6 +188,7 @@ allprojects {
|
|||||||
jvmTarget = "1.8"
|
jvmTarget = "1.8"
|
||||||
javaParameters = true // Useful for reflection.
|
javaParameters = true // Useful for reflection.
|
||||||
freeCompilerArgs = ['-Xjvm-default=compatibility']
|
freeCompilerArgs = ['-Xjvm-default=compatibility']
|
||||||
|
allWarningsAsErrors = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@ task metadata(type: ProGuardTask) {
|
|||||||
|
|
||||||
keepattributes '*'
|
keepattributes '*'
|
||||||
dontoptimize
|
dontoptimize
|
||||||
printseeds
|
|
||||||
verbose
|
verbose
|
||||||
|
|
||||||
dontwarn 'com.sun.jna.**'
|
dontwarn 'com.sun.jna.**'
|
||||||
@ -47,6 +46,7 @@ task metadata(type: ProGuardTask) {
|
|||||||
dontwarn 'org.jetbrains.kotlin.com.intellij.**'
|
dontwarn 'org.jetbrains.kotlin.com.intellij.**'
|
||||||
dontwarn 'org.jetbrains.kotlin.com.google.j2objc.annotations.**'
|
dontwarn 'org.jetbrains.kotlin.com.google.j2objc.annotations.**'
|
||||||
dontwarn 'org.jetbrains.kotlin.com.google.errorprone.annotations.**'
|
dontwarn 'org.jetbrains.kotlin.com.google.errorprone.annotations.**'
|
||||||
|
dontnote
|
||||||
|
|
||||||
keep 'class org.jetbrains.kotlin.load.java.JvmAnnotationNames { *; }'
|
keep 'class org.jetbrains.kotlin.load.java.JvmAnnotationNames { *; }'
|
||||||
keep 'class org.jetbrains.kotlin.metadata.** { *; }', includedescriptorclasses: true
|
keep 'class org.jetbrains.kotlin.metadata.** { *; }', includedescriptorclasses: true
|
||||||
@ -68,6 +68,7 @@ task validate(type: ProGuardTask) {
|
|||||||
verbose
|
verbose
|
||||||
|
|
||||||
dontwarn 'org.jetbrains.kotlin.com.google.errorprone.annotations.**'
|
dontwarn 'org.jetbrains.kotlin.com.google.errorprone.annotations.**'
|
||||||
|
dontnote
|
||||||
|
|
||||||
keep 'class *'
|
keep 'class *'
|
||||||
}
|
}
|
||||||
@ -77,5 +78,4 @@ artifacts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultTasks "metadata"
|
defaultTasks "metadata"
|
||||||
assemble.dependsOn metadata
|
|
||||||
metadata.finalizedBy validate
|
metadata.finalizedBy validate
|
||||||
|
@ -401,6 +401,11 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
|
|||||||
testToStringSerialisation(UUID.randomUUID())
|
testToStringSerialisation(UUID.randomUUID())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun Instant() {
|
||||||
|
testToStringSerialisation(Instant.now())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Date is treated as Instant`() {
|
fun `Date is treated as Instant`() {
|
||||||
val date = Date()
|
val date = Date()
|
||||||
|
@ -13,7 +13,6 @@ package net.corda.client.jfx
|
|||||||
import net.corda.client.jfx.model.NodeMonitorModel
|
import net.corda.client.jfx.model.NodeMonitorModel
|
||||||
import net.corda.client.jfx.model.ProgressTrackingEvent
|
import net.corda.client.jfx.model.ProgressTrackingEvent
|
||||||
import net.corda.core.context.InvocationOrigin
|
import net.corda.core.context.InvocationOrigin
|
||||||
import net.corda.core.contracts.Amount
|
|
||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.crypto.isFulfilledBy
|
import net.corda.core.crypto.isFulfilledBy
|
||||||
import net.corda.core.crypto.keys
|
import net.corda.core.crypto.keys
|
||||||
@ -32,12 +31,9 @@ import net.corda.core.transactions.SignedTransaction
|
|||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.USD
|
|
||||||
import net.corda.finance.flows.CashExitFlow
|
|
||||||
import net.corda.finance.flows.CashIssueFlow
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
import net.corda.finance.flows.CashPaymentFlow
|
import net.corda.finance.flows.CashPaymentFlow
|
||||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
import net.corda.node.services.Permissions.Companion.all
|
||||||
import net.corda.node.services.Permissions.Companion.startFlow
|
|
||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.*
|
||||||
import net.corda.testing.driver.DriverParameters
|
import net.corda.testing.driver.DriverParameters
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
@ -75,18 +71,7 @@ class NodeMonitorModelTest : IntegrationTest() {
|
|||||||
|
|
||||||
private fun setup(runTest: () -> Unit) {
|
private fun setup(runTest: () -> Unit) {
|
||||||
driver(DriverParameters(extraCordappPackagesToScan = listOf("net.corda.finance"))) {
|
driver(DriverParameters(extraCordappPackagesToScan = listOf("net.corda.finance"))) {
|
||||||
val cashUser = User("user1", "test", permissions = setOf(
|
val cashUser = User("user1", "test", permissions = setOf(all()))
|
||||||
startFlow<CashIssueFlow>(),
|
|
||||||
startFlow<CashPaymentFlow>(),
|
|
||||||
startFlow<CashExitFlow>(),
|
|
||||||
invokeRpc(CordaRPCOps::notaryIdentities),
|
|
||||||
invokeRpc("vaultTrackBy"),
|
|
||||||
invokeRpc("vaultQueryBy"),
|
|
||||||
invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed),
|
|
||||||
invokeRpc(CordaRPCOps::stateMachineRecordedTransactionMappingFeed),
|
|
||||||
invokeRpc(CordaRPCOps::stateMachinesFeed),
|
|
||||||
invokeRpc(CordaRPCOps::networkMapFeed))
|
|
||||||
)
|
|
||||||
val aliceNodeHandle = startNode(providedName = ALICE_NAME, rpcUsers = listOf(cashUser)).getOrThrow()
|
val aliceNodeHandle = startNode(providedName = ALICE_NAME, rpcUsers = listOf(cashUser)).getOrThrow()
|
||||||
aliceNode = aliceNodeHandle.nodeInfo
|
aliceNode = aliceNodeHandle.nodeInfo
|
||||||
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
|
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
|
||||||
@ -135,11 +120,7 @@ class NodeMonitorModelTest : IntegrationTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `cash issue works end to end`() = setup {
|
fun `cash issue works end to end`() = setup {
|
||||||
rpc.startFlow(::CashIssueFlow,
|
rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryParty)
|
||||||
Amount(100, USD),
|
|
||||||
OpaqueBytes(ByteArray(1, { 1 })),
|
|
||||||
notaryParty
|
|
||||||
)
|
|
||||||
|
|
||||||
vaultUpdates.expectEvents(isStrict = false) {
|
vaultUpdates.expectEvents(isStrict = false) {
|
||||||
sequence(
|
sequence(
|
||||||
|
@ -111,12 +111,12 @@ class NodeMonitorModel : AutoCloseable {
|
|||||||
* TODO provide an unsubscribe mechanism
|
* TODO provide an unsubscribe mechanism
|
||||||
*/
|
*/
|
||||||
fun register(nodeHostAndPort: NetworkHostAndPort, username: String, password: String) {
|
fun register(nodeHostAndPort: NetworkHostAndPort, username: String, password: String) {
|
||||||
|
|
||||||
// `retryableStateMachineUpdatesSubject` will change it's upstream subscriber in case of RPC connection failure, this `Observable` should
|
// `retryableStateMachineUpdatesSubject` will change it's upstream subscriber in case of RPC connection failure, this `Observable` should
|
||||||
// never produce an error.
|
// never produce an error.
|
||||||
// `stateMachineUpdatesSubject` will stay firmly subscribed to `retryableStateMachineUpdatesSubject`
|
// `stateMachineUpdatesSubject` will stay firmly subscribed to `retryableStateMachineUpdatesSubject`
|
||||||
retryableStateMachineUpdatesSubject.subscribe(stateMachineUpdatesSubject)
|
retryableStateMachineUpdatesSubject.subscribe(stateMachineUpdatesSubject)
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
// Proxy may change during re-connect, ensure that subject wiring accurately reacts to this activity.
|
// Proxy may change during re-connect, ensure that subject wiring accurately reacts to this activity.
|
||||||
proxyObservable.addListener { _, _, wrapper ->
|
proxyObservable.addListener { _, _, wrapper ->
|
||||||
if (wrapper != null) {
|
if (wrapper != null) {
|
||||||
|
@ -57,14 +57,16 @@ class StateMachineDataModel {
|
|||||||
private val progressTracking by observable(NodeMonitorModel::progressTracking)
|
private val progressTracking by observable(NodeMonitorModel::progressTracking)
|
||||||
private val progressEvents = progressTracking.recordAsAssociation(ProgressTrackingEvent::stateMachineId)
|
private val progressEvents = progressTracking.recordAsAssociation(ProgressTrackingEvent::stateMachineId)
|
||||||
|
|
||||||
val counter = Counter()
|
private val counter = Counter()
|
||||||
|
|
||||||
private val stateMachineIndexMap = HashMap<StateMachineRunId, Int>()
|
private val stateMachineIndexMap = HashMap<StateMachineRunId, Int>()
|
||||||
private val stateMachineStatus = stateMachineUpdates.fold(FXCollections.observableArrayList<SimpleObjectProperty<StateMachineStatus>>()) { list, update ->
|
private val stateMachineStatus = stateMachineUpdates.fold(FXCollections.observableArrayList<SimpleObjectProperty<StateMachineStatus>>()) { list, update ->
|
||||||
when (update) {
|
when (update) {
|
||||||
is StateMachineUpdate.Added -> {
|
is StateMachineUpdate.Added -> {
|
||||||
counter.addSmm()
|
counter.addSmm()
|
||||||
val flowInitiator= update.stateMachineInfo.initiator
|
// TODO Use invocationContext instead
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
val flowInitiator = update.stateMachineInfo.initiator
|
||||||
val added: SimpleObjectProperty<StateMachineStatus> =
|
val added: SimpleObjectProperty<StateMachineStatus> =
|
||||||
SimpleObjectProperty(StateMachineStatus.Added(update.id, update.stateMachineInfo.flowLogicClassName, flowInitiator))
|
SimpleObjectProperty(StateMachineStatus.Added(update.id, update.stateMachineInfo.flowLogicClassName, flowInitiator))
|
||||||
list.add(added)
|
list.add(added)
|
||||||
@ -83,7 +85,7 @@ class StateMachineDataModel {
|
|||||||
private val stateMachineDataList = stateMachineStatus.map {
|
private val stateMachineDataList = stateMachineStatus.map {
|
||||||
val smStatus = it.value as StateMachineStatus.Added
|
val smStatus = it.value as StateMachineStatus.Added
|
||||||
val id = smStatus.id
|
val id = smStatus.id
|
||||||
val progress = SimpleObjectProperty(progressEvents.get(id))
|
val progress = SimpleObjectProperty(progressEvents[id])
|
||||||
StateMachineData(id, smStatus.stateMachineName, smStatus.flowInitiator,
|
StateMachineData(id, smStatus.stateMachineName, smStatus.flowInitiator,
|
||||||
Pair(it, EasyBind.map(progress) { ProgressStatus(it?.message) }))
|
Pair(it, EasyBind.map(progress) { ProgressStatus(it?.message) }))
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ data class PartiallyResolvedTransaction(
|
|||||||
return PartiallyResolvedTransaction(
|
return PartiallyResolvedTransaction(
|
||||||
transaction = transaction,
|
transaction = transaction,
|
||||||
inputs = transaction.inputs.map { stateRef ->
|
inputs = transaction.inputs.map { stateRef ->
|
||||||
val tx = inputTransactions.get(stateRef)
|
val tx = inputTransactions[stateRef]
|
||||||
if (tx == null) {
|
if (tx == null) {
|
||||||
InputResolution.Unresolved(stateRef)
|
InputResolution.Unresolved(stateRef)
|
||||||
} else {
|
} else {
|
||||||
@ -74,7 +74,7 @@ data class PartiallyResolvedTransaction(
|
|||||||
val outputCount = transaction.coreTransaction.inputs.size
|
val outputCount = transaction.coreTransaction.inputs.size
|
||||||
val stateRefs = (0 until outputCount).map { StateRef(transaction.id, it) }
|
val stateRefs = (0 until outputCount).map { StateRef(transaction.id, it) }
|
||||||
stateRefs.map { stateRef ->
|
stateRefs.map { stateRef ->
|
||||||
val tx = inputTransactions.get(stateRef)
|
val tx = inputTransactions[stateRef]
|
||||||
if (tx == null) {
|
if (tx == null) {
|
||||||
OutputResolution.Unresolved(stateRef)
|
OutputResolution.Unresolved(stateRef)
|
||||||
} else {
|
} else {
|
||||||
@ -94,6 +94,7 @@ class TransactionDataModel {
|
|||||||
private val collectedTransactions = transactions.recordInSequence().distinctBy { it.id }
|
private val collectedTransactions = transactions.recordInSequence().distinctBy { it.id }
|
||||||
private val rpcProxy by observableValue(NodeMonitorModel::proxyObservable)
|
private val rpcProxy by observableValue(NodeMonitorModel::proxyObservable)
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
val partiallyResolvedTransactions = collectedTransactions.map {
|
val partiallyResolvedTransactions = collectedTransactions.map {
|
||||||
PartiallyResolvedTransaction.fromSignedTransaction(it,
|
PartiallyResolvedTransaction.fromSignedTransaction(it,
|
||||||
it.inputs.map { stateRef ->
|
it.inputs.map { stateRef ->
|
||||||
|
@ -84,14 +84,12 @@ class FlowsExecutionModeTests : NodeBasedTest(listOf("net.corda.finance.contract
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
|
|
||||||
node = startNode(ALICE_NAME, rpcUsers = listOf(rpcUser))
|
node = startNode(ALICE_NAME, rpcUsers = listOf(rpcUser))
|
||||||
client = CordaRPCClient(node.internals.configuration.rpcOptions.address!!)
|
client = CordaRPCClient(node.internals.configuration.rpcOptions.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `flows draining mode can be enabled and queried`() {
|
fun `flows draining mode can be enabled and queried`() {
|
||||||
|
|
||||||
asALoggerUser { rpcOps ->
|
asALoggerUser { rpcOps ->
|
||||||
val newValue = true
|
val newValue = true
|
||||||
rpcOps.setFlowsDrainingModeEnabled(true)
|
rpcOps.setFlowsDrainingModeEnabled(true)
|
||||||
@ -104,7 +102,6 @@ class FlowsExecutionModeTests : NodeBasedTest(listOf("net.corda.finance.contract
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `flows draining mode can be disabled and queried`() {
|
fun `flows draining mode can be disabled and queried`() {
|
||||||
|
|
||||||
asALoggerUser { rpcOps ->
|
asALoggerUser { rpcOps ->
|
||||||
rpcOps.setFlowsDrainingModeEnabled(true)
|
rpcOps.setFlowsDrainingModeEnabled(true)
|
||||||
val newValue = false
|
val newValue = false
|
||||||
@ -118,7 +115,6 @@ class FlowsExecutionModeTests : NodeBasedTest(listOf("net.corda.finance.contract
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `node starts with flows draining mode disabled`() {
|
fun `node starts with flows draining mode disabled`() {
|
||||||
|
|
||||||
asALoggerUser { rpcOps ->
|
asALoggerUser { rpcOps ->
|
||||||
val defaultStartingMode = rpcOps.isFlowsDrainingModeEnabled()
|
val defaultStartingMode = rpcOps.isFlowsDrainingModeEnabled()
|
||||||
|
|
||||||
@ -127,14 +123,12 @@ class FlowsExecutionModeTests : NodeBasedTest(listOf("net.corda.finance.contract
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun login(username: String, password: String, externalTrace: Trace? = null, impersonatedActor: Actor? = null): CordaRPCConnection {
|
private fun login(username: String, password: String, externalTrace: Trace? = null, impersonatedActor: Actor? = null): CordaRPCConnection {
|
||||||
|
|
||||||
return client.start(username, password, externalTrace, impersonatedActor)
|
return client.start(username, password, externalTrace, impersonatedActor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun asALoggerUser(action: (CordaRPCOps) -> Unit) {
|
private fun asALoggerUser(action: (CordaRPCOps) -> Unit) {
|
||||||
|
|
||||||
login(rpcUser.username, rpcUser.password).use {
|
login(rpcUser.username, rpcUser.password).use {
|
||||||
action(it.proxy)
|
action(it.proxy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,7 @@ task predeterminise(type: ProGuardTask) {
|
|||||||
dontpreverify
|
dontpreverify
|
||||||
dontobfuscate
|
dontobfuscate
|
||||||
dontoptimize
|
dontoptimize
|
||||||
|
dontnote
|
||||||
printseeds
|
printseeds
|
||||||
verbose
|
verbose
|
||||||
|
|
||||||
@ -133,6 +134,7 @@ task determinise(type: ProGuardTask) {
|
|||||||
keepattributes '*'
|
keepattributes '*'
|
||||||
keepdirectories
|
keepdirectories
|
||||||
dontobfuscate
|
dontobfuscate
|
||||||
|
dontnote
|
||||||
printseeds
|
printseeds
|
||||||
verbose
|
verbose
|
||||||
|
|
||||||
@ -178,7 +180,6 @@ task checkDeterminism(type: ProGuardTask, dependsOn: jdkTask) {
|
|||||||
defaultTasks "determinise"
|
defaultTasks "determinise"
|
||||||
determinise.finalizedBy metafix
|
determinise.finalizedBy metafix
|
||||||
metafix.finalizedBy checkDeterminism
|
metafix.finalizedBy checkDeterminism
|
||||||
assemble.dependsOn checkDeterminism
|
|
||||||
|
|
||||||
def deterministicJar = metafix.outputs.files.singleFile
|
def deterministicJar = metafix.outputs.files.singleFile
|
||||||
artifacts {
|
artifacts {
|
||||||
|
@ -18,10 +18,10 @@ import java.util.*
|
|||||||
* A unique identifier for a single state machine run, valid across node restarts. Note that a single run always
|
* A unique identifier for a single state machine run, valid across node restarts. Note that a single run always
|
||||||
* has at least one flow, but that flow may also invoke sub-flows: they all share the same run id.
|
* has at least one flow, but that flow may also invoke sub-flows: they all share the same run id.
|
||||||
*/
|
*/
|
||||||
|
@DeleteForDJVM
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
data class StateMachineRunId(val uuid: UUID) {
|
data class StateMachineRunId(val uuid: UUID) {
|
||||||
companion object {
|
companion object {
|
||||||
@DeleteForDJVM
|
|
||||||
fun createRandom(): StateMachineRunId = StateMachineRunId(UUID.randomUUID())
|
fun createRandom(): StateMachineRunId = StateMachineRunId(UUID.randomUUID())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
package net.corda.core.internal
|
package net.corda.core.internal
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import net.corda.core.DeleteForDJVM
|
||||||
import net.corda.core.DoNotImplement
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
import net.corda.core.context.InvocationContext
|
import net.corda.core.context.InvocationContext
|
||||||
@ -20,6 +21,7 @@ import net.corda.core.node.ServiceHub
|
|||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
|
|
||||||
/** This is an internal interface that is implemented by code in the node module. You should look at [FlowLogic]. */
|
/** This is an internal interface that is implemented by code in the node module. You should look at [FlowLogic]. */
|
||||||
|
@DeleteForDJVM
|
||||||
@DoNotImplement
|
@DoNotImplement
|
||||||
interface FlowStateMachine<FLOWRETURN> {
|
interface FlowStateMachine<FLOWRETURN> {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@ -52,7 +52,7 @@ data class StateMachineInfo @JvmOverloads constructor(
|
|||||||
* An object representing information about the initiator of the flow. Note that this field is
|
* An object representing information about the initiator of the flow. Note that this field is
|
||||||
* superceded by the [invocationContext] property, which has more detail.
|
* superceded by the [invocationContext] property, which has more detail.
|
||||||
*/
|
*/
|
||||||
@Deprecated("There is more info available using 'context'") val initiator: FlowInitiator,
|
@Deprecated("There is more info available using 'invocationContext'") val initiator: FlowInitiator,
|
||||||
/** A [DataFeed] of the current progress step as a human readable string, and updates to that string. */
|
/** A [DataFeed] of the current progress step as a human readable string, and updates to that string. */
|
||||||
val progressTrackerStepAndUpdates: DataFeed<String, String>?,
|
val progressTrackerStepAndUpdates: DataFeed<String, String>?,
|
||||||
/** An [InvocationContext] describing why and by whom the flow was started. */
|
/** An [InvocationContext] describing why and by whom the flow was started. */
|
||||||
|
@ -12,11 +12,15 @@ package net.corda.core.node.services
|
|||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.DoNotImplement
|
import net.corda.core.DoNotImplement
|
||||||
|
import net.corda.core.crypto.CompositeKey
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.DigitalSignature
|
||||||
import net.corda.core.crypto.SignableData
|
import net.corda.core.crypto.SignableData
|
||||||
import net.corda.core.crypto.TransactionSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
|
import java.security.KeyPair
|
||||||
|
import java.security.PrivateKey
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The KMS is responsible for storing and using private keys to sign things. An implementation of this may, for example,
|
* The KMS is responsible for storing and using private keys to sign things. An implementation of this may, for example,
|
||||||
|
@ -140,7 +140,7 @@ object MappedSchemaValidator {
|
|||||||
fieldsFromOtherMappedSchema(schema) + methodsFromOtherMappedSchema(schema)
|
fieldsFromOtherMappedSchema(schema) + methodsFromOtherMappedSchema(schema)
|
||||||
|
|
||||||
/** Returns true if [javax.persistence] annotation expect [javax.persistence.Transient] is found. */
|
/** Returns true if [javax.persistence] annotation expect [javax.persistence.Transient] is found. */
|
||||||
private inline fun hasJpaAnnotation(annotations: Array<Annotation>) =
|
private fun hasJpaAnnotation(annotations: Array<Annotation>) =
|
||||||
annotations.any { annotation -> annotation.toString().startsWith("@javax.persistence.") && annotation !is javax.persistence.Transient }
|
annotations.any { annotation -> annotation.toString().startsWith("@javax.persistence.") && annotation !is javax.persistence.Transient }
|
||||||
|
|
||||||
class SchemaCrossReferenceReport(private val schema: String, private val entity: String, private val referencedSchema: String,
|
class SchemaCrossReferenceReport(private val schema: String, private val entity: String, private val referencedSchema: String,
|
||||||
|
@ -58,7 +58,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
|||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
constructor(componentGroups: List<ComponentGroup>) : this(componentGroups, PrivacySalt())
|
constructor(componentGroups: List<ComponentGroup>) : this(componentGroups, PrivacySalt())
|
||||||
|
|
||||||
@Deprecated("Required only in some unit-tests and for backwards compatibility purposes.", ReplaceWith("WireTransaction(val componentGroups: List<ComponentGroup>, override val privacySalt: PrivacySalt)"), DeprecationLevel.WARNING)
|
@Deprecated("Required only for backwards compatibility purposes.", ReplaceWith("WireTransaction(val componentGroups: List<ComponentGroup>, override val privacySalt: PrivacySalt)"), DeprecationLevel.WARNING)
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
constructor(inputs: List<StateRef>,
|
constructor(inputs: List<StateRef>,
|
||||||
attachments: List<SecureHash>,
|
attachments: List<SecureHash>,
|
||||||
|
@ -41,7 +41,8 @@ class ContractsDSLTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RunWith(Parameterized::class)
|
@RunWith(Parameterized::class)
|
||||||
class RequireSingleCommandTests(private val testFunction: (Collection<CommandWithParties<CommandData>>) -> CommandWithParties<CommandData>, description: String) {
|
class RequireSingleCommandTests(private val testFunction: (Collection<CommandWithParties<CommandData>>) -> CommandWithParties<CommandData>,
|
||||||
|
@Suppress("UNUSED_PARAMETER") description: String) {
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Parameterized.Parameters(name = "{1}")
|
@Parameterized.Parameters(name = "{1}")
|
||||||
@ -74,7 +75,8 @@ class ContractsDSLTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RunWith(Parameterized::class)
|
@RunWith(Parameterized::class)
|
||||||
class SelectWithSingleInputsTests(private val testFunction: (Collection<CommandWithParties<CommandData>>, PublicKey?, AbstractParty?) -> Iterable<CommandWithParties<CommandData>>, description: String) {
|
class SelectWithSingleInputsTests(private val testFunction: (Collection<CommandWithParties<CommandData>>, PublicKey?, AbstractParty?) -> Iterable<CommandWithParties<CommandData>>,
|
||||||
|
@Suppress("UNUSED_PARAMETER") description: String) {
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Parameterized.Parameters(name = "{1}")
|
@Parameterized.Parameters(name = "{1}")
|
||||||
@ -122,7 +124,8 @@ class ContractsDSLTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RunWith(Parameterized::class)
|
@RunWith(Parameterized::class)
|
||||||
class SelectWithMultipleInputsTests(private val testFunction: (Collection<CommandWithParties<CommandData>>, Collection<PublicKey>?, Collection<Party>?) -> Iterable<CommandWithParties<CommandData>>, description: String) {
|
class SelectWithMultipleInputsTests(private val testFunction: (Collection<CommandWithParties<CommandData>>, Collection<PublicKey>?, Collection<Party>?) -> Iterable<CommandWithParties<CommandData>>,
|
||||||
|
@Suppress("UNUSED_PARAMETER") description: String) {
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Parameterized.Parameters(name = "{1}")
|
@Parameterized.Parameters(name = "{1}")
|
||||||
|
@ -30,6 +30,7 @@ import net.corda.testing.dsl.LedgerDSL
|
|||||||
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
||||||
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
||||||
import net.corda.testing.internal.TEST_TX_TIME
|
import net.corda.testing.internal.TEST_TX_TIME
|
||||||
|
import net.corda.testing.internal.createWireTransaction
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.ledger
|
import net.corda.testing.node.ledger
|
||||||
@ -274,7 +275,7 @@ class PartialMerkleTreeTest {
|
|||||||
timeWindow: TimeWindow? = null,
|
timeWindow: TimeWindow? = null,
|
||||||
attachments: List<SecureHash> = emptyList()
|
attachments: List<SecureHash> = emptyList()
|
||||||
): WireTransaction {
|
): WireTransaction {
|
||||||
return WireTransaction(
|
return createWireTransaction(
|
||||||
inputs = testTx.inputs,
|
inputs = testTx.inputs,
|
||||||
attachments = attachments,
|
attachments = attachments,
|
||||||
outputs = testTx.outputs,
|
outputs = testTx.outputs,
|
||||||
|
@ -67,7 +67,7 @@ class AttachmentTests {
|
|||||||
bobNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java)
|
bobNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java)
|
||||||
// Insert an attachment into node zero's store directly.
|
// Insert an attachment into node zero's store directly.
|
||||||
val id = aliceNode.database.transaction {
|
val id = aliceNode.database.transaction {
|
||||||
aliceNode.attachments.importAttachment(fakeAttachment().inputStream())
|
aliceNode.attachments.importAttachment(fakeAttachment().inputStream(), "test", null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get node one to run a flow to fetch it and insert it.
|
// Get node one to run a flow to fetch it and insert it.
|
||||||
@ -120,7 +120,7 @@ class AttachmentTests {
|
|||||||
val attachment = fakeAttachment()
|
val attachment = fakeAttachment()
|
||||||
// Insert an attachment into node zero's store directly.
|
// Insert an attachment into node zero's store directly.
|
||||||
val id = aliceNode.database.transaction {
|
val id = aliceNode.database.transaction {
|
||||||
aliceNode.attachments.importAttachment(attachment.inputStream())
|
aliceNode.attachments.importAttachment(attachment.inputStream(), "test", null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Corrupt its store.
|
// Corrupt its store.
|
||||||
|
@ -156,7 +156,7 @@ class ResolveTransactionsFlowTest {
|
|||||||
}
|
}
|
||||||
// TODO: this operation should not require an explicit transaction
|
// TODO: this operation should not require an explicit transaction
|
||||||
val id = megaCorpNode.transaction {
|
val id = megaCorpNode.transaction {
|
||||||
megaCorpNode.services.attachments.importAttachment(makeJar())
|
megaCorpNode.services.attachments.importAttachment(makeJar(), "test", null)
|
||||||
}
|
}
|
||||||
val stx2 = makeTransactions(withAttachment = id).second
|
val stx2 = makeTransactions(withAttachment = id).second
|
||||||
val p = TestFlow(stx2, megaCorp)
|
val p = TestFlow(stx2, megaCorp)
|
||||||
@ -211,6 +211,7 @@ class ResolveTransactionsFlowTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
@InitiatedBy(TestFlow::class)
|
@InitiatedBy(TestFlow::class)
|
||||||
private class TestResponseFlow(val otherSideSession: FlowSession) : FlowLogic<Void?>() {
|
private class TestResponseFlow(val otherSideSession: FlowSession) : FlowLogic<Void?>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@ -21,10 +21,10 @@ import rx.Observable
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class TopologicalSortTest {
|
class TopologicalSortTest {
|
||||||
class DummyTransaction(
|
class DummyTransaction constructor(
|
||||||
override val id: SecureHash,
|
override val id: SecureHash,
|
||||||
override val inputs: List<StateRef>,
|
override val inputs: List<StateRef>,
|
||||||
val numberOfOutputs: Int,
|
@Suppress("CanBeParameter") private val numberOfOutputs: Int,
|
||||||
override val notary: Party
|
override val notary: Party
|
||||||
) : CoreTransaction() {
|
) : CoreTransaction() {
|
||||||
override val outputs: List<TransactionState<ContractState>> = (1..numberOfOutputs).map {
|
override val outputs: List<TransactionState<ContractState>> = (1..numberOfOutputs).map {
|
||||||
@ -78,7 +78,7 @@ class TopologicalSortTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Swap two random items
|
// Swap two random items
|
||||||
transactions.combine(Generator.intRange(0, N - 1), Generator.intRange(0, N - 2)) { txs, i, j ->
|
transactions.combine(Generator.intRange(0, N - 1), Generator.intRange(0, N - 2)) { txs, i, _ ->
|
||||||
val k = 0 // if (i == j) i + 1 else j
|
val k = 0 // if (i == j) i + 1 else j
|
||||||
val tmp = txs[i]
|
val tmp = txs[i]
|
||||||
txs[i] = txs[k]
|
txs[i] = txs[k]
|
||||||
@ -94,7 +94,7 @@ class TopologicalSortTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkTopologicallyOrdered(txs: List<SignedTransaction>) {
|
private fun checkTopologicallyOrdered(txs: List<SignedTransaction>) {
|
||||||
val outputs = HashSet<StateRef>()
|
val outputs = HashSet<StateRef>()
|
||||||
for (tx in txs) {
|
for (tx in txs) {
|
||||||
if (!outputs.containsAll(tx.inputs)) {
|
if (!outputs.containsAll(tx.inputs)) {
|
||||||
|
@ -18,6 +18,7 @@ import net.corda.core.crypto.CompositeKey
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.*
|
||||||
|
import net.corda.testing.internal.createWireTransaction
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -63,7 +64,7 @@ class TransactionTests {
|
|||||||
val cpub = ck.public
|
val cpub = ck.public
|
||||||
val c1 = CompositeKey.Builder().addKeys(apub, bpub).build(2)
|
val c1 = CompositeKey.Builder().addKeys(apub, bpub).build(2)
|
||||||
val compKey = CompositeKey.Builder().addKeys(c1, cpub).build(1)
|
val compKey = CompositeKey.Builder().addKeys(c1, cpub).build(1)
|
||||||
val wtx = WireTransaction(
|
val wtx = createWireTransaction(
|
||||||
inputs = listOf(StateRef(SecureHash.randomSHA256(), 0)),
|
inputs = listOf(StateRef(SecureHash.randomSHA256(), 0)),
|
||||||
attachments = emptyList(),
|
attachments = emptyList(),
|
||||||
outputs = emptyList(),
|
outputs = emptyList(),
|
||||||
@ -89,7 +90,7 @@ class TransactionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `signed transaction missing signatures`() {
|
fun `signed transaction missing signatures`() {
|
||||||
val wtx = WireTransaction(
|
val wtx = createWireTransaction(
|
||||||
inputs = listOf(StateRef(SecureHash.randomSHA256(), 0)),
|
inputs = listOf(StateRef(SecureHash.randomSHA256(), 0)),
|
||||||
attachments = emptyList(),
|
attachments = emptyList(),
|
||||||
outputs = emptyList(),
|
outputs = emptyList(),
|
||||||
@ -129,8 +130,8 @@ class TransactionTests {
|
|||||||
}, DummyContract.PROGRAM_ID))
|
}, DummyContract.PROGRAM_ID))
|
||||||
val id = SecureHash.randomSHA256()
|
val id = SecureHash.randomSHA256()
|
||||||
val timeWindow: TimeWindow? = null
|
val timeWindow: TimeWindow? = null
|
||||||
val privacySalt: PrivacySalt = PrivacySalt()
|
val privacySalt = PrivacySalt()
|
||||||
val transaction: LedgerTransaction = LedgerTransaction(
|
val transaction = LedgerTransaction(
|
||||||
inputs,
|
inputs,
|
||||||
outputs,
|
outputs,
|
||||||
commands,
|
commands,
|
||||||
@ -147,7 +148,7 @@ class TransactionTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `transaction cannot have duplicate inputs`() {
|
fun `transaction cannot have duplicate inputs`() {
|
||||||
val stateRef = StateRef(SecureHash.randomSHA256(), 0)
|
val stateRef = StateRef(SecureHash.randomSHA256(), 0)
|
||||||
fun buildTransaction() = WireTransaction(
|
fun buildTransaction() = createWireTransaction(
|
||||||
inputs = listOf(stateRef, stateRef),
|
inputs = listOf(stateRef, stateRef),
|
||||||
attachments = emptyList(),
|
attachments = emptyList(),
|
||||||
outputs = emptyList(),
|
outputs = emptyList(),
|
||||||
@ -170,7 +171,7 @@ class TransactionTests {
|
|||||||
val attachments = emptyList<Attachment>()
|
val attachments = emptyList<Attachment>()
|
||||||
val id = SecureHash.randomSHA256()
|
val id = SecureHash.randomSHA256()
|
||||||
val timeWindow: TimeWindow? = null
|
val timeWindow: TimeWindow? = null
|
||||||
val privacySalt: PrivacySalt = PrivacySalt()
|
val privacySalt = PrivacySalt()
|
||||||
fun buildTransaction() = LedgerTransaction(
|
fun buildTransaction() = LedgerTransaction(
|
||||||
inputs,
|
inputs,
|
||||||
outputs,
|
outputs,
|
||||||
@ -188,7 +189,7 @@ class TransactionTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `transactions with identical contents must have different ids`() {
|
fun `transactions with identical contents must have different ids`() {
|
||||||
val outputState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DummyContract.PROGRAM_ID, DUMMY_NOTARY)
|
val outputState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DummyContract.PROGRAM_ID, DUMMY_NOTARY)
|
||||||
fun buildTransaction() = WireTransaction(
|
fun buildTransaction() = createWireTransaction(
|
||||||
inputs = emptyList(),
|
inputs = emptyList(),
|
||||||
attachments = emptyList(),
|
attachments = emptyList(),
|
||||||
outputs = listOf(outputState),
|
outputs = listOf(outputState),
|
||||||
|
@ -136,7 +136,7 @@ class IntegrationTestingTutorial : IntegrationTest() {
|
|||||||
// move to Bob
|
// move to Bob
|
||||||
parallel(
|
parallel(
|
||||||
(1..numberOfStates).map { i ->
|
(1..numberOfStates).map { i ->
|
||||||
expect(match = { it.moved() == i * 100 }) { update: Vault.Update<Cash.State> ->
|
expect(match = { it.moved() == i * 100 }) { _: Vault.Update<Cash.State> ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -154,7 +154,7 @@ class IntegrationTestingTutorial : IntegrationTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Vault.Update<Cash.State>.moved(): Int {
|
private fun Vault.Update<Cash.State>.moved(): Int {
|
||||||
val consumedSum = consumed.sumBy { it.state.data.amount.quantity.toInt() }
|
val consumedSum = consumed.sumBy { it.state.data.amount.quantity.toInt() }
|
||||||
val producedSum = produced.sumBy { it.state.data.amount.quantity.toInt() }
|
val producedSum = produced.sumBy { it.state.data.amount.quantity.toInt() }
|
||||||
return consumedSum - producedSum
|
return consumedSum - producedSum
|
||||||
|
@ -40,6 +40,7 @@ import java.time.Duration;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
@ -134,7 +135,7 @@ public class FlowCookbookJava {
|
|||||||
// We retrieve a notary from the network map.
|
// We retrieve a notary from the network map.
|
||||||
// DOCSTART 01
|
// DOCSTART 01
|
||||||
CordaX500Name notaryName = new CordaX500Name("Notary Service", "London", "GB");
|
CordaX500Name notaryName = new CordaX500Name("Notary Service", "London", "GB");
|
||||||
Party specificNotary = getServiceHub().getNetworkMapCache().getNotary(notaryName);
|
Party specificNotary = Objects.requireNonNull(getServiceHub().getNetworkMapCache().getNotary(notaryName));
|
||||||
// Alternatively, we can pick an arbitrary notary from the notary
|
// Alternatively, we can pick an arbitrary notary from the notary
|
||||||
// list. However, it is always preferable to specify the notary
|
// list. However, it is always preferable to specify the notary
|
||||||
// explicitly, as the notary list might change when new notaries are
|
// explicitly, as the notary list might change when new notaries are
|
||||||
@ -378,7 +379,7 @@ public class FlowCookbookJava {
|
|||||||
// Or we can add the output state as a ``TransactionState``, which already specifies
|
// Or we can add the output state as a ``TransactionState``, which already specifies
|
||||||
// the output's contract and notary.
|
// the output's contract and notary.
|
||||||
// DOCSTART 51
|
// DOCSTART 51
|
||||||
TransactionState txState = new TransactionState(ourOutputState, DummyContract.PROGRAM_ID, specificNotary);
|
TransactionState txState = new TransactionState<>(ourOutputState, DummyContract.PROGRAM_ID, specificNotary);
|
||||||
// DOCEND 51
|
// DOCEND 51
|
||||||
|
|
||||||
// Commands can be added as ``Command``s.
|
// Commands can be added as ``Command``s.
|
||||||
@ -662,7 +663,7 @@ public class FlowCookbookJava {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void checkTransaction(SignedTransaction stx) {
|
protected void checkTransaction(@NotNull SignedTransaction stx) {
|
||||||
requireThat(require -> {
|
requireThat(require -> {
|
||||||
// Any additional checking we see fit...
|
// Any additional checking we see fit...
|
||||||
DummyState outputState = (DummyState) stx.getTx().getOutputs().get(0).getData();
|
DummyState outputState = (DummyState) stx.getTx().getOutputs().get(0).getData();
|
||||||
|
@ -13,6 +13,7 @@ package net.corda.docs.java.tutorial.contract;
|
|||||||
import net.corda.core.contracts.*;
|
import net.corda.core.contracts.*;
|
||||||
import net.corda.core.transactions.LedgerTransaction;
|
import net.corda.core.transactions.LedgerTransaction;
|
||||||
import net.corda.core.transactions.LedgerTransaction.InOutGroup;
|
import net.corda.core.transactions.LedgerTransaction.InOutGroup;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Currency;
|
import java.util.Currency;
|
||||||
@ -22,6 +23,7 @@ import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
|
|||||||
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
||||||
import static net.corda.finance.utils.StateSumming.sumCashBy;
|
import static net.corda.finance.utils.StateSumming.sumCashBy;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class CommercialPaper implements Contract {
|
public class CommercialPaper implements Contract {
|
||||||
// DOCSTART 1
|
// DOCSTART 1
|
||||||
public static final String IOU_CONTRACT_ID = "com.example.contract.IOUContract";
|
public static final String IOU_CONTRACT_ID = "com.example.contract.IOUContract";
|
||||||
@ -29,7 +31,7 @@ public class CommercialPaper implements Contract {
|
|||||||
|
|
||||||
// DOCSTART 3
|
// DOCSTART 3
|
||||||
@Override
|
@Override
|
||||||
public void verify(LedgerTransaction tx) {
|
public void verify(@NotNull LedgerTransaction tx) {
|
||||||
List<InOutGroup<State, State>> groups = tx.groupStates(State.class, State::withoutOwner);
|
List<InOutGroup<State, State>> groups = tx.groupStates(State.class, State::withoutOwner);
|
||||||
CommandWithParties<Commands> cmd = requireSingleCommand(tx.getCommands(), Commands.class);
|
CommandWithParties<Commands> cmd = requireSingleCommand(tx.getCommands(), Commands.class);
|
||||||
// DOCEND 3
|
// DOCEND 3
|
||||||
@ -37,7 +39,7 @@ public class CommercialPaper implements Contract {
|
|||||||
// DOCSTART 4
|
// DOCSTART 4
|
||||||
TimeWindow timeWindow = tx.getTimeWindow();
|
TimeWindow timeWindow = tx.getTimeWindow();
|
||||||
|
|
||||||
for (InOutGroup group : groups) {
|
for (InOutGroup<State, State> group : groups) {
|
||||||
List<State> inputs = group.getInputs();
|
List<State> inputs = group.getInputs();
|
||||||
List<State> outputs = group.getOutputs();
|
List<State> outputs = group.getOutputs();
|
||||||
|
|
||||||
@ -57,6 +59,7 @@ public class CommercialPaper implements Contract {
|
|||||||
Amount<Issued<Currency>> received = sumCashBy(tx.getOutputStates(), input.getOwner());
|
Amount<Issued<Currency>> received = sumCashBy(tx.getOutputStates(), input.getOwner());
|
||||||
if (timeWindow == null) throw new IllegalArgumentException("Redemptions must be timestamped");
|
if (timeWindow == null) throw new IllegalArgumentException("Redemptions must be timestamped");
|
||||||
Instant time = timeWindow.getFromTime();
|
Instant time = timeWindow.getFromTime();
|
||||||
|
if (time == null) throw new IllegalArgumentException("Redemptions must have a from time");
|
||||||
requireThat(require -> {
|
requireThat(require -> {
|
||||||
require.using("the paper must have matured", time.isAfter(input.getMaturityDate()));
|
require.using("the paper must have matured", time.isAfter(input.getMaturityDate()));
|
||||||
require.using("the received amount equals the face value", received == input.getFaceValue());
|
require.using("the received amount equals the face value", received == input.getFaceValue());
|
||||||
@ -68,6 +71,7 @@ public class CommercialPaper implements Contract {
|
|||||||
State output = outputs.get(0);
|
State output = outputs.get(0);
|
||||||
if (timeWindow == null) throw new IllegalArgumentException("Issuances must have a time-window");
|
if (timeWindow == null) throw new IllegalArgumentException("Issuances must have a time-window");
|
||||||
Instant time = timeWindow.getUntilTime();
|
Instant time = timeWindow.getUntilTime();
|
||||||
|
if (time == null) throw new IllegalArgumentException("Issuances must have an until time");
|
||||||
requireThat(require -> {
|
requireThat(require -> {
|
||||||
// Don't allow people to issue commercial paper under other entities identities.
|
// Don't allow people to issue commercial paper under other entities identities.
|
||||||
require.using("output states are issued by a command signer", cmd.getSigners().contains(output.getIssuance().getParty().getOwningKey()));
|
require.using("output states are issued by a command signer", cmd.getSigners().contains(output.getIssuance().getParty().getOwningKey()));
|
||||||
|
@ -50,6 +50,7 @@ enum class PrintOrVisualise {
|
|||||||
Visualise
|
Visualise
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
require(args.isNotEmpty()) { "Usage: <binary> [Print|Visualise]" }
|
require(args.isNotEmpty()) { "Usage: <binary> [Print|Visualise]" }
|
||||||
val printOrVisualise = PrintOrVisualise.valueOf(args[0])
|
val printOrVisualise = PrintOrVisualise.valueOf(args[0])
|
||||||
@ -109,6 +110,7 @@ fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// END 5
|
// END 5
|
||||||
|
Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:Suppress("UNUSED_VARIABLE", "unused")
|
@file:Suppress("UNUSED_VARIABLE", "unused", "DEPRECATION")
|
||||||
|
|
||||||
package net.corda.docs
|
package net.corda.docs
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("UNUSED_VARIABLE")
|
||||||
|
|
||||||
package net.corda.docs.tutorial.tearoffs
|
package net.corda.docs.tutorial.tearoffs
|
||||||
|
|
||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
@ -52,4 +54,4 @@ fun main(args: Array<String>) {
|
|||||||
} catch (e: FilteredTransactionVerificationException) {
|
} catch (e: FilteredTransactionVerificationException) {
|
||||||
throw MerkleTreeException("Rate Fix Oracle: Couldn't verify partial Merkle tree.")
|
throw MerkleTreeException("Rate Fix Oracle: Couldn't verify partial Merkle tree.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,27 @@ Optionally run the node's webserver as well by opening a terminal window in the
|
|||||||
|
|
||||||
.. warning:: The node webserver is for testing purposes only and will be removed soon.
|
.. warning:: The node webserver is for testing purposes only and will be removed soon.
|
||||||
|
|
||||||
|
Command-line options
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
The node can optionally be started with the following command-line options:
|
||||||
|
|
||||||
|
* ``--base-directory``: The node working directory where all the files are kept (default: ``.``)
|
||||||
|
* ``--bootstrap-raft-cluster``: Bootstraps Raft cluster. The node forms a single node cluster (ignoring otherwise configured peer
|
||||||
|
addresses), acting as a seed for other nodes to join the cluster
|
||||||
|
* ``--config-file``: The path to the config file (default: ``node.conf``)
|
||||||
|
* ``--help``
|
||||||
|
* ``--initial-registration``: Start initial node registration with Corda network to obtain certificate from the permissioning
|
||||||
|
server
|
||||||
|
* ``--just-generate-node-info``: Perform the node start-up task necessary to generate its nodeInfo, save it to disk, then
|
||||||
|
quit
|
||||||
|
* ``--log-to-console``: If set, prints logging to the console as well as to a file
|
||||||
|
* ``--logging-level <[ERROR,WARN,INFO, DEBUG,TRACE]>``: Enable logging at this level and higher (default: INFO)
|
||||||
|
* ``--network-root-truststore``: Network root trust store obtained from network operator
|
||||||
|
* ``--network-root-truststore-password``: Network root trust store password obtained from network operator
|
||||||
|
* ``--no-local-shell``: Do not start the embedded shell locally
|
||||||
|
* ``--sshd``: Enables SSHD server for node administration
|
||||||
|
* ``--version``: Print the version and exit
|
||||||
|
|
||||||
Enabling remote debugging
|
Enabling remote debugging
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
To enable remote debugging of the node, run the following from the terminal window:
|
To enable remote debugging of the node, run the following from the terminal window:
|
||||||
@ -61,8 +82,11 @@ This command line will start the node with JMX metrics accessible via HTTP on po
|
|||||||
|
|
||||||
See :ref:`Monitoring your node <jolokia_ref>` for further details.
|
See :ref:`Monitoring your node <jolokia_ref>` for further details.
|
||||||
|
|
||||||
Starting all nodes at once from the command line (native)
|
Starting all nodes at once from the command line
|
||||||
---------------------------------------------------------
|
------------------------------------------------
|
||||||
|
|
||||||
|
Native
|
||||||
|
~~~~~~
|
||||||
If you created your nodes using ``deployNodes``, a ``runnodes`` shell script (or batch file on Windows) will have been
|
If you created your nodes using ``deployNodes``, a ``runnodes`` shell script (or batch file on Windows) will have been
|
||||||
generated to allow you to quickly start up all nodes and their webservers. ``runnodes`` should only be used for testing
|
generated to allow you to quickly start up all nodes and their webservers. ``runnodes`` should only be used for testing
|
||||||
purposes.
|
purposes.
|
||||||
@ -79,8 +103,8 @@ If you receive an ``OutOfMemoryError`` exception when interacting with the nodes
|
|||||||
Java heap memory available to them, which you can do when running them individually. See
|
Java heap memory available to them, which you can do when running them individually. See
|
||||||
:ref:`starting-an-individual-corda-node`.
|
:ref:`starting-an-individual-corda-node`.
|
||||||
|
|
||||||
Starting all nodes at once from the command line (docker-compose)
|
docker-compose
|
||||||
-----------------------------------------------------------------
|
~~~~~~~~~~~~~~
|
||||||
If you created your nodes using ``Dockerform``, the ``docker-compose.yml`` file and corresponding ``Dockerfile`` for
|
If you created your nodes using ``Dockerform``, the ``docker-compose.yml`` file and corresponding ``Dockerfile`` for
|
||||||
nodes has been created and configured appropriately. Navigate to ``build/nodes`` directory and run ``docker-compose up``
|
nodes has been created and configured appropriately. Navigate to ``build/nodes`` directory and run ``docker-compose up``
|
||||||
command. This will startup nodes inside new, internal network.
|
command. This will startup nodes inside new, internal network.
|
||||||
|
@ -411,32 +411,38 @@ For more information on the client RPC interface and how to build an RPC client
|
|||||||
|
|
||||||
Running nodes across machines
|
Running nodes across machines
|
||||||
-----------------------------
|
-----------------------------
|
||||||
The nodes can be split across different machines and configured to communicate across the network.
|
The nodes can be configured to communicate as a network even when distributed across several machines:
|
||||||
|
|
||||||
After deploying the nodes, navigate to the build folder (``kotlin-source/build/nodes``) and for each node that needs to
|
* Deploy the nodes as usual:
|
||||||
be moved to another machine open its config file and change the Artemis messaging address to the IP address of the machine
|
|
||||||
where the node will run (e.g. ``p2pAddress="10.18.0.166:10007"``).
|
|
||||||
|
|
||||||
These changes require new node-info files to be distributed amongst the nodes. Use the network bootstrapper tool
|
* Unix/Mac OSX: ``./gradlew deployNodes``
|
||||||
(see :doc:`network-bootstrapper` for more information on this and how to built it) to update the files and have
|
* Windows: ``gradlew.bat deployNodes``
|
||||||
them distributed locally.
|
|
||||||
|
|
||||||
``java -jar network-bootstrapper.jar kotlin-source/build/nodes``
|
* Navigate to the build folder (``kotlin-source/build/nodes``)
|
||||||
|
* For each node, open its ``node.conf`` file and change ``localhost`` in its ``p2pAddress`` to the IP address of the machine
|
||||||
|
where the node will be run (e.g. ``p2pAddress="10.18.0.166:10007"``)
|
||||||
|
* These changes require new node-info files to be distributed amongst the nodes. Use the network bootstrapper tool
|
||||||
|
(see :doc:`network-bootstrapper`) to update the files and have them distributed locally:
|
||||||
|
|
||||||
Once that's done move the node folders to their designated machines (e.g. using a USB key). It is important that none of the
|
``java -jar network-bootstrapper.jar kotlin-source/build/nodes``
|
||||||
nodes - including the notary - end up on more than one machine. Each computer should also have a copy of ``runnodes``
|
|
||||||
and ``runnodes.bat``.
|
|
||||||
|
|
||||||
For example, you may end up with the following layout:
|
* Move the node folders to their individual machines (e.g. using a USB key). It is important that none of the
|
||||||
|
nodes - including the notary - end up on more than one machine. Each computer should also have a copy of ``runnodes``
|
||||||
|
and ``runnodes.bat``.
|
||||||
|
|
||||||
* Machine 1: ``Notary``, ``PartyA``, ``runnodes``, ``runnodes.bat``
|
For example, you may end up with the following layout:
|
||||||
* Machine 2: ``PartyB``, ``PartyC``, ``runnodes``, ``runnodes.bat``
|
|
||||||
|
|
||||||
After starting each node, the nodes will be able to see one another and agree IOUs among themselves.
|
* Machine 1: ``Notary``, ``PartyA``, ``runnodes``, ``runnodes.bat``
|
||||||
|
* Machine 2: ``PartyB``, ``PartyC``, ``runnodes``, ``runnodes.bat``
|
||||||
|
|
||||||
.. note:: If you are using H2 and wish to use the same ``h2port`` value for all the nodes, then only assign them that
|
* After starting each node, the nodes will be able to see one another and agree IOUs among themselves
|
||||||
value after the nodes have been moved to their machines. The initial bootstrapping process requires access to the nodes'
|
|
||||||
databases and if they share the same H2 port then the process will fail.
|
.. warning:: The bootstrapper must be run **after** the ``node.conf`` files have been modified, but **before** the nodes
|
||||||
|
are distributed across machines. Otherwise, the nodes will not be able to communicate.
|
||||||
|
|
||||||
|
.. note:: If you are using H2 and wish to use the same ``h2port`` value for two or more nodes, you must only assign them that
|
||||||
|
value after the nodes have been moved to their individual machines. The initial bootstrapping process requires access to the
|
||||||
|
nodes' databases and if two nodes share the same H2 port, the process will fail.
|
||||||
|
|
||||||
Testing and debugging
|
Testing and debugging
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -1,41 +1,23 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.2.40'
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "com.github.jengelman.gradle.plugins:shadow:$shadow_version"
|
||||||
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.3'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
apply plugin: 'kotlin'
|
||||||
id "org.jetbrains.kotlin.jvm"
|
apply plugin: 'application'
|
||||||
id 'com.github.johnrengelman.shadow' version '2.0.3'
|
apply plugin: 'com.github.johnrengelman.shadow'
|
||||||
id 'java'
|
|
||||||
id 'application'
|
|
||||||
}
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
compile group: 'info.picocli', name: 'picocli', version: '3.0.1'
|
compile "info.picocli:picocli:3.0.1"
|
||||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
testCompile "junit:junit:$junit_version"
|
||||||
}
|
|
||||||
compileKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
compileTestKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mainClassName = "net.corda.avalanche.MainKt"
|
mainClassName = "net.corda.avalanche.MainKt"
|
||||||
shadowJar {
|
shadowJar {
|
||||||
baseName = "avalanche"
|
baseName = "avalanche"
|
||||||
|
@ -8,37 +8,18 @@
|
|||||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
buildscript {
|
ext {
|
||||||
ext.commonsio_version = '2.6'
|
commonsio_version = '2.6'
|
||||||
ext.commonslogging_version = '1.2'
|
cucumber_version = '1.2.5'
|
||||||
ext.cucumber_version = '1.2.5'
|
crash_version = 'cce5a00f114343c1145c1d7756e1dd6df3ea984e'
|
||||||
ext.crash_version = 'cce5a00f114343c1145c1d7756e1dd6df3ea984e'
|
docker_client_version = '8.11.0'
|
||||||
ext.docker_client_version = '8.11.0'
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
jcenter()
|
|
||||||
url 'https://jitpack.io'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group 'net.corda.behave'
|
group 'net.corda.behave'
|
||||||
|
|
||||||
apply plugin: 'java'
|
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
apply plugin: 'net.corda.plugins.publish-utils'
|
||||||
|
|
||||||
sourceCompatibility = 1.8
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
behave {
|
behave {
|
||||||
kotlin {
|
kotlin {
|
||||||
@ -89,6 +70,7 @@ dependencies {
|
|||||||
|
|
||||||
compile "org.slf4j:log4j-over-slf4j:$slf4j_version"
|
compile "org.slf4j:log4j-over-slf4j:$slf4j_version"
|
||||||
compile "org.slf4j:jul-to-slf4j:$slf4j_version"
|
compile "org.slf4j:jul-to-slf4j:$slf4j_version"
|
||||||
|
compile "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
||||||
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
||||||
compile "org.apache.logging.log4j:log4j-core:$log4j_version"
|
compile "org.apache.logging.log4j:log4j-core:$log4j_version"
|
||||||
|
|
||||||
@ -96,10 +78,9 @@ dependencies {
|
|||||||
compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version"
|
compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version"
|
||||||
|
|
||||||
// FastClasspathScanner: classpath scanning
|
// FastClasspathScanner: classpath scanning
|
||||||
compile 'io.github.lukehutch:fast-classpath-scanner:2.12.3'
|
compile "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version"
|
||||||
|
|
||||||
compile "commons-io:commons-io:$commonsio_version"
|
compile "commons-io:commons-io:$commonsio_version"
|
||||||
compile "commons-logging:commons-logging:$commonslogging_version"
|
|
||||||
compile "com.spotify:docker-client:$docker_client_version"
|
compile "com.spotify:docker-client:$docker_client_version"
|
||||||
compile "io.reactivex:rxjava:$rxjava_version"
|
compile "io.reactivex:rxjava:$rxjava_version"
|
||||||
|
|
||||||
@ -122,14 +103,6 @@ dependencies {
|
|||||||
behaveCompile "info.cukes:cucumber-picocontainer:$cucumber_version"
|
behaveCompile "info.cukes:cucumber-picocontainer:$cucumber_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
|
|
||||||
compileTestKotlin {
|
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
|
|
||||||
test {
|
test {
|
||||||
testLogging.showStandardStreams = true
|
testLogging.showStandardStreams = true
|
||||||
}
|
}
|
||||||
|
@ -11,19 +11,19 @@
|
|||||||
package net.corda.behave.network
|
package net.corda.behave.network
|
||||||
|
|
||||||
import net.corda.behave.database.DatabaseType
|
import net.corda.behave.database.DatabaseType
|
||||||
import net.corda.behave.file.*
|
import net.corda.behave.file.LogSource
|
||||||
import net.corda.behave.monitoring.PatternWatch
|
import net.corda.behave.file.currentDirectory
|
||||||
|
import net.corda.behave.file.stagingRoot
|
||||||
|
import net.corda.behave.file.tmpDirectory
|
||||||
import net.corda.behave.node.Distribution
|
import net.corda.behave.node.Distribution
|
||||||
import net.corda.behave.node.Node
|
import net.corda.behave.node.Node
|
||||||
import net.corda.behave.node.configuration.NotaryType
|
import net.corda.behave.node.configuration.NotaryType
|
||||||
import net.corda.behave.process.Command
|
import net.corda.behave.process.Command
|
||||||
import net.corda.behave.process.JarCommand
|
import net.corda.behave.process.JarCommand
|
||||||
import net.corda.core.CordaException
|
import net.corda.core.CordaException
|
||||||
import net.corda.core.CordaRuntimeException
|
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.minutes
|
import net.corda.core.utilities.minutes
|
||||||
import net.corda.core.utilities.seconds
|
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -147,116 +147,8 @@ class Network private constructor(
|
|||||||
* using Local signing and "Auto Approval" mode
|
* using Local signing and "Auto Approval" mode
|
||||||
*/
|
*/
|
||||||
private fun bootstrapDoorman() {
|
private fun bootstrapDoorman() {
|
||||||
|
|
||||||
// TODO: rework how we use the Doorman/NMS (now these are a separate product / distribution)
|
// TODO: rework how we use the Doorman/NMS (now these are a separate product / distribution)
|
||||||
signalFailure("Bootstrapping a Corda Enterprise network using the Doorman is no longer supported; exiting ...")
|
signalFailure("Bootstrapping a Corda Enterprise network using the Doorman is no longer supported; exiting ...")
|
||||||
return
|
|
||||||
|
|
||||||
// WARNING!! Need to use the correct bootstrapper
|
|
||||||
// only if using OS nodes (need to choose the latest version)
|
|
||||||
val r3node = nodes.values
|
|
||||||
.find { it.config.distribution.type == Distribution.Type.CORDA_ENTERPRISE } ?: throw CordaRuntimeException("Missing R3 distribution node")
|
|
||||||
val distribution = r3node.config.distribution
|
|
||||||
|
|
||||||
// Copy over reference configuration files used in bootstrapping
|
|
||||||
val source = doormanConfigDirectory
|
|
||||||
val doormanTargetDirectory = targetDirectory / "doorman"
|
|
||||||
source.toFile().copyRecursively(doormanTargetDirectory.toFile(), true)
|
|
||||||
|
|
||||||
// Use master version of Bootstrapper
|
|
||||||
val doormanJar = Distribution.R3_MASTER.doormanJar
|
|
||||||
log.info("DoormanJar URL: $doormanJar\n")
|
|
||||||
|
|
||||||
// 1. Create key stores for local signer
|
|
||||||
|
|
||||||
// java -jar doorman-<version>.jar --mode ROOT_KEYGEN
|
|
||||||
log.info("Doorman target directory: $doormanTargetDirectory")
|
|
||||||
runCommand(JarCommand(doormanJar,
|
|
||||||
arrayOf("--config-file", "$doormanConfigDirectory/node-init.conf", "--mode", "ROOT_KEYGEN", "--trust-store-password", "password"),
|
|
||||||
doormanTargetDirectory, timeout))
|
|
||||||
|
|
||||||
// java -jar doorman-<version>.jar --mode CA_KEYGEN
|
|
||||||
runCommand(JarCommand(doormanJar,
|
|
||||||
arrayOf("--config-file", "$doormanConfigDirectory/node-init.conf", "--mode", "CA_KEYGEN"),
|
|
||||||
doormanTargetDirectory, timeout))
|
|
||||||
|
|
||||||
// 2. Start the doorman service for notary registration
|
|
||||||
doormanNMS = JarCommand(doormanJar,
|
|
||||||
arrayOf("--config-file", "$doormanConfigDirectory/node-init.conf"),
|
|
||||||
doormanTargetDirectory, timeout)
|
|
||||||
|
|
||||||
val doormanCommand = runCommand(doormanNMS, noWait = true)
|
|
||||||
log.info("Waiting for DoormanNMS to be alive")
|
|
||||||
|
|
||||||
PatternWatch(doormanCommand.output, "Network management web services started on").await(30.seconds)
|
|
||||||
log.info("DoormanNMS up and running")
|
|
||||||
|
|
||||||
// Notary Nodes
|
|
||||||
val notaryNodes = nodes.values.filter { it.config.notary.notaryType != NotaryType.NONE }
|
|
||||||
notaryNodes.forEach { notaryNode ->
|
|
||||||
val notaryTargetDirectory = targetDirectory / notaryNode.config.name
|
|
||||||
log.info("Notary target directory: $notaryTargetDirectory")
|
|
||||||
|
|
||||||
// 3. Create notary node and register with the doorman
|
|
||||||
runCommand(JarCommand(distribution.cordaJar,
|
|
||||||
arrayOf("--initial-registration",
|
|
||||||
"--base-directory", "$notaryTargetDirectory",
|
|
||||||
"--network-root-truststore", "../doorman/certificates/distribute-nodes/network-root-truststore.jks",
|
|
||||||
"--network-root-truststore-password", "password"),
|
|
||||||
notaryTargetDirectory, timeout))
|
|
||||||
|
|
||||||
// 4. Generate node info files for notary nodes
|
|
||||||
runCommand(JarCommand(distribution.cordaJar,
|
|
||||||
arrayOf("--just-generate-node-info",
|
|
||||||
"--base-directory", "$notaryTargetDirectory"),
|
|
||||||
notaryTargetDirectory, timeout))
|
|
||||||
|
|
||||||
// cp (or ln -s) nodeInfo* notary-node-info
|
|
||||||
val nodeInfoFile = notaryTargetDirectory.toFile().listFiles { _, filename -> filename.matches("nodeInfo-.+".toRegex()) }.firstOrNull() ?: throw CordaRuntimeException("Missing notary nodeInfo file")
|
|
||||||
|
|
||||||
Files.copy(nodeInfoFile.toPath(), (notaryTargetDirectory / "notary-node-info"), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING)
|
|
||||||
}
|
|
||||||
|
|
||||||
// exit Doorman process
|
|
||||||
doormanCommand.interrupt()
|
|
||||||
doormanCommand.waitFor()
|
|
||||||
|
|
||||||
// 5. Add notary identities to the network parameters
|
|
||||||
|
|
||||||
// 6. Load initial network parameters file for network map service
|
|
||||||
val networkParamsConfig = if (notaryNodes.isEmpty()) "network-parameters-without-notary.conf" else "network-parameters.conf"
|
|
||||||
val updateNetworkParams = JarCommand(doormanJar,
|
|
||||||
arrayOf("--config-file", "$doormanTargetDirectory/node.conf", "--set-network-parameters", "$doormanTargetDirectory/$networkParamsConfig"),
|
|
||||||
doormanTargetDirectory, timeout)
|
|
||||||
runCommand(updateNetworkParams)
|
|
||||||
|
|
||||||
// 7. Start a fully configured Doorman / NMS
|
|
||||||
doormanNMS = JarCommand(doormanJar,
|
|
||||||
arrayOf("--config-file", "$doormanConfigDirectory/node.conf"),
|
|
||||||
doormanTargetDirectory, timeout)
|
|
||||||
|
|
||||||
val doormanNMSCommand = runCommand(doormanNMS, noWait = true)
|
|
||||||
log.info("Waiting for DoormanNMS to be alive")
|
|
||||||
|
|
||||||
PatternWatch(doormanNMSCommand.output, "Network management web services started on").await(30.seconds)
|
|
||||||
log.info("DoormanNMS up and running")
|
|
||||||
|
|
||||||
// 8. Register other participant nodes
|
|
||||||
val partyNodes = nodes.values.filter { it.config.notary.notaryType == NotaryType.NONE }
|
|
||||||
partyNodes.forEach { partyNode ->
|
|
||||||
val partyTargetDirectory = targetDirectory / partyNode.config.name
|
|
||||||
log.info("Party target directory: $partyTargetDirectory")
|
|
||||||
|
|
||||||
// 3. Create notary node and register with the doorman
|
|
||||||
runCommand(JarCommand(distribution.cordaJar,
|
|
||||||
arrayOf("--initial-registration",
|
|
||||||
"--network-root-truststore", "../doorman/certificates/distribute-nodes/network-root-truststore.jks",
|
|
||||||
"--network-root-truststore-password", "password",
|
|
||||||
"--base-directory", "$partyTargetDirectory"),
|
|
||||||
partyTargetDirectory, timeout))
|
|
||||||
}
|
|
||||||
|
|
||||||
isDoormanNMSRunning = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun runCommand(command: Command, noWait: Boolean = false): Command {
|
private fun runCommand(command: Command, noWait: Boolean = false): Command {
|
||||||
@ -446,7 +338,7 @@ class Network private constructor(
|
|||||||
val rpcProxyPortNo = node.config.nodeInterface.rpcProxy
|
val rpcProxyPortNo = node.config.nodeInterface.rpcProxy
|
||||||
val pid = Files.lines(tmpDirectory / "rpcProxy-pid-$rpcProxyPortNo").findFirst().get()
|
val pid = Files.lines(tmpDirectory / "rpcProxy-pid-$rpcProxyPortNo").findFirst().get()
|
||||||
// TODO: consider generic implementation to support non *nix platforms
|
// TODO: consider generic implementation to support non *nix platforms
|
||||||
Command(listOf("kill", "-9", "$pid")).run()
|
Command(listOf("kill", "-9", pid)).run()
|
||||||
(tmpDirectory / "rpcProxy-pid-$rpcProxyPortNo").deleteIfExists()
|
(tmpDirectory / "rpcProxy-pid-$rpcProxyPortNo").deleteIfExists()
|
||||||
}
|
}
|
||||||
catch (e: Exception) {
|
catch (e: Exception) {
|
||||||
|
@ -14,7 +14,6 @@ import net.corda.behave.database.DatabaseConnection
|
|||||||
import net.corda.behave.database.DatabaseType
|
import net.corda.behave.database.DatabaseType
|
||||||
import net.corda.behave.file.LogSource
|
import net.corda.behave.file.LogSource
|
||||||
import net.corda.behave.file.currentDirectory
|
import net.corda.behave.file.currentDirectory
|
||||||
import net.corda.behave.file.stagingRoot
|
|
||||||
import net.corda.behave.monitoring.PatternWatch
|
import net.corda.behave.monitoring.PatternWatch
|
||||||
import net.corda.behave.node.configuration.*
|
import net.corda.behave.node.configuration.*
|
||||||
import net.corda.behave.process.JarCommand
|
import net.corda.behave.process.JarCommand
|
||||||
@ -31,8 +30,8 @@ import net.corda.core.internal.div
|
|||||||
import net.corda.core.internal.exists
|
import net.corda.core.internal.exists
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.minutes
|
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import net.corda.core.utilities.minutes
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
@ -45,7 +44,7 @@ import java.util.concurrent.CountDownLatch
|
|||||||
*/
|
*/
|
||||||
class Node(
|
class Node(
|
||||||
val config: Configuration,
|
val config: Configuration,
|
||||||
val rootDirectory: Path = currentDirectory,
|
private val rootDirectory: Path = currentDirectory,
|
||||||
private val settings: ServiceSettings = ServiceSettings(),
|
private val settings: ServiceSettings = ServiceSettings(),
|
||||||
val rpcProxy: Boolean = false,
|
val rpcProxy: Boolean = false,
|
||||||
val networkType: Distribution.Type
|
val networkType: Distribution.Type
|
||||||
@ -77,18 +76,6 @@ class Node(
|
|||||||
|
|
||||||
private var haveDependenciesStopped = false
|
private var haveDependenciesStopped = false
|
||||||
|
|
||||||
fun describe(): String {
|
|
||||||
val network = config.nodeInterface
|
|
||||||
val database = config.database
|
|
||||||
return """
|
|
||||||
|Node Information: ${config.name}
|
|
||||||
| - P2P: ${network.host}:${network.p2pPort}
|
|
||||||
| - RPC: ${network.host}:${network.rpcPort}
|
|
||||||
| - SSH: ${network.host}:${network.sshPort}
|
|
||||||
| - DB: ${network.host}:${database.port} (${database.type})
|
|
||||||
|""".trimMargin()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun configure() {
|
fun configure() {
|
||||||
if (isConfigured) { return }
|
if (isConfigured) { return }
|
||||||
isConfigured = true
|
isConfigured = true
|
||||||
@ -164,10 +151,6 @@ class Node(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val nodeInfoGenerationOutput: LogSource by lazy {
|
|
||||||
LogSource(logDirectory, "node-info-gen.log")
|
|
||||||
}
|
|
||||||
|
|
||||||
val logOutput: LogSource by lazy {
|
val logOutput: LogSource by lazy {
|
||||||
val hostname = InetAddress.getLocalHost().hostName
|
val hostname = InetAddress.getLocalHost().hostName
|
||||||
LogSource(logDirectory, "node-$hostname.*.log")
|
LogSource(logDirectory, "node-$hostname.*.log")
|
||||||
@ -387,10 +370,11 @@ class Node(
|
|||||||
val name = name ?: error("Node name not set")
|
val name = name ?: error("Node name not set")
|
||||||
val directory = directory ?: error("Runtime directory not set")
|
val directory = directory ?: error("Runtime directory not set")
|
||||||
// TODO: rework how we use the Doorman/NMS (now these are a separate product / distribution)
|
// TODO: rework how we use the Doorman/NMS (now these are a separate product / distribution)
|
||||||
val compatibilityZoneURL = null
|
val compatibilityZoneURL = if (networkType == Distribution.Type.CORDA_ENTERPRISE && System.getProperty("USE_NETWORK_SERVICES") != null) {
|
||||||
if (networkType == Distribution.Type.CORDA_ENTERPRISE && System.getProperty("USE_NETWORK_SERVICES") != null)
|
"http://localhost:1300" // TODO: add additional USE_NETWORK_SERVICES_URL to specify location of existing operational environment to use.
|
||||||
"http://localhost:1300" // TODO: add additional USE_NETWORK_SERVICES_URL to specify location of existing operational environment to use.
|
} else {
|
||||||
else null
|
null
|
||||||
|
}
|
||||||
return Node(
|
return Node(
|
||||||
Configuration(
|
Configuration(
|
||||||
name,
|
name,
|
||||||
|
@ -72,7 +72,7 @@ class ScenarioState {
|
|||||||
|
|
||||||
inline fun <T> withNetwork(action: ScenarioState.() -> T): T {
|
inline fun <T> withNetwork(action: ScenarioState.() -> T): T {
|
||||||
ensureNetworkIsRunning()
|
ensureNetworkIsRunning()
|
||||||
return action()
|
return this.action()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T> withClient(nodeName: String, crossinline action: (CordaRPCOps) -> T): T {
|
inline fun <T> withClient(nodeName: String, crossinline action: (CordaRPCOps) -> T): T {
|
||||||
|
@ -10,34 +10,33 @@
|
|||||||
|
|
||||||
package net.corda.behave.scenarios.helpers
|
package net.corda.behave.scenarios.helpers
|
||||||
|
|
||||||
import net.corda.behave.logging.getLogger
|
|
||||||
import net.corda.behave.scenarios.ScenarioState
|
import net.corda.behave.scenarios.ScenarioState
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
abstract class Substeps(protected val state: ScenarioState) {
|
abstract class Substeps(protected val state: ScenarioState) {
|
||||||
|
protected val log: Logger = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
protected val log = getLogger<Substeps>()
|
protected fun withNetwork(action: ScenarioState.() -> Unit) = state.withNetwork(action)
|
||||||
|
|
||||||
protected fun withNetwork(action: ScenarioState.() -> Unit) =
|
|
||||||
state.withNetwork(action)
|
|
||||||
|
|
||||||
protected fun <T> withClient(nodeName: String, action: ScenarioState.(CordaRPCOps) -> T): T {
|
protected fun <T> withClient(nodeName: String, action: ScenarioState.(CordaRPCOps) -> T): T {
|
||||||
return state.withClient(nodeName, {
|
return state.withClient(nodeName) {
|
||||||
return@withClient try {
|
try {
|
||||||
action(state, it)
|
action(state, it)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
state.error<T>(ex.message ?: "Failed to execute RPC call")
|
state.error(ex.message ?: "Failed to execute RPC call")
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun <T> withClientProxy(nodeName: String, action: ScenarioState.(CordaRPCOps) -> T): T {
|
protected fun <T> withClientProxy(nodeName: String, action: ScenarioState.(CordaRPCOps) -> T): T {
|
||||||
return state.withClientProxy(nodeName, {
|
return state.withClientProxy(nodeName) {
|
||||||
return@withClientProxy try {
|
try {
|
||||||
action(state, it)
|
action(state, it)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
state.error<T>(ex.message ?: "Failed to execute HTTP call")
|
state.error(ex.message ?: "Failed to execute HTTP call")
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import org.junit.Test
|
|||||||
import rx.observers.TestSubscriber
|
import rx.observers.TestSubscriber
|
||||||
|
|
||||||
class CommandTests {
|
class CommandTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `successful command returns zero`() {
|
fun `successful command returns zero`() {
|
||||||
val exitCode = Command(listOf("ls", "/")).run()
|
val exitCode = Command(listOf("ls", "/")).run()
|
||||||
|
@ -34,7 +34,7 @@ dependencies {
|
|||||||
compile project(':finance')
|
compile project(':finance')
|
||||||
|
|
||||||
// ObjectWeb Asm: a library for synthesising and working with JVM bytecode.
|
// ObjectWeb Asm: a library for synthesising and working with JVM bytecode.
|
||||||
compile "org.ow2.asm:asm:5.0.4"
|
compile "org.ow2.asm:asm:$asm_version"
|
||||||
|
|
||||||
compile "com.google.guava:guava:$guava_version"
|
compile "com.google.guava:guava:$guava_version"
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
apply plugin: 'kotlin-kapt'
|
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
|
|
||||||
description 'A javaagent to allow hooking into Kryo'
|
description 'A javaagent to allow hooking into Kryo'
|
||||||
|
@ -114,7 +114,6 @@ object FiberMonitor {
|
|||||||
thread {
|
thread {
|
||||||
while (true) {
|
while (true) {
|
||||||
Thread.sleep(1000)
|
Thread.sleep(1000)
|
||||||
this
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ package net.corda.flowhook
|
|||||||
|
|
||||||
import java.lang.instrument.Instrumentation
|
import java.lang.instrument.Instrumentation
|
||||||
|
|
||||||
@Suppress("UNUSED")
|
@Suppress("UNUSED", "UNUSED_PARAMETER")
|
||||||
class FlowHookAgent {
|
class FlowHookAgent {
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@ -22,4 +22,3 @@ class FlowHookAgent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import org.apache.activemq.artemis.core.io.buffer.TimedBuffer
|
|||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
@Suppress("UNUSED")
|
@Suppress("UNUSED", "UNUSED_PARAMETER")
|
||||||
object FlowHookContainer {
|
object FlowHookContainer {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -34,7 +34,6 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
apply plugin: 'kotlin-kapt'
|
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
apply plugin: 'net.corda.plugins.cordapp'
|
apply plugin: 'net.corda.plugins.cordapp'
|
||||||
|
|
||||||
|
@ -11,13 +11,11 @@ import java.util.concurrent.Callable
|
|||||||
|
|
||||||
// Responsible for executing test scenario for a single node executing `LinearStateBatchNotariseFlow` and verifying the results
|
// Responsible for executing test scenario for a single node executing `LinearStateBatchNotariseFlow` and verifying the results
|
||||||
class LinearStateScenarioRunner(options: OptionSet) : AbstractScenarioRunner(options), Callable<Boolean> {
|
class LinearStateScenarioRunner(options: OptionSet) : AbstractScenarioRunner(options), Callable<Boolean> {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = contextLogger()
|
private val logger = contextLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun call(): Boolean {
|
override fun call(): Boolean {
|
||||||
|
|
||||||
scenarioInitialized()
|
scenarioInitialized()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -42,6 +40,7 @@ class LinearStateScenarioRunner(options: OptionSet) : AbstractScenarioRunner(opt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
private fun verifyResultsAndStatesTally(results: MutableList<LinearStateBatchNotariseFlow.Result>, states: Vault.Page<LinearStateBatchNotariseContract.State>): Boolean {
|
private fun verifyResultsAndStatesTally(results: MutableList<LinearStateBatchNotariseFlow.Result>, states: Vault.Page<LinearStateBatchNotariseContract.State>): Boolean {
|
||||||
// Unfortunately, there is absolutely nothing in `LinearStateBatchNotariseFlow.Result` which can link it to the original transaction
|
// Unfortunately, there is absolutely nothing in `LinearStateBatchNotariseFlow.Result` which can link it to the original transaction
|
||||||
return true
|
return true
|
||||||
|
@ -8,33 +8,11 @@
|
|||||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
buildscript {
|
ext {
|
||||||
// For sharing constants between builds
|
javaassist_version = "3.12.1.GA"
|
||||||
Properties constants = new Properties()
|
|
||||||
file("$projectDir/../../constants.properties").withInputStream { constants.load(it) }
|
|
||||||
|
|
||||||
ext.kotlin_version = constants.getProperty("kotlinVersion")
|
|
||||||
ext.javaassist_version = "3.12.1.GA"
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
|
||||||
jcenter()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
|
||||||
jcenter()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
apply plugin: 'kotlin-kapt'
|
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
|
|
||||||
description 'A javaagent to allow hooking into Kryo'
|
description 'A javaagent to allow hooking into Kryo'
|
||||||
|
@ -8,33 +8,11 @@
|
|||||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
buildscript {
|
ext {
|
||||||
// For sharing constants between builds
|
javaassist_version = "3.12.1.GA"
|
||||||
Properties constants = new Properties()
|
|
||||||
file("$projectDir/../../constants.properties").withInputStream { constants.load(it) }
|
|
||||||
|
|
||||||
ext.kotlin_version = constants.getProperty("kotlinVersion")
|
|
||||||
ext.javaassist_version = "3.12.1.GA"
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
|
||||||
jcenter()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
|
||||||
jcenter()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
apply plugin: 'kotlin-kapt'
|
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
|
|
||||||
description 'A javaagent to allow hooking into the instrumentation by Quasar'
|
description 'A javaagent to allow hooking into the instrumentation by Quasar'
|
||||||
|
@ -12,12 +12,12 @@ package net.corda.finance.contracts.universal
|
|||||||
|
|
||||||
import net.corda.core.crypto.toStringShort
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.uncheckedCast
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
private class PrettyPrint(arr : Arrangement) {
|
private class PrettyPrint(arr : Arrangement) {
|
||||||
|
|
||||||
val parties = involvedParties(arr)
|
val parties = involvedParties(arr)
|
||||||
|
|
||||||
private val sb = StringBuilder()
|
private val sb = StringBuilder()
|
||||||
@ -26,21 +26,21 @@ private class PrettyPrint(arr : Arrangement) {
|
|||||||
private var atStart = true
|
private var atStart = true
|
||||||
private fun print(msg: String) {
|
private fun print(msg: String) {
|
||||||
if (atStart)
|
if (atStart)
|
||||||
repeat(indentLevel, { sb.append(' ') })
|
repeat(indentLevel) { sb.append(' ') }
|
||||||
sb.append(msg)
|
sb.append(msg)
|
||||||
atStart = false
|
atStart = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun println(message: Any?) {
|
private fun println(message: Any?) {
|
||||||
if (atStart)
|
if (atStart)
|
||||||
repeat(indentLevel, { sb.append(' ') })
|
repeat(indentLevel) { sb.append(' ') }
|
||||||
sb.appendln(message)
|
sb.appendln(message)
|
||||||
atStart = true
|
atStart = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun print(msg: Any?) {
|
private fun print(msg: Any?) {
|
||||||
if (atStart)
|
if (atStart)
|
||||||
repeat(indentLevel, { sb.append(' ') })
|
repeat(indentLevel) { sb.append(' ') }
|
||||||
sb.append(msg)
|
sb.append(msg)
|
||||||
atStart = false
|
atStart = false
|
||||||
}
|
}
|
||||||
@ -55,8 +55,7 @@ private class PrettyPrint(arr : Arrangement) {
|
|||||||
val partyMap = mutableMapOf<PublicKey, String>()
|
val partyMap = mutableMapOf<PublicKey, String>()
|
||||||
val usedPartyNames = mutableSetOf<String>()
|
val usedPartyNames = mutableSetOf<String>()
|
||||||
|
|
||||||
fun createPartyName(party : Party) : String
|
fun createPartyName(party : Party): String {
|
||||||
{
|
|
||||||
val parts = party.name.organisation.toLowerCase().split(' ')
|
val parts = party.name.organisation.toLowerCase().split(' ')
|
||||||
|
|
||||||
var camelName = parts.drop(1).fold(parts.first()) {
|
var camelName = parts.drop(1).fold(parts.first()) {
|
||||||
@ -79,38 +78,38 @@ private class PrettyPrint(arr : Arrangement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun prettyPrint(per: Perceivable<Boolean>, x: Boolean? = null) {
|
fun prettyPrintBoolean(per: Perceivable<Boolean>) {
|
||||||
when (per) {
|
when (per) {
|
||||||
is Const -> print("\"${per.value}\"")
|
is Const -> print("\"${per.value}\"")
|
||||||
is PerceivableOr -> {
|
is PerceivableOr -> {
|
||||||
prettyPrint(per.left)
|
prettyPrintBoolean(per.left)
|
||||||
print(" or ")
|
print(" or ")
|
||||||
prettyPrint(per.right)
|
prettyPrintBoolean(per.right)
|
||||||
}
|
}
|
||||||
is PerceivableAnd -> {
|
is PerceivableAnd -> {
|
||||||
prettyPrint(per.left)
|
prettyPrintBoolean(per.left)
|
||||||
print(" and ")
|
print(" and ")
|
||||||
prettyPrint(per.right)
|
prettyPrintBoolean(per.right)
|
||||||
}
|
}
|
||||||
is TimePerceivable -> {
|
is TimePerceivable -> {
|
||||||
when (per.cmp) {
|
when (per.cmp) {
|
||||||
Comparison.GT, Comparison.GTE -> {
|
Comparison.GT, Comparison.GTE -> {
|
||||||
print("after(")
|
print("after(")
|
||||||
prettyPrint(per.instant)
|
prettyPrintInstant(per.instant)
|
||||||
print(")")
|
print(")")
|
||||||
}
|
}
|
||||||
Comparison.LT, Comparison.LTE -> {
|
Comparison.LT, Comparison.LTE -> {
|
||||||
print("before(")
|
print("before(")
|
||||||
prettyPrint(per.instant)
|
prettyPrintInstant(per.instant)
|
||||||
print(")")
|
print(")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PerceivableComparison<*> -> {
|
is PerceivableComparison<*> -> {
|
||||||
when (per.type) {
|
when (per.type) {
|
||||||
BigDecimal::class.java -> prettyPrint(per.left as Perceivable<BigDecimal>)
|
BigDecimal::class.java -> prettyPrintBigDecimal(uncheckedCast(per.left))
|
||||||
Instant::class.java -> prettyPrint(per.left as Perceivable<Instant>)
|
Instant::class.java -> prettyPrintInstant(uncheckedCast(per.left))
|
||||||
Boolean::class.java -> prettyPrint(per.left as Perceivable<Boolean>)
|
Boolean::class.java -> prettyPrintBoolean(uncheckedCast(per.left))
|
||||||
}
|
}
|
||||||
when (per.cmp) {
|
when (per.cmp) {
|
||||||
Comparison.GT -> print(" > ")
|
Comparison.GT -> print(" > ")
|
||||||
@ -119,9 +118,9 @@ private class PrettyPrint(arr : Arrangement) {
|
|||||||
Comparison.LTE -> print(" <= ")
|
Comparison.LTE -> print(" <= ")
|
||||||
}
|
}
|
||||||
when (per.type) {
|
when (per.type) {
|
||||||
BigDecimal::class.java -> prettyPrint(per.right as Perceivable<BigDecimal>)
|
BigDecimal::class.java -> prettyPrintBigDecimal(uncheckedCast(per.right))
|
||||||
Instant::class.java -> prettyPrint(per.right as Perceivable<Instant>)
|
Instant::class.java -> prettyPrintInstant(uncheckedCast(per.right))
|
||||||
Boolean::class.java -> prettyPrint(per.right as Perceivable<Boolean>)
|
Boolean::class.java -> prettyPrintBoolean(uncheckedCast(per.right))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is TerminalEvent -> print("TerminalEvent(${partyMap[per.reference.owningKey]}, \"${per.source}\")")
|
is TerminalEvent -> print("TerminalEvent(${partyMap[per.reference.owningKey]}, \"${per.source}\")")
|
||||||
@ -130,7 +129,7 @@ private class PrettyPrint(arr : Arrangement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun prettyPrint(per: Perceivable<Instant>, x: Instant? = null) {
|
fun prettyPrintInstant(per: Perceivable<Instant>) {
|
||||||
when (per) {
|
when (per) {
|
||||||
is Const -> print("\"${per.value}\"")
|
is Const -> print("\"${per.value}\"")
|
||||||
is StartDate -> print("startDate")
|
is StartDate -> print("startDate")
|
||||||
@ -139,34 +138,33 @@ private class PrettyPrint(arr : Arrangement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun prettyPrint(per: Perceivable<BigDecimal>, x: BigDecimal? = null) {
|
fun prettyPrintBigDecimal(per: Perceivable<BigDecimal>) {
|
||||||
when (per) {
|
when (per) {
|
||||||
is PerceivableOperation<BigDecimal> -> {
|
is PerceivableOperation<BigDecimal> -> {
|
||||||
prettyPrint(per.left)
|
prettyPrintBigDecimal(per.left)
|
||||||
when (per.op) {
|
when (per.op) {
|
||||||
Operation.PLUS -> print(" + ")
|
Operation.PLUS -> print(" + ")
|
||||||
Operation.MINUS -> print(" - ")
|
Operation.MINUS -> print(" - ")
|
||||||
Operation.DIV -> print(" / ")
|
Operation.DIV -> print(" / ")
|
||||||
Operation.TIMES -> print(" * ")
|
Operation.TIMES -> print(" * ")
|
||||||
else -> print(per.op)
|
|
||||||
}
|
}
|
||||||
prettyPrint(per.right)
|
prettyPrintBigDecimal(per.right)
|
||||||
}
|
}
|
||||||
is UnaryPlus -> {
|
is UnaryPlus -> {
|
||||||
print("(")
|
print("(")
|
||||||
prettyPrint(per.arg)
|
prettyPrintBigDecimal(per.arg)
|
||||||
print(".).plus()")
|
print(".).plus()")
|
||||||
}
|
}
|
||||||
is Const -> print(per.value)
|
is Const -> print(per.value)
|
||||||
is Interest -> {
|
is Interest -> {
|
||||||
print("Interest(")
|
print("Interest(")
|
||||||
prettyPrint(per.amount)
|
prettyPrintBigDecimal(per.amount)
|
||||||
print(", \"${per.dayCountConvention}\", ")
|
print(", \"${per.dayCountConvention}\", ")
|
||||||
prettyPrint(per.amount)
|
prettyPrintBigDecimal(per.amount)
|
||||||
print(", ")
|
print(", ")
|
||||||
prettyPrint(per.start)
|
prettyPrintInstant(per.start)
|
||||||
print(", ")
|
print(", ")
|
||||||
prettyPrint(per.end)
|
prettyPrintInstant(per.end)
|
||||||
print(")")
|
print(")")
|
||||||
}
|
}
|
||||||
is CurrencyCross -> print("${per.foreign}/${per.domestic}")
|
is CurrencyCross -> print("${per.foreign}/${per.domestic}")
|
||||||
@ -175,7 +173,6 @@ private class PrettyPrint(arr : Arrangement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun prettyPrint(arr: Arrangement) {
|
fun prettyPrint(arr: Arrangement) {
|
||||||
|
|
||||||
when (arr) {
|
when (arr) {
|
||||||
is Zero -> println("zero")
|
is Zero -> println("zero")
|
||||||
is RollOut -> {
|
is RollOut -> {
|
||||||
@ -193,7 +190,7 @@ private class PrettyPrint(arr : Arrangement) {
|
|||||||
is Continuation -> println("next()")
|
is Continuation -> println("next()")
|
||||||
is Obligation -> {
|
is Obligation -> {
|
||||||
print("${partyMap[arr.from.owningKey]}.gives( ${partyMap[arr.to.owningKey]}, ")
|
print("${partyMap[arr.from.owningKey]}.gives( ${partyMap[arr.to.owningKey]}, ")
|
||||||
prettyPrint(arr.amount)
|
prettyPrintBigDecimal(arr.amount)
|
||||||
println(", ${arr.currency})")
|
println(", ${arr.currency})")
|
||||||
}
|
}
|
||||||
is Actions -> {
|
is Actions -> {
|
||||||
@ -201,7 +198,7 @@ private class PrettyPrint(arr : Arrangement) {
|
|||||||
indent {
|
indent {
|
||||||
for ((name, condition, arrangement) in arr.actions) {
|
for ((name, condition, arrangement) in arr.actions) {
|
||||||
print("\"$name\".givenThat(")
|
print("\"$name\".givenThat(")
|
||||||
prettyPrint(condition)
|
prettyPrintBoolean(condition)
|
||||||
println(") {")
|
println(") {")
|
||||||
indent {
|
indent {
|
||||||
prettyPrint(arrangement)
|
prettyPrint(arrangement)
|
||||||
|
@ -46,46 +46,45 @@ class UniversalContract : Contract {
|
|||||||
class Split(val ratio: BigDecimal) : Commands
|
class Split(val ratio: BigDecimal) : Commands
|
||||||
}
|
}
|
||||||
|
|
||||||
fun eval(@Suppress("UNUSED_PARAMETER") tx: LedgerTransaction, expr: Perceivable<Instant>): Instant? = when (expr) {
|
fun evalInstant(expr: Perceivable<Instant>): Instant? = when (expr) {
|
||||||
is Const -> expr.value
|
is Const -> expr.value
|
||||||
is StartDate -> null
|
is StartDate -> null
|
||||||
is EndDate -> null
|
is EndDate -> null
|
||||||
else -> throw Error("Unable to evaluate")
|
else -> throw Error("Unable to evaluate")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun eval(tx: LedgerTransaction, expr: Perceivable<Boolean>): Boolean = when (expr) {
|
fun evalBoolean(tx: LedgerTransaction, expr: Perceivable<Boolean>): Boolean = when (expr) {
|
||||||
is PerceivableAnd -> eval(tx, expr.left) && eval(tx, expr.right)
|
is PerceivableAnd -> evalBoolean(tx, expr.left) && evalBoolean(tx, expr.right)
|
||||||
is PerceivableOr -> eval(tx, expr.left) || eval(tx, expr.right)
|
is PerceivableOr -> evalBoolean(tx, expr.left) || evalBoolean(tx, expr.right)
|
||||||
is Const<Boolean> -> expr.value
|
is Const<Boolean> -> expr.value
|
||||||
is TimePerceivable -> when (expr.cmp) {
|
is TimePerceivable -> when (expr.cmp) {
|
||||||
Comparison.LTE -> tx.timeWindow!!.fromTime!! <= eval(tx, expr.instant)
|
Comparison.LTE -> tx.timeWindow!!.fromTime!! <= evalInstant(expr.instant)
|
||||||
Comparison.GTE -> tx.timeWindow!!.untilTime!! >= eval(tx, expr.instant)
|
Comparison.GTE -> tx.timeWindow!!.untilTime!! >= evalInstant(expr.instant)
|
||||||
else -> throw NotImplementedError("eval special")
|
else -> throw NotImplementedError("eval special")
|
||||||
}
|
}
|
||||||
is ActorPerceivable -> tx.commands.single().signers.contains(expr.actor.owningKey)
|
is ActorPerceivable -> tx.commands.single().signers.contains(expr.actor.owningKey)
|
||||||
else -> throw NotImplementedError("eval - Boolean - " + expr.javaClass.name)
|
else -> throw NotImplementedError("eval - Boolean - " + expr.javaClass.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun eval(tx: LedgerTransaction, expr: Perceivable<BigDecimal>): BigDecimal =
|
fun evalBigDecimal(tx: LedgerTransaction, expr: Perceivable<BigDecimal>): BigDecimal =
|
||||||
when (expr) {
|
when (expr) {
|
||||||
is Const<BigDecimal> -> expr.value
|
is Const<BigDecimal> -> expr.value
|
||||||
is UnaryPlus -> {
|
is UnaryPlus -> {
|
||||||
val x = eval(tx, expr.arg)
|
val x = evalBigDecimal(tx, expr.arg)
|
||||||
if (x > BigDecimal.ZERO)
|
if (x > BigDecimal.ZERO)
|
||||||
x
|
x
|
||||||
else
|
else
|
||||||
BigDecimal.ZERO
|
BigDecimal.ZERO
|
||||||
}
|
}
|
||||||
is PerceivableOperation -> {
|
is PerceivableOperation -> {
|
||||||
val l = eval(tx, expr.left)
|
val l = evalBigDecimal(tx, expr.left)
|
||||||
val r = eval(tx, expr.right)
|
val r = evalBigDecimal(tx, expr.right)
|
||||||
|
|
||||||
when (expr.op) {
|
when (expr.op) {
|
||||||
Operation.DIV -> l / r
|
Operation.DIV -> l / r
|
||||||
Operation.MINUS -> l - r
|
Operation.MINUS -> l - r
|
||||||
Operation.PLUS -> l + r
|
Operation.PLUS -> l + r
|
||||||
Operation.TIMES -> l * r
|
Operation.TIMES -> l * r
|
||||||
else -> throw NotImplementedError("eval - amount - operation " + expr.op)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Fixing -> {
|
is Fixing -> {
|
||||||
@ -93,8 +92,8 @@ class UniversalContract : Contract {
|
|||||||
0.0.bd
|
0.0.bd
|
||||||
}
|
}
|
||||||
is Interest -> {
|
is Interest -> {
|
||||||
val a = eval(tx, expr.amount)
|
val a = evalBigDecimal(tx, expr.amount)
|
||||||
val i = eval(tx, expr.interest)
|
val i = evalBigDecimal(tx, expr.interest)
|
||||||
|
|
||||||
//TODO
|
//TODO
|
||||||
|
|
||||||
@ -105,7 +104,7 @@ class UniversalContract : Contract {
|
|||||||
|
|
||||||
fun validateImmediateTransfers(tx: LedgerTransaction, arrangement: Arrangement): Arrangement = when (arrangement) {
|
fun validateImmediateTransfers(tx: LedgerTransaction, arrangement: Arrangement): Arrangement = when (arrangement) {
|
||||||
is Obligation -> {
|
is Obligation -> {
|
||||||
val amount = eval(tx, arrangement.amount)
|
val amount = evalBigDecimal(tx, arrangement.amount)
|
||||||
requireThat { "transferred quantity is non-negative" using (amount >= BigDecimal.ZERO) }
|
requireThat { "transferred quantity is non-negative" using (amount >= BigDecimal.ZERO) }
|
||||||
Obligation(const(amount), arrangement.currency, arrangement.from, arrangement.to)
|
Obligation(const(amount), arrangement.currency, arrangement.from, arrangement.to)
|
||||||
}
|
}
|
||||||
@ -220,7 +219,7 @@ class UniversalContract : Contract {
|
|||||||
"action must have a time-window" using (tx.timeWindow != null)
|
"action must have a time-window" using (tx.timeWindow != null)
|
||||||
// "action must be authorized" by (cmd.signers.any { action.actors.any { party -> party.owningKey == it } })
|
// "action must be authorized" by (cmd.signers.any { action.actors.any { party -> party.owningKey == it } })
|
||||||
// todo perhaps merge these two requirements?
|
// todo perhaps merge these two requirements?
|
||||||
"condition must be met" using (eval(tx, action.condition))
|
"condition must be met" using evalBoolean(tx, action.condition)
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify that any resulting transfers can be resolved
|
// verify that any resulting transfers can be resolved
|
||||||
@ -297,7 +296,7 @@ class UniversalContract : Contract {
|
|||||||
perceivable.dayCountConvention, replaceFixing(tx, perceivable.interest, fixings, unusedFixings),
|
perceivable.dayCountConvention, replaceFixing(tx, perceivable.interest, fixings, unusedFixings),
|
||||||
perceivable.start, perceivable.end))
|
perceivable.start, perceivable.end))
|
||||||
is Fixing -> {
|
is Fixing -> {
|
||||||
val dt = eval(tx, perceivable.date)
|
val dt = evalInstant(perceivable.date)
|
||||||
if (dt != null && fixings.containsKey(FixOf(perceivable.source, dt.toLocalDate(), perceivable.tenor))) {
|
if (dt != null && fixings.containsKey(FixOf(perceivable.source, dt.toLocalDate(), perceivable.tenor))) {
|
||||||
unusedFixings.remove(FixOf(perceivable.source, dt.toLocalDate(), perceivable.tenor))
|
unusedFixings.remove(FixOf(perceivable.source, dt.toLocalDate(), perceivable.tenor))
|
||||||
uncheckedCast(Const(fixings[FixOf(perceivable.source, dt.toLocalDate(), perceivable.tenor)]!!))
|
uncheckedCast(Const(fixings[FixOf(perceivable.source, dt.toLocalDate(), perceivable.tenor)]!!))
|
||||||
|
@ -24,13 +24,12 @@ class IRS {
|
|||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
|
||||||
|
|
||||||
val notional = 50.M
|
private val testTxTime1: Instant = Instant.parse("2017-09-02T12:00:00.00Z")
|
||||||
val currency = EUR
|
private val notional = 50.M
|
||||||
|
private val currency = EUR
|
||||||
val tradeDate: LocalDate = LocalDate.of(2016, 9, 1)
|
|
||||||
|
|
||||||
|
private val tradeDate: LocalDate = LocalDate.of(2016, 9, 1)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
@ -43,7 +42,7 @@ class IRS {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val contractInitial = arrange {
|
private val contractInitial = arrange {
|
||||||
rollOut("2016-09-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
|
rollOut("2016-09-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
|
||||||
actions {
|
actions {
|
||||||
(acmeCorp or highStreetBank) may {
|
(acmeCorp or highStreetBank) may {
|
||||||
@ -62,7 +61,8 @@ class IRS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val contractAfterFixingFirst = arrange {
|
|
||||||
|
private val contractAfterFixingFirst = arrange {
|
||||||
actions {
|
actions {
|
||||||
(acmeCorp or highStreetBank) may {
|
(acmeCorp or highStreetBank) may {
|
||||||
val floating = interest(notional, "act/365", 1.0.bd, "2016-09-01", "2016-12-01")
|
val floating = interest(notional, "act/365", 1.0.bd, "2016-09-01", "2016-12-01")
|
||||||
@ -93,15 +93,15 @@ class IRS {
|
|||||||
rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
|
rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
|
||||||
actions {
|
actions {
|
||||||
(acmeCorp or highStreetBank) may {
|
(acmeCorp or highStreetBank) may {
|
||||||
val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end)
|
val nextFloating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end)
|
||||||
val fixed = interest(notional, "act/365", 0.5.bd, start, end)
|
val nextFixed = interest(notional, "act/365", 0.5.bd, start, end)
|
||||||
|
|
||||||
"pay floating" anytime {
|
"pay floating" anytime {
|
||||||
highStreetBank.owes(acmeCorp, floating - fixed, currency)
|
highStreetBank.owes(acmeCorp, nextFloating - nextFixed, currency)
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
"pay fixed" anytime {
|
"pay fixed" anytime {
|
||||||
highStreetBank.owes(acmeCorp, fixed - floating, currency)
|
highStreetBank.owes(acmeCorp, nextFixed - nextFloating, currency)
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +112,7 @@ class IRS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val contractAfterExecutionFirst = arrange {
|
private val contractAfterExecutionFirst = arrange {
|
||||||
rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
|
rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
|
||||||
actions {
|
actions {
|
||||||
(acmeCorp or highStreetBank) may {
|
(acmeCorp or highStreetBank) may {
|
||||||
@ -132,19 +132,20 @@ class IRS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val paymentFirst = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) }
|
private val paymentFirst = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) }
|
||||||
|
|
||||||
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY), contractInitial)
|
private val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY), contractInitial)
|
||||||
|
|
||||||
val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterFixingFirst)
|
private val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterFixingFirst)
|
||||||
val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterExecutionFirst)
|
private val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterExecutionFirst)
|
||||||
|
|
||||||
|
private val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY), paymentFirst)
|
||||||
|
|
||||||
val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY), paymentFirst)
|
|
||||||
@Test
|
@Test
|
||||||
fun issue() {
|
fun issue() {
|
||||||
transaction {
|
transaction {
|
||||||
output(UNIVERSAL_PROGRAM_ID, stateInitial)
|
output(UNIVERSAL_PROGRAM_ID, stateInitial)
|
||||||
timeWindow(TEST_TX_TIME_1)
|
timeWindow(testTxTime1)
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
|
command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
|
||||||
@ -160,7 +161,7 @@ class IRS {
|
|||||||
transaction {
|
transaction {
|
||||||
input(UNIVERSAL_PROGRAM_ID, stateInitial)
|
input(UNIVERSAL_PROGRAM_ID, stateInitial)
|
||||||
output(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
|
output(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
|
||||||
timeWindow(TEST_TX_TIME_1)
|
timeWindow(testTxTime1)
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
|
command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
|
||||||
@ -200,7 +201,7 @@ class IRS {
|
|||||||
input(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
|
input(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
|
||||||
output(UNIVERSAL_PROGRAM_ID, stateAfterExecutionFirst)
|
output(UNIVERSAL_PROGRAM_ID, stateAfterExecutionFirst)
|
||||||
output(UNIVERSAL_PROGRAM_ID, statePaymentFirst)
|
output(UNIVERSAL_PROGRAM_ID, statePaymentFirst)
|
||||||
timeWindow(TEST_TX_TIME_1)
|
timeWindow(testTxTime1)
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
|
command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
|
||||||
|
@ -27,8 +27,8 @@ class CashSelectionOracleImpl : AbstractCashSelection(maxRetries = 16, retrySlee
|
|||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isCompatible(metaData: DatabaseMetaData): Boolean {
|
override fun isCompatible(metadata: DatabaseMetaData): Boolean {
|
||||||
return metaData.driverName.startsWith(JDBC_DRIVER_NAME, ignoreCase = true)
|
return metadata.driverName.startsWith(JDBC_DRIVER_NAME, ignoreCase = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString() = "${this::class.qualifiedName} for '$JDBC_DRIVER_NAME'"
|
override fun toString() = "${this::class.qualifiedName} for '$JDBC_DRIVER_NAME'"
|
||||||
|
@ -16,7 +16,6 @@ import net.corda.core.identity.AbstractParty
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.toBase58String
|
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.sql.DatabaseMetaData
|
import java.sql.DatabaseMetaData
|
||||||
import java.sql.ResultSet
|
import java.sql.ResultSet
|
||||||
@ -32,8 +31,8 @@ class CashSelectionSQLServerImpl : AbstractCashSelection(maxRetries = 16, retryS
|
|||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isCompatible(metaData: DatabaseMetaData): Boolean {
|
override fun isCompatible(metadata: DatabaseMetaData): Boolean {
|
||||||
return metaData.driverName.startsWith(JDBC_DRIVER_NAME, ignoreCase = true)
|
return metadata.driverName.startsWith(JDBC_DRIVER_NAME, ignoreCase = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString() = "${this::class.qualifiedName} for '$JDBC_DRIVER_NAME'"
|
override fun toString() = "${this::class.qualifiedName} for '$JDBC_DRIVER_NAME'"
|
||||||
|
@ -8,7 +8,6 @@ import java.nio.file.Paths
|
|||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
|
|
||||||
if (args.isEmpty()) {
|
if (args.isEmpty()) {
|
||||||
println("Usage: launcher <main-class-name> [args]")
|
println("Usage: launcher <main-class-name> [args]")
|
||||||
exitProcess(0)
|
exitProcess(0)
|
||||||
@ -54,15 +53,14 @@ fun main(args: Array<String>) {
|
|||||||
@Suppress("unchecked")
|
@Suppress("unchecked")
|
||||||
private fun fixBaseDirArg(args: Array<String>, nodeBaseDirFromArgs: Path): Array<String> {
|
private fun fixBaseDirArg(args: Array<String>, nodeBaseDirFromArgs: Path): Array<String> {
|
||||||
val baseDirIdx = args.indexOf("--base-directory")
|
val baseDirIdx = args.indexOf("--base-directory")
|
||||||
if (baseDirIdx != -1){
|
return if (baseDirIdx != -1) {
|
||||||
args[baseDirIdx+1] = nodeBaseDirFromArgs.toString()
|
// Replace the arg that follows, i.e. --base-directory X
|
||||||
return args
|
// TODO This will not work for --base-directory=X
|
||||||
|
args[baseDirIdx + 1] = nodeBaseDirFromArgs.toString()
|
||||||
|
args
|
||||||
|
} else {
|
||||||
|
args + listOf("--base-directory", nodeBaseDirFromArgs.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
val argsWithBaseDir = args.copyOf(args.size + 2)
|
|
||||||
argsWithBaseDir[argsWithBaseDir.lastIndex - 1] = "--base-directory"
|
|
||||||
argsWithBaseDir[argsWithBaseDir.lastIndex] = nodeBaseDirFromArgs.toString()
|
|
||||||
return argsWithBaseDir as Array<String>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupClassLoader(nodeBaseDir: Path): ClassLoader {
|
private fun setupClassLoader(nodeBaseDir: Path): ClassLoader {
|
||||||
|
@ -35,6 +35,8 @@ import java.util.*
|
|||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
|
import kotlin.reflect.full.createInstance
|
||||||
|
import kotlin.reflect.full.findAnnotation
|
||||||
import kotlin.reflect.full.memberProperties
|
import kotlin.reflect.full.memberProperties
|
||||||
import kotlin.reflect.full.primaryConstructor
|
import kotlin.reflect.full.primaryConstructor
|
||||||
import kotlin.reflect.jvm.jvmErasure
|
import kotlin.reflect.jvm.jvmErasure
|
||||||
@ -42,6 +44,17 @@ import kotlin.reflect.jvm.jvmErasure
|
|||||||
@Target(AnnotationTarget.PROPERTY)
|
@Target(AnnotationTarget.PROPERTY)
|
||||||
annotation class OldConfig(val value: String)
|
annotation class OldConfig(val value: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation can be used to provide ConfigParser for the class,
|
||||||
|
* the [parseAs] method will use the provided parser instead of data class constructs to parse the object.
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
annotation class CustomConfigParser(val parser: KClass<out ConfigParser<*>>)
|
||||||
|
|
||||||
|
interface ConfigParser<T> {
|
||||||
|
fun parse(config: Config): T
|
||||||
|
}
|
||||||
|
|
||||||
const val CUSTOM_NODE_PROPERTIES_ROOT = "custom"
|
const val CUSTOM_NODE_PROPERTIES_ROOT = "custom"
|
||||||
|
|
||||||
// TODO Move other config parsing to use parseAs and remove this
|
// TODO Move other config parsing to use parseAs and remove this
|
||||||
@ -50,7 +63,10 @@ operator fun <T : Any> Config.getValue(receiver: Any, metadata: KProperty<*>): T
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> Config.parseAs(clazz: KClass<T>, onUnknownKeys: ((Set<String>, logger: Logger) -> Unit) = UnknownConfigKeysPolicy.FAIL::handle, nestedPath: String? = null): T {
|
fun <T : Any> Config.parseAs(clazz: KClass<T>, onUnknownKeys: ((Set<String>, logger: Logger) -> Unit) = UnknownConfigKeysPolicy.FAIL::handle, nestedPath: String? = null): T {
|
||||||
require(clazz.isData) { "Only Kotlin data classes can be parsed. Offending: ${clazz.qualifiedName}" }
|
// Use custom parser if provided, instead of treating the object as data class.
|
||||||
|
clazz.findAnnotation<CustomConfigParser>()?.let { return uncheckedCast(it.parser.createInstance().parse(this)) }
|
||||||
|
|
||||||
|
require(clazz.isData) { "Only Kotlin data classes or class annotated with CustomConfigParser can be parsed. Offending: ${clazz.qualifiedName}" }
|
||||||
val constructor = clazz.primaryConstructor!!
|
val constructor = clazz.primaryConstructor!!
|
||||||
val parameters = constructor.parameters
|
val parameters = constructor.parameters
|
||||||
val parameterNames = parameters.flatMap { param ->
|
val parameterNames = parameters.flatMap { param ->
|
||||||
|
@ -237,6 +237,36 @@ class ConfigParsingTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `parse with provided parser`() {
|
||||||
|
val type1Config = mapOf("type" to "1", "value" to "type 1 value")
|
||||||
|
val type2Config = mapOf("type" to "2", "value" to "type 2 value")
|
||||||
|
|
||||||
|
val configuration = config("values" to listOf(type1Config, type2Config))
|
||||||
|
val objects = configuration.parseAs<TestObjects>()
|
||||||
|
|
||||||
|
assertThat(objects.values).containsExactly(TestObject.Type1("type 1 value"), TestObject.Type2("type 2 value"))
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestParser : ConfigParser<TestObject> {
|
||||||
|
override fun parse(config: Config): TestObject {
|
||||||
|
val type = config.getInt("type")
|
||||||
|
return when (type) {
|
||||||
|
1 -> config.parseAs<TestObject.Type1>(onUnknownKeys = UnknownConfigKeysPolicy.IGNORE::handle)
|
||||||
|
2 -> config.parseAs<TestObject.Type2>(onUnknownKeys = UnknownConfigKeysPolicy.IGNORE::handle)
|
||||||
|
else -> throw IllegalArgumentException("Unsupported Object type : '$type'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class TestObjects(val values: List<TestObject>)
|
||||||
|
|
||||||
|
@CustomConfigParser(TestParser::class)
|
||||||
|
sealed class TestObject {
|
||||||
|
data class Type1(val value: String) : TestObject()
|
||||||
|
data class Type2(val value: String) : TestObject()
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun <reified S : SingleData<V>, reified L : ListData<V>, V : Any> testPropertyType(
|
private inline fun <reified S : SingleData<V>, reified L : ListData<V>, V : Any> testPropertyType(
|
||||||
value1: V,
|
value1: V,
|
||||||
value2: V,
|
value2: V,
|
||||||
@ -320,6 +350,7 @@ class ConfigParsingTest {
|
|||||||
require(positive > 0) { "$positive is not positive" }
|
require(positive > 0) { "$positive is not positive" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class OldData(
|
data class OldData(
|
||||||
@OldConfig("oldValue")
|
@OldConfig("oldValue")
|
||||||
val newValue: String)
|
val newValue: String)
|
||||||
|
@ -63,6 +63,7 @@ class AuthDBTests : NodeBasedTest() {
|
|||||||
private lateinit var client: CordaRPCClient
|
private lateinit var client: CordaRPCClient
|
||||||
private lateinit var db: UsersDB
|
private lateinit var db: UsersDB
|
||||||
|
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
@Parameterized.Parameter
|
@Parameterized.Parameter
|
||||||
lateinit var passwordEncryption: PasswordEncryption
|
lateinit var passwordEncryption: PasswordEncryption
|
||||||
|
|
||||||
@ -110,7 +111,7 @@ class AuthDBTests : NodeBasedTest() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig)
|
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig)
|
||||||
client = CordaRPCClient(node.internals.configuration.rpcOptions.address!!)
|
client = CordaRPCClient(node.internals.configuration.rpcOptions.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -243,9 +244,8 @@ private data class RoleAndPermissions(val role: String, val permissions: List<St
|
|||||||
/*
|
/*
|
||||||
* Manage in-memory DB mocking a users database with the schema expected by Node's security manager
|
* Manage in-memory DB mocking a users database with the schema expected by Node's security manager
|
||||||
*/
|
*/
|
||||||
private class UsersDB : AutoCloseable {
|
private class UsersDB(name: String, users: List<UserAndRoles> = emptyList(), roleAndPermissions: List<RoleAndPermissions> = emptyList()) : AutoCloseable {
|
||||||
|
val jdbcUrl = "jdbc:h2:mem:$name;DB_CLOSE_DELAY=-1"
|
||||||
val jdbcUrl: String
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DB_CREATE_SCHEMA = """
|
const val DB_CREATE_SCHEMA = """
|
||||||
@ -295,11 +295,7 @@ private class UsersDB : AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(name: String,
|
init {
|
||||||
users: List<UserAndRoles> = emptyList(),
|
|
||||||
roleAndPermissions: List<RoleAndPermissions> = emptyList()) {
|
|
||||||
|
|
||||||
jdbcUrl = "jdbc:h2:mem:$name;DB_CLOSE_DELAY=-1"
|
|
||||||
dataSource = DataSourceFactory.createDataSource(Properties().apply {
|
dataSource = DataSourceFactory.createDataSource(Properties().apply {
|
||||||
put("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
put("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
||||||
put("dataSource.url", jdbcUrl)
|
put("dataSource.url", jdbcUrl)
|
||||||
@ -307,11 +303,9 @@ private class UsersDB : AutoCloseable {
|
|||||||
session {
|
session {
|
||||||
it.execute(DB_CREATE_SCHEMA)
|
it.execute(DB_CREATE_SCHEMA)
|
||||||
}
|
}
|
||||||
|
|
||||||
require(users.map { it.username }.toSet().size == users.size) {
|
require(users.map { it.username }.toSet().size == users.size) {
|
||||||
"Duplicate username in input"
|
"Duplicate username in input"
|
||||||
}
|
}
|
||||||
|
|
||||||
users.forEach { insert(it) }
|
users.forEach { insert(it) }
|
||||||
roleAndPermissions.forEach { insert(it) }
|
roleAndPermissions.forEach { insert(it) }
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,7 @@ class MySQLNotaryServiceTests : IntegrationTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createNotaryNode(): InternalMockNetwork.MockNode {
|
private fun createNotaryNode(): InternalMockNetwork.MockNode {
|
||||||
val dataStoreProperties = makeTestDataSourceProperties(configSupplier = { _, _ -> ConfigFactory.empty() }, fallBackConfigSupplier = ::inMemoryH2DataSourceConfig).apply {
|
val dataStoreProperties = makeInternalTestDataSourceProperties(configSupplier = { ConfigFactory.empty() }).apply {
|
||||||
setProperty("autoCommit", "false")
|
setProperty("autoCommit", "false")
|
||||||
}
|
}
|
||||||
return mockNet.createUnstartedNode(
|
return mockNet.createUnstartedNode(
|
||||||
|
@ -32,7 +32,6 @@ import net.corda.testing.internal.DEV_ROOT_CA
|
|||||||
import net.corda.testing.internal.IntegrationTest
|
import net.corda.testing.internal.IntegrationTest
|
||||||
import net.corda.testing.internal.IntegrationTestSchemas
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
|
||||||
import net.corda.testing.node.internal.SharedCompatibilityZoneParams
|
import net.corda.testing.node.internal.SharedCompatibilityZoneParams
|
||||||
import net.corda.testing.node.internal.internalDriver
|
import net.corda.testing.node.internal.internalDriver
|
||||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||||
@ -64,7 +63,6 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
private val notaryName = CordaX500Name("NotaryService", "Zurich", "CH")
|
private val notaryName = CordaX500Name("NotaryService", "Zurich", "CH")
|
||||||
private val aliceName = CordaX500Name("Alice", "London", "GB")
|
private val aliceName = CordaX500Name("Alice", "London", "GB")
|
||||||
private val genevieveName = CordaX500Name("Genevieve", "London", "GB")
|
private val genevieveName = CordaX500Name("Genevieve", "London", "GB")
|
||||||
private val log = contextLogger()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@ -82,7 +80,7 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
pollInterval = 1.seconds,
|
pollInterval = 1.seconds,
|
||||||
hostAndPort = portAllocation.nextHostAndPort(),
|
hostAndPort = portAllocation.nextHostAndPort(),
|
||||||
myHostNameValue = "localhost",
|
myHostNameValue = "localhost",
|
||||||
additionalServices = registrationHandler)
|
additionalServices = *arrayOf(registrationHandler))
|
||||||
serverHostAndPort = server.start()
|
serverHostAndPort = server.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ class MQSecurityAsNodeTest : P2PMQSecurityTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `login to a non ssl port as a node user`() {
|
fun `login to a non ssl port as a node user`() {
|
||||||
val attacker = clientTo(alice.internals.configuration.rpcOptions.address!!, sslConfiguration = null)
|
val attacker = clientTo(alice.internals.configuration.rpcOptions.address, sslConfiguration = null)
|
||||||
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
|
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
|
||||||
attacker.start(NODE_P2P_USER, NODE_P2P_USER, enableSSL = false)
|
attacker.start(NODE_P2P_USER, NODE_P2P_USER, enableSSL = false)
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ class MQSecurityAsNodeTest : P2PMQSecurityTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `login to a non ssl port as a peer user`() {
|
fun `login to a non ssl port as a peer user`() {
|
||||||
val attacker = clientTo(alice.internals.configuration.rpcOptions.address!!, sslConfiguration = null)
|
val attacker = clientTo(alice.internals.configuration.rpcOptions.address, sslConfiguration = null)
|
||||||
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
|
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
|
||||||
attacker.start(PEER_USER, PEER_USER, enableSSL = false) // Login as a peer
|
attacker.start(PEER_USER, PEER_USER, enableSSL = false) // Login as a peer
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import org.junit.Test
|
|||||||
*/
|
*/
|
||||||
class MQSecurityAsRPCTest : RPCMQSecurityTest() {
|
class MQSecurityAsRPCTest : RPCMQSecurityTest() {
|
||||||
override fun createAttacker(): SimpleMQClient {
|
override fun createAttacker(): SimpleMQClient {
|
||||||
return clientTo(alice.internals.configuration.rpcOptions.address!!)
|
return clientTo(alice.internals.configuration.rpcOptions.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -41,6 +41,7 @@ import net.corda.testing.node.internal.NodeBasedTest
|
|||||||
import net.corda.testing.node.internal.startFlow
|
import net.corda.testing.node.internal.startFlow
|
||||||
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
|
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
|
||||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||||
|
import org.apache.activemq.artemis.api.core.RoutingType
|
||||||
import org.apache.activemq.artemis.api.core.SimpleString
|
import org.apache.activemq.artemis.api.core.SimpleString
|
||||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
@ -125,9 +126,9 @@ abstract class MQSecurityTest : NodeBasedTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun loginToRPCAndGetClientQueue(): String {
|
fun loginToRPCAndGetClientQueue(): String {
|
||||||
loginToRPC(alice.internals.configuration.rpcOptions.address!!, rpcUser)
|
loginToRPC(alice.internals.configuration.rpcOptions.address, rpcUser)
|
||||||
val clientQueueQuery = SimpleString("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.${rpcUser.username}.*")
|
val clientQueueQuery = SimpleString("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.${rpcUser.username}.*")
|
||||||
val client = clientTo(alice.internals.configuration.rpcOptions.address!!)
|
val client = clientTo(alice.internals.configuration.rpcOptions.address)
|
||||||
client.start(rpcUser.username, rpcUser.password, false)
|
client.start(rpcUser.username, rpcUser.password, false)
|
||||||
return client.session.addressQuery(clientQueueQuery).queueNames.single().toString()
|
return client.session.addressQuery(clientQueueQuery).queueNames.single().toString()
|
||||||
}
|
}
|
||||||
@ -140,7 +141,7 @@ abstract class MQSecurityTest : NodeBasedTest() {
|
|||||||
|
|
||||||
fun assertTempQueueCreationAttackFails(queue: String) {
|
fun assertTempQueueCreationAttackFails(queue: String) {
|
||||||
assertAttackFails(queue, "CREATE_NON_DURABLE_QUEUE") {
|
assertAttackFails(queue, "CREATE_NON_DURABLE_QUEUE") {
|
||||||
attacker.session.createTemporaryQueue(queue, queue)
|
attacker.session.createTemporaryQueue(queue, RoutingType.MULTICAST, queue)
|
||||||
}
|
}
|
||||||
// Double-check
|
// Double-check
|
||||||
assertThatExceptionOfType(ActiveMQNonExistentQueueException::class.java).isThrownBy {
|
assertThatExceptionOfType(ActiveMQNonExistentQueueException::class.java).isThrownBy {
|
||||||
@ -157,7 +158,7 @@ abstract class MQSecurityTest : NodeBasedTest() {
|
|||||||
fun assertNonTempQueueCreationAttackFails(queue: String, durable: Boolean) {
|
fun assertNonTempQueueCreationAttackFails(queue: String, durable: Boolean) {
|
||||||
val permission = if (durable) "CREATE_DURABLE_QUEUE" else "CREATE_NON_DURABLE_QUEUE"
|
val permission = if (durable) "CREATE_DURABLE_QUEUE" else "CREATE_NON_DURABLE_QUEUE"
|
||||||
assertAttackFails(queue, permission) {
|
assertAttackFails(queue, permission) {
|
||||||
attacker.session.createQueue(queue, queue, durable)
|
attacker.session.createQueue(queue, RoutingType.MULTICAST, queue, durable)
|
||||||
}
|
}
|
||||||
// Double-check
|
// Double-check
|
||||||
assertThatExceptionOfType(ActiveMQNonExistentQueueException::class.java).isThrownBy {
|
assertThatExceptionOfType(ActiveMQNonExistentQueueException::class.java).isThrownBy {
|
||||||
|
@ -14,11 +14,10 @@ import com.typesafe.config.Config
|
|||||||
import com.typesafe.config.ConfigException
|
import com.typesafe.config.ConfigException
|
||||||
import net.corda.core.context.AuthServiceId
|
import net.corda.core.context.AuthServiceId
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.TimedFlow
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.internal.artemis.CertificateChainCheckPolicy
|
|
||||||
import net.corda.node.services.config.rpc.NodeRpcOptions
|
import net.corda.node.services.config.rpc.NodeRpcOptions
|
||||||
import net.corda.nodeapi.BrokerRpcSslOptions
|
import net.corda.nodeapi.BrokerRpcSslOptions
|
||||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||||
@ -51,6 +50,7 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
|||||||
val devModeOptions: DevModeOptions?
|
val devModeOptions: DevModeOptions?
|
||||||
val compatibilityZoneURL: URL?
|
val compatibilityZoneURL: URL?
|
||||||
val networkServices: NetworkServicesConfig?
|
val networkServices: NetworkServicesConfig?
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
val certificateChainCheckPolicies: List<CertChainPolicyConfig>
|
val certificateChainCheckPolicies: List<CertChainPolicyConfig>
|
||||||
val verifierType: VerifierType
|
val verifierType: VerifierType
|
||||||
val flowTimeout: FlowTimeoutConfiguration
|
val flowTimeout: FlowTimeoutConfiguration
|
||||||
@ -237,6 +237,7 @@ data class NodeConfigurationImpl(
|
|||||||
override val messagingServerExternal: Boolean = (messagingServerAddress != null),
|
override val messagingServerExternal: Boolean = (messagingServerAddress != null),
|
||||||
override val enterpriseConfiguration: EnterpriseConfiguration,
|
override val enterpriseConfiguration: EnterpriseConfiguration,
|
||||||
override val notary: NotaryConfig?,
|
override val notary: NotaryConfig?,
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
@Deprecated("Do not configure")
|
@Deprecated("Do not configure")
|
||||||
override val certificateChainCheckPolicies: List<CertChainPolicyConfig> = emptyList(),
|
override val certificateChainCheckPolicies: List<CertChainPolicyConfig> = emptyList(),
|
||||||
override val devMode: Boolean = false,
|
override val devMode: Boolean = false,
|
||||||
@ -413,6 +414,7 @@ data class NodeConfigurationImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for usage of deprecated config
|
// Check for usage of deprecated config
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
if(certificateChainCheckPolicies.isNotEmpty()) {
|
if(certificateChainCheckPolicies.isNotEmpty()) {
|
||||||
logger.warn("""You are configuring certificateChainCheckPolicies. This is a setting that is not used, and will be removed in a future version.
|
logger.warn("""You are configuring certificateChainCheckPolicies. This is a setting that is not used, and will be removed in a future version.
|
||||||
|Please contact the R3 team on the public slack to discuss your use case.
|
|Please contact the R3 team on the public slack to discuss your use case.
|
||||||
@ -467,18 +469,7 @@ enum class CertChainPolicyType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Do not use")
|
@Deprecated("Do not use")
|
||||||
data class CertChainPolicyConfig(val role: String, private val policy: CertChainPolicyType, private val trustedAliases: Set<String>) {
|
data class CertChainPolicyConfig(val role: String, private val policy: CertChainPolicyType, private val trustedAliases: Set<String>)
|
||||||
val certificateChainCheckPolicy: CertificateChainCheckPolicy
|
|
||||||
get() {
|
|
||||||
return when (policy) {
|
|
||||||
CertChainPolicyType.Any -> CertificateChainCheckPolicy.Any
|
|
||||||
CertChainPolicyType.RootMustMatch -> CertificateChainCheckPolicy.RootMustMatch
|
|
||||||
CertChainPolicyType.LeafMustMatch -> CertificateChainCheckPolicy.LeafMustMatch
|
|
||||||
CertChainPolicyType.MustContainOneOf -> CertificateChainCheckPolicy.MustContainOneOf(trustedAliases)
|
|
||||||
CertChainPolicyType.UsernameMustMatch -> CertificateChainCheckPolicy.UsernameMustMatchCommonName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Supported types of authentication/authorization data providers
|
// Supported types of authentication/authorization data providers
|
||||||
enum class AuthDataSourceType {
|
enum class AuthDataSourceType {
|
||||||
@ -515,8 +506,6 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copyWithAdditionalUser(user: User) = AuthService(dataSource.copyWithAdditionalUser(user), id, options)
|
|
||||||
|
|
||||||
// Optional components: cache
|
// Optional components: cache
|
||||||
data class Options(val cache: Options.Cache?) {
|
data class Options(val cache: Options.Cache?) {
|
||||||
|
|
||||||
@ -544,12 +533,6 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
|
|||||||
AuthDataSourceType.DB -> require(users == null && connection != null)
|
AuthDataSourceType.DB -> require(users == null && connection != null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copyWithAdditionalUser(user: User): DataSource {
|
|
||||||
val extendedList = this.users?.toMutableList() ?: mutableListOf()
|
|
||||||
extendedList.add(user)
|
|
||||||
return DataSource(this.type, this.passwordEncryption, this.connection, listOf(*extendedList.toTypedArray()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -34,7 +34,7 @@ import javax.annotation.concurrent.ThreadSafe
|
|||||||
*/
|
*/
|
||||||
// TODO There is duplicated logic between this and PersistentIdentityService
|
// TODO There is duplicated logic between this and PersistentIdentityService
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class InMemoryIdentityService(identities: List<out PartyAndCertificate> = emptyList(),
|
class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(),
|
||||||
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityService {
|
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityService {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
|
@ -63,7 +63,7 @@ class E2ETestKeyManagementService(val identityService: IdentityService,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun freshKeyAndCert(identity: PartyAndCertificate, revocationEnabled: Boolean): PartyAndCertificate {
|
override fun freshKeyAndCert(identity: PartyAndCertificate, revocationEnabled: Boolean): PartyAndCertificate {
|
||||||
return freshCertificate(identityService, freshKey(), identity, getSigner(identity.owningKey), revocationEnabled)
|
return freshCertificate(identityService, freshKey(), identity, getSigner(identity.owningKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSigner(publicKey: PublicKey): ContentSigner = getSigner(getSigningKeyPair(publicKey))
|
private fun getSigner(publicKey: PublicKey): ContentSigner = getSigner(getSigningKeyPair(publicKey))
|
||||||
|
@ -40,8 +40,7 @@ import java.time.Duration
|
|||||||
fun freshCertificate(identityService: IdentityService,
|
fun freshCertificate(identityService: IdentityService,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
issuer: PartyAndCertificate,
|
issuer: PartyAndCertificate,
|
||||||
issuerSigner: ContentSigner,
|
issuerSigner: ContentSigner): PartyAndCertificate {
|
||||||
revocationEnabled: Boolean = false): PartyAndCertificate {
|
|
||||||
val issuerRole = CertRole.extract(issuer.certificate)
|
val issuerRole = CertRole.extract(issuer.certificate)
|
||||||
require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" }
|
require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" }
|
||||||
val issuerCert = issuer.certificate
|
val issuerCert = issuer.certificate
|
||||||
|
@ -98,8 +98,9 @@ class PersistentKeyManagementService(val identityService: IdentityService,
|
|||||||
return keyPair.public
|
return keyPair.public
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun freshKeyAndCert(identity: PartyAndCertificate, revocationEnabled: Boolean): PartyAndCertificate =
|
override fun freshKeyAndCert(identity: PartyAndCertificate, revocationEnabled: Boolean): PartyAndCertificate {
|
||||||
freshCertificate(identityService, freshKey(), identity, getSigner(identity.owningKey), revocationEnabled)
|
return freshCertificate(identityService, freshKey(), identity, getSigner(identity.owningKey))
|
||||||
|
}
|
||||||
|
|
||||||
private fun getSigner(publicKey: PublicKey): ContentSigner = getSigner(getSigningKeyPair(publicKey))
|
private fun getSigner(publicKey: PublicKey): ContentSigner = getSigner(getSigningKeyPair(publicKey))
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import net.corda.node.utilities.AffinityExecutor
|
|||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||||
import org.hibernate.Session
|
import org.hibernate.Session
|
||||||
|
import org.hibernate.query.NativeQuery
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.sql.SQLTransientConnectionException
|
import java.sql.SQLTransientConnectionException
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
@ -132,7 +133,7 @@ class RunOnceService(private val database: CordaPersistence, private val machine
|
|||||||
|
|
||||||
private fun insertMutualExclusion(session: Session) {
|
private fun insertMutualExclusion(session: Session) {
|
||||||
val query = session.createNativeQuery("INSERT INTO $TABLE VALUES ('X', :machineName, :pid, CURRENT_TIMESTAMP, :version)", MutualExclusion::class.java)
|
val query = session.createNativeQuery("INSERT INTO $TABLE VALUES ('X', :machineName, :pid, CURRENT_TIMESTAMP, :version)", MutualExclusion::class.java)
|
||||||
query.unwrap(org.hibernate.SQLQuery::class.java).addSynchronizedEntityClass(MutualExclusion::class.java)
|
query.unwrap(NativeQuery::class.java).addSynchronizedEntityClass(MutualExclusion::class.java)
|
||||||
query.setParameter("pid", pid)
|
query.setParameter("pid", pid)
|
||||||
query.setParameter("machineName", machineName)
|
query.setParameter("machineName", machineName)
|
||||||
query.setParameter("version", 0)
|
query.setParameter("version", 0)
|
||||||
|
@ -70,7 +70,7 @@ class MultiThreadedStateMachineManager(
|
|||||||
val checkpointStorage: CheckpointStorage,
|
val checkpointStorage: CheckpointStorage,
|
||||||
val executor: ExecutorService,
|
val executor: ExecutorService,
|
||||||
val database: CordaPersistence,
|
val database: CordaPersistence,
|
||||||
val secureRandom: SecureRandom,
|
private val secureRandom: SecureRandom,
|
||||||
private val unfinishedFibers: ReusableLatch = ReusableLatch(),
|
private val unfinishedFibers: ReusableLatch = ReusableLatch(),
|
||||||
private val classloader: ClassLoader = MultiThreadedStateMachineManager::class.java.classLoader
|
private val classloader: ClassLoader = MultiThreadedStateMachineManager::class.java.classLoader
|
||||||
) : StateMachineManager, StateMachineManagerInternal {
|
) : StateMachineManager, StateMachineManagerInternal {
|
||||||
@ -158,7 +158,7 @@ class MultiThreadedStateMachineManager(
|
|||||||
}
|
}
|
||||||
serviceHub.networkMapCache.nodeReady.then {
|
serviceHub.networkMapCache.nodeReady.then {
|
||||||
resumeRestoredFlows(fibers)
|
resumeRestoredFlows(fibers)
|
||||||
flowMessaging.start { receivedMessage, deduplicationHandler ->
|
flowMessaging.start { _, deduplicationHandler ->
|
||||||
lifeCycle.requireState(State.STARTED, StateMachineStoppedException("Flow cannot be started. State machine is stopped.")) {
|
lifeCycle.requireState(State.STARTED, StateMachineStoppedException("Flow cannot be started. State machine is stopped.")) {
|
||||||
deliverExternalEvent(deduplicationHandler.externalCause)
|
deliverExternalEvent(deduplicationHandler.externalCause)
|
||||||
}
|
}
|
||||||
@ -306,10 +306,10 @@ class MultiThreadedStateMachineManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkQuasarJavaAgentPresence() {
|
private fun checkQuasarJavaAgentPresence() {
|
||||||
check(SuspendableHelper.isJavaAgentActive(), {
|
check(SuspendableHelper.isJavaAgentActive()) {
|
||||||
"""Missing the '-javaagent' JVM argument. Make sure you run the tests with the Quasar java agent attached to your JVM.
|
"""Missing the '-javaagent' JVM argument. Make sure you run the tests with the Quasar java agent attached to your JVM.
|
||||||
#See https://docs.corda.net/troubleshooting.html - 'Fiber classes not instrumented' for more details.""".trimMargin("#")
|
#See https://docs.corda.net/troubleshooting.html - 'Fiber classes not instrumented' for more details.""".trimMargin("#")
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decrementLiveFibers() {
|
private fun decrementLiveFibers() {
|
||||||
@ -324,8 +324,7 @@ class MultiThreadedStateMachineManager(
|
|||||||
return checkpointStorage.getAllCheckpoints().map { (id, serializedCheckpoint) ->
|
return checkpointStorage.getAllCheckpoints().map { (id, serializedCheckpoint) ->
|
||||||
// If a flow is added before start() then don't attempt to restore it
|
// If a flow is added before start() then don't attempt to restore it
|
||||||
if (concurrentBox.content.flows.containsKey(id)) return@map null
|
if (concurrentBox.content.flows.containsKey(id)) return@map null
|
||||||
val checkpoint = deserializeCheckpoint(serializedCheckpoint)
|
val checkpoint = deserializeCheckpoint(serializedCheckpoint) ?: return@map null
|
||||||
if (checkpoint == null) return@map null
|
|
||||||
createFlowFromCheckpoint(
|
createFlowFromCheckpoint(
|
||||||
id = id,
|
id = id,
|
||||||
checkpoint = checkpoint,
|
checkpoint = checkpoint,
|
||||||
@ -440,7 +439,7 @@ class MultiThreadedStateMachineManager(
|
|||||||
val flowId = sessionToFlow[recipientId]
|
val flowId = sessionToFlow[recipientId]
|
||||||
if (flowId == null) {
|
if (flowId == null) {
|
||||||
deduplicationHandler.afterDatabaseTransaction()
|
deduplicationHandler.afterDatabaseTransaction()
|
||||||
if (sessionMessage.payload is EndSessionMessage) {
|
if (sessionMessage.payload === EndSessionMessage) {
|
||||||
logger.debug {
|
logger.debug {
|
||||||
"Got ${EndSessionMessage::class.java.simpleName} for " +
|
"Got ${EndSessionMessage::class.java.simpleName} for " +
|
||||||
"unknown session $recipientId, discarding..."
|
"unknown session $recipientId, discarding..."
|
||||||
@ -537,12 +536,6 @@ class MultiThreadedStateMachineManager(
|
|||||||
isStartIdempotent: Boolean
|
isStartIdempotent: Boolean
|
||||||
): CordaFuture<FlowStateMachine<A>> {
|
): CordaFuture<FlowStateMachine<A>> {
|
||||||
val flowId = StateMachineRunId.createRandom()
|
val flowId = StateMachineRunId.createRandom()
|
||||||
val deduplicationSeed = when (flowStart) {
|
|
||||||
FlowStart.Explicit -> flowId.uuid.toString()
|
|
||||||
is FlowStart.Initiated ->
|
|
||||||
"${flowStart.initiatingMessage.initiatorSessionId.toLong}-" +
|
|
||||||
"${flowStart.initiatingMessage.initiationEntropy}"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before we construct the state machine state by freezing the FlowLogic we need to make sure that lazy properties
|
// Before we construct the state machine state by freezing the FlowLogic we need to make sure that lazy properties
|
||||||
// have access to the fiber (and thereby the service hub)
|
// have access to the fiber (and thereby the service hub)
|
||||||
@ -553,7 +546,7 @@ class MultiThreadedStateMachineManager(
|
|||||||
val frozenFlowLogic = (flowLogic as FlowLogic<*>).serialize(context = checkpointSerializationContext!!)
|
val frozenFlowLogic = (flowLogic as FlowLogic<*>).serialize(context = checkpointSerializationContext!!)
|
||||||
|
|
||||||
val flowCorDappVersion = FlowStateMachineImpl.createSubFlowVersion(serviceHub.cordappProvider.getCordappForFlow(flowLogic), serviceHub.myInfo.platformVersion)
|
val flowCorDappVersion = FlowStateMachineImpl.createSubFlowVersion(serviceHub.cordappProvider.getCordappForFlow(flowLogic), serviceHub.myInfo.platformVersion)
|
||||||
val initialCheckpoint = Checkpoint.create(invocationContext, flowStart, flowLogic.javaClass, frozenFlowLogic, ourIdentity, deduplicationSeed, flowCorDappVersion).getOrThrow()
|
val initialCheckpoint = Checkpoint.create(invocationContext, flowStart, flowLogic.javaClass, frozenFlowLogic, ourIdentity, flowCorDappVersion).getOrThrow()
|
||||||
val startedFuture = openFuture<Unit>()
|
val startedFuture = openFuture<Unit>()
|
||||||
val initialState = StateMachineState(
|
val initialState = StateMachineState(
|
||||||
checkpoint = initialCheckpoint,
|
checkpoint = initialCheckpoint,
|
||||||
@ -721,7 +714,7 @@ class MultiThreadedStateMachineManager(
|
|||||||
private fun addAndStartFlow(id: StateMachineRunId, flow: Flow) {
|
private fun addAndStartFlow(id: StateMachineRunId, flow: Flow) {
|
||||||
val checkpoint = flow.fiber.snapshot().checkpoint
|
val checkpoint = flow.fiber.snapshot().checkpoint
|
||||||
for (sessionId in getFlowSessionIds(checkpoint)) {
|
for (sessionId in getFlowSessionIds(checkpoint)) {
|
||||||
sessionToFlow.put(sessionId, id)
|
sessionToFlow[sessionId] = id
|
||||||
}
|
}
|
||||||
concurrentBox.concurrent {
|
concurrentBox.concurrent {
|
||||||
val oldFlow = flows.put(id, flow)
|
val oldFlow = flows.put(id, flow)
|
||||||
|
@ -66,10 +66,10 @@ import kotlin.streams.toList
|
|||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class SingleThreadedStateMachineManager(
|
class SingleThreadedStateMachineManager(
|
||||||
val serviceHub: ServiceHubInternal,
|
val serviceHub: ServiceHubInternal,
|
||||||
val checkpointStorage: CheckpointStorage,
|
private val checkpointStorage: CheckpointStorage,
|
||||||
val executor: ExecutorService,
|
val executor: ExecutorService,
|
||||||
val database: CordaPersistence,
|
val database: CordaPersistence,
|
||||||
val secureRandom: SecureRandom,
|
private val secureRandom: SecureRandom,
|
||||||
private val unfinishedFibers: ReusableLatch = ReusableLatch(),
|
private val unfinishedFibers: ReusableLatch = ReusableLatch(),
|
||||||
private val classloader: ClassLoader = SingleThreadedStateMachineManager::class.java.classLoader
|
private val classloader: ClassLoader = SingleThreadedStateMachineManager::class.java.classLoader
|
||||||
) : StateMachineManager, StateMachineManagerInternal {
|
) : StateMachineManager, StateMachineManagerInternal {
|
||||||
@ -145,7 +145,7 @@ class SingleThreadedStateMachineManager(
|
|||||||
}
|
}
|
||||||
serviceHub.networkMapCache.nodeReady.then {
|
serviceHub.networkMapCache.nodeReady.then {
|
||||||
resumeRestoredFlows(fibers)
|
resumeRestoredFlows(fibers)
|
||||||
flowMessaging.start { receivedMessage, deduplicationHandler ->
|
flowMessaging.start { _, deduplicationHandler ->
|
||||||
executor.execute {
|
executor.execute {
|
||||||
deliverExternalEvent(deduplicationHandler.externalCause)
|
deliverExternalEvent(deduplicationHandler.externalCause)
|
||||||
}
|
}
|
||||||
@ -296,10 +296,10 @@ class SingleThreadedStateMachineManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkQuasarJavaAgentPresence() {
|
private fun checkQuasarJavaAgentPresence() {
|
||||||
check(SuspendableHelper.isJavaAgentActive(), {
|
check(SuspendableHelper.isJavaAgentActive()) {
|
||||||
"""Missing the '-javaagent' JVM argument. Make sure you run the tests with the Quasar java agent attached to your JVM.
|
"""Missing the '-javaagent' JVM argument. Make sure you run the tests with the Quasar java agent attached to your JVM.
|
||||||
#See https://docs.corda.net/troubleshooting.html - 'Fiber classes not instrumented' for more details.""".trimMargin("#")
|
#See https://docs.corda.net/troubleshooting.html - 'Fiber classes not instrumented' for more details.""".trimMargin("#")
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decrementLiveFibers() {
|
private fun decrementLiveFibers() {
|
||||||
@ -314,8 +314,7 @@ class SingleThreadedStateMachineManager(
|
|||||||
return checkpointStorage.getAllCheckpoints().map { (id, serializedCheckpoint) ->
|
return checkpointStorage.getAllCheckpoints().map { (id, serializedCheckpoint) ->
|
||||||
// If a flow is added before start() then don't attempt to restore it
|
// If a flow is added before start() then don't attempt to restore it
|
||||||
mutex.locked { if (flows.containsKey(id)) return@map null }
|
mutex.locked { if (flows.containsKey(id)) return@map null }
|
||||||
val checkpoint = deserializeCheckpoint(serializedCheckpoint)
|
val checkpoint = deserializeCheckpoint(serializedCheckpoint) ?: return@map null
|
||||||
if (checkpoint == null) return@map null
|
|
||||||
logger.debug { "Restored $checkpoint" }
|
logger.debug { "Restored $checkpoint" }
|
||||||
createFlowFromCheckpoint(
|
createFlowFromCheckpoint(
|
||||||
id = id,
|
id = id,
|
||||||
@ -534,12 +533,6 @@ class SingleThreadedStateMachineManager(
|
|||||||
isStartIdempotent: Boolean
|
isStartIdempotent: Boolean
|
||||||
): CordaFuture<FlowStateMachine<A>> {
|
): CordaFuture<FlowStateMachine<A>> {
|
||||||
val flowId = StateMachineRunId.createRandom()
|
val flowId = StateMachineRunId.createRandom()
|
||||||
val deduplicationSeed = when (flowStart) {
|
|
||||||
FlowStart.Explicit -> flowId.uuid.toString()
|
|
||||||
is FlowStart.Initiated ->
|
|
||||||
"${flowStart.initiatingMessage.initiatorSessionId.toLong}-" +
|
|
||||||
"${flowStart.initiatingMessage.initiationEntropy}"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before we construct the state machine state by freezing the FlowLogic we need to make sure that lazy properties
|
// Before we construct the state machine state by freezing the FlowLogic we need to make sure that lazy properties
|
||||||
// have access to the fiber (and thereby the service hub)
|
// have access to the fiber (and thereby the service hub)
|
||||||
@ -551,7 +544,7 @@ class SingleThreadedStateMachineManager(
|
|||||||
|
|
||||||
val flowCorDappVersion = createSubFlowVersion(serviceHub.cordappProvider.getCordappForFlow(flowLogic), serviceHub.myInfo.platformVersion)
|
val flowCorDappVersion = createSubFlowVersion(serviceHub.cordappProvider.getCordappForFlow(flowLogic), serviceHub.myInfo.platformVersion)
|
||||||
|
|
||||||
val initialCheckpoint = Checkpoint.create(invocationContext, flowStart, flowLogic.javaClass, frozenFlowLogic, ourIdentity, deduplicationSeed, flowCorDappVersion).getOrThrow()
|
val initialCheckpoint = Checkpoint.create(invocationContext, flowStart, flowLogic.javaClass, frozenFlowLogic, ourIdentity, flowCorDappVersion).getOrThrow()
|
||||||
val startedFuture = openFuture<Unit>()
|
val startedFuture = openFuture<Unit>()
|
||||||
val initialState = StateMachineState(
|
val initialState = StateMachineState(
|
||||||
checkpoint = initialCheckpoint,
|
checkpoint = initialCheckpoint,
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.node.services.statemachine
|
|||||||
|
|
||||||
import net.corda.core.flows.StateMachineRunId
|
import net.corda.core.flows.StateMachineRunId
|
||||||
import net.corda.core.internal.ThreadBox
|
import net.corda.core.internal.ThreadBox
|
||||||
|
import net.corda.core.internal.TimedFlow
|
||||||
import net.corda.core.internal.bufferUntilSubscribed
|
import net.corda.core.internal.bufferUntilSubscribed
|
||||||
import net.corda.core.messaging.DataFeed
|
import net.corda.core.messaging.DataFeed
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
@ -100,12 +101,6 @@ class StaffedFlowHospital {
|
|||||||
|
|
||||||
private data class ConsultationReport(val error: Throwable, val diagnosis: Diagnosis, val by: List<Staff>)
|
private data class ConsultationReport(val error: Throwable, val diagnosis: Diagnosis, val by: List<Staff>)
|
||||||
|
|
||||||
/**
|
|
||||||
* The flow running in [flowFiber] has cleaned, possibly as a result of a flow hospital resume.
|
|
||||||
*/
|
|
||||||
// It's okay for flows to be cleaned... we fix them now!
|
|
||||||
fun flowCleaned(flowFiber: FlowFiber) = Unit
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The flow has been removed from the state machine.
|
* The flow has been removed from the state machine.
|
||||||
*/
|
*/
|
||||||
|
@ -79,7 +79,6 @@ data class Checkpoint(
|
|||||||
flowLogicClass: Class<FlowLogic<*>>,
|
flowLogicClass: Class<FlowLogic<*>>,
|
||||||
frozenFlowLogic: SerializedBytes<FlowLogic<*>>,
|
frozenFlowLogic: SerializedBytes<FlowLogic<*>>,
|
||||||
ourIdentity: Party,
|
ourIdentity: Party,
|
||||||
deduplicationSeed: String,
|
|
||||||
subFlowVersion: SubFlowVersion
|
subFlowVersion: SubFlowVersion
|
||||||
): Try<Checkpoint> {
|
): Try<Checkpoint> {
|
||||||
return SubFlow.create(flowLogicClass, subFlowVersion).map { topLevelSubFlow ->
|
return SubFlow.create(flowLogicClass, subFlowVersion).map { topLevelSubFlow ->
|
||||||
|
@ -55,9 +55,7 @@ class HospitalisingInterceptor(
|
|||||||
|
|
||||||
when (nextState.checkpoint.errorState) {
|
when (nextState.checkpoint.errorState) {
|
||||||
is ErrorState.Clean -> {
|
is ErrorState.Clean -> {
|
||||||
if (hospitalisedFlows.remove(fiber.id) != null) {
|
hospitalisedFlows.remove(fiber.id)
|
||||||
flowHospital.flowCleaned(fiber)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is ErrorState.Errored -> {
|
is ErrorState.Errored -> {
|
||||||
val exceptionsToHandle = nextState.checkpoint.errorState.errors.map { it.exception }
|
val exceptionsToHandle = nextState.checkpoint.errorState.errors.map { it.exception }
|
||||||
|
@ -86,7 +86,7 @@ abstract class AppendOnlyPersistentMapBase<K, V, E, out EK>(
|
|||||||
Transactional.Committed(value)
|
Transactional.Committed(value)
|
||||||
} else {
|
} else {
|
||||||
// Some database transactions, including us, writing, with readers seeing whatever is in the database and writers seeing the (in memory) value.
|
// Some database transactions, including us, writing, with readers seeing whatever is in the database and writers seeing the (in memory) value.
|
||||||
Transactional.InFlight(this, key, { loadValue(key) }).apply { alsoWrite(value) }
|
Transactional.InFlight(this, key, _readerValueLoader = { loadValue(key) }).apply { alsoWrite(value) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,13 +156,13 @@ abstract class AppendOnlyPersistentMapBase<K, V, E, out EK>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helpers to know if transaction(s) are currently writing the given key.
|
// Helpers to know if transaction(s) are currently writing the given key.
|
||||||
protected fun weAreWriting(key: K): Boolean = pendingKeys.get(key)?.contains(contextTransaction) ?: false
|
protected fun weAreWriting(key: K): Boolean = pendingKeys[key]?.contains(contextTransaction) ?: false
|
||||||
protected fun anyoneWriting(key: K): Boolean = pendingKeys.get(key)?.isNotEmpty() ?: false
|
protected fun anyoneWriting(key: K): Boolean = pendingKeys[key]?.isNotEmpty() ?: false
|
||||||
|
|
||||||
// Indicate this database transaction is a writer of this key.
|
// Indicate this database transaction is a writer of this key.
|
||||||
private fun addPendingKey(key: K, databaseTransaction: DatabaseTransaction): Boolean {
|
private fun addPendingKey(key: K, databaseTransaction: DatabaseTransaction): Boolean {
|
||||||
var added = true
|
var added = true
|
||||||
pendingKeys.compute(key) { k, oldSet ->
|
pendingKeys.compute(key) { _, oldSet ->
|
||||||
if (oldSet == null) {
|
if (oldSet == null) {
|
||||||
val newSet = HashSet<DatabaseTransaction>(0)
|
val newSet = HashSet<DatabaseTransaction>(0)
|
||||||
newSet += databaseTransaction
|
newSet += databaseTransaction
|
||||||
@ -177,7 +177,7 @@ abstract class AppendOnlyPersistentMapBase<K, V, E, out EK>(
|
|||||||
|
|
||||||
// Remove this database transaction as a writer of this key, because the transaction committed or rolled back.
|
// Remove this database transaction as a writer of this key, because the transaction committed or rolled back.
|
||||||
private fun removePendingKey(key: K, databaseTransaction: DatabaseTransaction) {
|
private fun removePendingKey(key: K, databaseTransaction: DatabaseTransaction) {
|
||||||
pendingKeys.compute(key) { k, oldSet ->
|
pendingKeys.compute(key) { _, oldSet ->
|
||||||
if (oldSet == null) {
|
if (oldSet == null) {
|
||||||
oldSet
|
oldSet
|
||||||
} else {
|
} else {
|
||||||
@ -209,7 +209,7 @@ abstract class AppendOnlyPersistentMapBase<K, V, E, out EK>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No one can see it.
|
// No one can see it.
|
||||||
class Missing<T>() : Transactional<T>() {
|
class Missing<T> : Transactional<T>() {
|
||||||
override val value: T
|
override val value: T
|
||||||
get() = throw NoSuchElementException("Not present")
|
get() = throw NoSuchElementException("Not present")
|
||||||
override val isPresent: Boolean
|
override val isPresent: Boolean
|
||||||
@ -238,7 +238,7 @@ abstract class AppendOnlyPersistentMapBase<K, V, E, out EK>(
|
|||||||
|
|
||||||
fun alsoWrite(_value: T) {
|
fun alsoWrite(_value: T) {
|
||||||
// Make the lazy loader the writers see actually just return the value that has been set.
|
// Make the lazy loader the writers see actually just return the value that has been set.
|
||||||
writerValueLoader.set({ _value })
|
writerValueLoader.set { _value }
|
||||||
// We make all these vals so that the lambdas do not need a reference to this, and so the onCommit only has a weak ref to the value.
|
// We make all these vals so that the lambdas do not need a reference to this, and so the onCommit only has a weak ref to the value.
|
||||||
// We want this so that the cache could evict the value (due to memory constraints etc) without the onCommit callback
|
// We want this so that the cache could evict the value (due to memory constraints etc) without the onCommit callback
|
||||||
// retaining what could be a large memory footprint object.
|
// retaining what could be a large memory footprint object.
|
||||||
@ -252,10 +252,9 @@ abstract class AppendOnlyPersistentMapBase<K, V, E, out EK>(
|
|||||||
// and then stop saying the transaction is writing the key.
|
// and then stop saying the transaction is writing the key.
|
||||||
tx.onCommit {
|
tx.onCommit {
|
||||||
if (strongComitted.compareAndSet(false, true)) {
|
if (strongComitted.compareAndSet(false, true)) {
|
||||||
val dereferencedKey = strongKey
|
|
||||||
val dereferencedValue = weakValue.get()
|
val dereferencedValue = weakValue.get()
|
||||||
if (dereferencedValue != null) {
|
if (dereferencedValue != null) {
|
||||||
strongMap.cache.put(dereferencedKey, Committed(dereferencedValue))
|
strongMap.cache.put(strongKey, Committed(dereferencedValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strongMap.removePendingKey(strongKey, tx)
|
strongMap.removePendingKey(strongKey, tx)
|
||||||
@ -272,7 +271,7 @@ abstract class AppendOnlyPersistentMapBase<K, V, E, out EK>(
|
|||||||
private fun loadAsWriter(): T {
|
private fun loadAsWriter(): T {
|
||||||
val _value = writerValueLoader.get()()
|
val _value = writerValueLoader.get()()
|
||||||
if (writerValueLoader.get() == _writerValueLoader) {
|
if (writerValueLoader.get() == _writerValueLoader) {
|
||||||
writerValueLoader.set({ _value })
|
writerValueLoader.set { _value }
|
||||||
}
|
}
|
||||||
return _value
|
return _value
|
||||||
}
|
}
|
||||||
@ -282,7 +281,7 @@ abstract class AppendOnlyPersistentMapBase<K, V, E, out EK>(
|
|||||||
private fun loadAsReader(): T? {
|
private fun loadAsReader(): T? {
|
||||||
val _value = readerValueLoader.get()()
|
val _value = readerValueLoader.get()()
|
||||||
if (readerValueLoader.get() == _readerValueLoader) {
|
if (readerValueLoader.get() == _readerValueLoader) {
|
||||||
readerValueLoader.set({ _value })
|
readerValueLoader.set { _value }
|
||||||
}
|
}
|
||||||
return _value
|
return _value
|
||||||
}
|
}
|
||||||
@ -320,7 +319,7 @@ open class AppendOnlyPersistentMap<K, V, E, out EK>(
|
|||||||
toPersistentEntity,
|
toPersistentEntity,
|
||||||
persistentEntityClass) {
|
persistentEntityClass) {
|
||||||
//TODO determine cacheBound based on entity class later or with node config allowing tuning, or using some heuristic based on heap size
|
//TODO determine cacheBound based on entity class later or with node config allowing tuning, or using some heuristic based on heap size
|
||||||
override val cache = NonInvalidatingCache<K, Transactional<V>>(
|
override val cache = NonInvalidatingCache(
|
||||||
bound = cacheBound,
|
bound = cacheBound,
|
||||||
loadFunction = { key: K ->
|
loadFunction = { key: K ->
|
||||||
// This gets called if a value is read and the cache has no Transactional for this key yet.
|
// This gets called if a value is read and the cache has no Transactional for this key yet.
|
||||||
@ -331,10 +330,10 @@ open class AppendOnlyPersistentMap<K, V, E, out EK>(
|
|||||||
// If someone is writing (but not us)
|
// If someone is writing (but not us)
|
||||||
// For those not writing, the value cannot be seen.
|
// For those not writing, the value cannot be seen.
|
||||||
// For those writing, they need to re-load the value from the database (which their database transaction CAN see).
|
// For those writing, they need to re-load the value from the database (which their database transaction CAN see).
|
||||||
Transactional.InFlight<K, V>(this, key, { null }, { loadValue(key)!! })
|
Transactional.InFlight(this, key, { null }, { loadValue(key)!! })
|
||||||
} else {
|
} else {
|
||||||
// If no one is writing, then the value does not exist.
|
// If no one is writing, then the value does not exist.
|
||||||
Transactional.Missing<V>()
|
Transactional.Missing()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// A value was found
|
// A value was found
|
||||||
@ -342,10 +341,10 @@ open class AppendOnlyPersistentMap<K, V, E, out EK>(
|
|||||||
// If we are writing, it might not be globally visible, and was evicted from the cache.
|
// If we are writing, it might not be globally visible, and was evicted from the cache.
|
||||||
// For those not writing, they need to check the database again.
|
// For those not writing, they need to check the database again.
|
||||||
// For those writing, they can see the value found.
|
// For those writing, they can see the value found.
|
||||||
Transactional.InFlight<K, V>(this, key, { loadValue(key) }, { value })
|
Transactional.InFlight(this, key, { loadValue(key) }, { value })
|
||||||
} else {
|
} else {
|
||||||
// If no one is writing, then make it globally visible.
|
// If no one is writing, then make it globally visible.
|
||||||
Transactional.Committed<V>(value)
|
Transactional.Committed(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -364,26 +363,22 @@ class WeightBasedAppendOnlyPersistentMap<K, V, E, out EK>(
|
|||||||
fromPersistentEntity,
|
fromPersistentEntity,
|
||||||
toPersistentEntity,
|
toPersistentEntity,
|
||||||
persistentEntityClass) {
|
persistentEntityClass) {
|
||||||
override val cache = NonInvalidatingWeightBasedCache<K, Transactional<V>>(
|
override val cache = NonInvalidatingWeightBasedCache(
|
||||||
maxWeight = maxWeight,
|
maxWeight = maxWeight,
|
||||||
weigher = object : Weigher<K, Transactional<V>> {
|
weigher = Weigher { key, value -> weighingFunc(key, value) },
|
||||||
override fun weigh(key: K, value: Transactional<V>): Int {
|
|
||||||
return weighingFunc(key, value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
loadFunction = { key: K ->
|
loadFunction = { key: K ->
|
||||||
val value: V? = loadValue(key)
|
val value: V? = loadValue(key)
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
if (anyoneWriting(key)) {
|
if (anyoneWriting(key)) {
|
||||||
Transactional.InFlight<K, V>(this, key, { null }, { loadValue(key)!! })
|
Transactional.InFlight(this, key, { null }, { loadValue(key)!! })
|
||||||
} else {
|
} else {
|
||||||
Transactional.Missing<V>()
|
Transactional.Missing()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (weAreWriting(key)) {
|
if (weAreWriting(key)) {
|
||||||
Transactional.InFlight<K, V>(this, key, { loadValue(key) }, { value })
|
Transactional.InFlight(this, key, { loadValue(key) }, { value })
|
||||||
} else {
|
} else {
|
||||||
Transactional.Committed<V>(value)
|
Transactional.Committed(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -46,9 +46,6 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import rx.Observable;
|
import rx.Observable;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -80,7 +77,7 @@ public class VaultQueryJavaTests {
|
|||||||
private CordaPersistence database;
|
private CordaPersistence database;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws CertificateException, InvalidAlgorithmParameterException {
|
public void setUp() {
|
||||||
List<String> cordappPackages = asList(
|
List<String> cordappPackages = asList(
|
||||||
"net.corda.testing.internal.vault",
|
"net.corda.testing.internal.vault",
|
||||||
"net.corda.finance.contracts.asset",
|
"net.corda.finance.contracts.asset",
|
||||||
@ -101,14 +98,10 @@ public class VaultQueryJavaTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void cleanUp() throws IOException {
|
public void cleanUp() {
|
||||||
database.close();
|
database.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sample Vault Query API tests
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static queryBy() tests
|
* Static queryBy() tests
|
||||||
*/
|
*/
|
||||||
@ -119,6 +112,7 @@ public class VaultQueryJavaTests {
|
|||||||
FieldInfo currency = getField("currency", SampleCashSchemaV2.PersistentCashState.class);
|
FieldInfo currency = getField("currency", SampleCashSchemaV2.PersistentCashState.class);
|
||||||
|
|
||||||
CriteriaExpression.AggregateFunctionExpression<Object, Boolean> expression = sum(quantity, singletonList(currency), Sort.Direction.ASC);
|
CriteriaExpression.AggregateFunctionExpression<Object, Boolean> expression = sum(quantity, singletonList(currency), Sort.Direction.ASC);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
VaultCustomQueryCriteria<SampleCashSchemaV2.PersistentCashState> criteria = new VaultCustomQueryCriteria(expression, Vault.StateStatus.UNCONSUMED, null);
|
VaultCustomQueryCriteria<SampleCashSchemaV2.PersistentCashState> criteria = new VaultCustomQueryCriteria(expression, Vault.StateStatus.UNCONSUMED, null);
|
||||||
|
|
||||||
database.transaction(tx -> vaultService.queryBy(FungibleAsset.class, criteria));
|
database.transaction(tx -> vaultService.queryBy(FungibleAsset.class, criteria));
|
||||||
@ -131,13 +125,14 @@ public class VaultQueryJavaTests {
|
|||||||
FieldInfo stateRef = getField("stateRef", SampleCashSchemaV2.PersistentCashState.class);
|
FieldInfo stateRef = getField("stateRef", SampleCashSchemaV2.PersistentCashState.class);
|
||||||
|
|
||||||
CriteriaExpression.AggregateFunctionExpression<Object, Boolean> expression = sum(quantity, asList(currency, stateRef), Sort.Direction.ASC);
|
CriteriaExpression.AggregateFunctionExpression<Object, Boolean> expression = sum(quantity, asList(currency, stateRef), Sort.Direction.ASC);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
VaultCustomQueryCriteria<SampleCashSchemaV2.PersistentCashState> criteria = new VaultCustomQueryCriteria(expression, Vault.StateStatus.UNCONSUMED, null);
|
VaultCustomQueryCriteria<SampleCashSchemaV2.PersistentCashState> criteria = new VaultCustomQueryCriteria(expression, Vault.StateStatus.UNCONSUMED, null);
|
||||||
|
|
||||||
database.transaction(tx -> vaultService.queryBy(FungibleAsset.class, criteria));
|
database.transaction(tx -> vaultService.queryBy(FungibleAsset.class, criteria));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void unconsumedLinearStates() throws VaultQueryException {
|
public void unconsumedLinearStates() {
|
||||||
database.transaction(tx -> {
|
database.transaction(tx -> {
|
||||||
vaultFiller.fillWithSomeTestLinearStates(3);
|
vaultFiller.fillWithSomeTestLinearStates(3);
|
||||||
return tx;
|
return tx;
|
||||||
@ -209,7 +204,7 @@ public class VaultQueryJavaTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void consumedDealStatesPagedSorted() throws VaultQueryException {
|
public void consumedDealStatesPagedSorted() {
|
||||||
List<String> dealIds = asList("123", "456", "789");
|
List<String> dealIds = asList("123", "456", "789");
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Triple<StateAndRef<LinearState>, UniqueIdentifier, Vault<DealState>> ids =
|
Triple<StateAndRef<LinearState>, UniqueIdentifier, Vault<DealState>> ids =
|
||||||
@ -320,7 +315,6 @@ public class VaultQueryJavaTests {
|
|||||||
DataFeed<Vault.Page<ContractState>, Vault.Update<ContractState>> results = vaultService.trackBy(ContractState.class, criteria);
|
DataFeed<Vault.Page<ContractState>, Vault.Update<ContractState>> results = vaultService.trackBy(ContractState.class, criteria);
|
||||||
|
|
||||||
Vault.Page<ContractState> snapshot = results.getSnapshot();
|
Vault.Page<ContractState> snapshot = results.getSnapshot();
|
||||||
Observable<Vault.Update<ContractState>> updates = results.getUpdates();
|
|
||||||
|
|
||||||
// DOCEND VaultJavaQueryExample4
|
// DOCEND VaultJavaQueryExample4
|
||||||
assertThat(snapshot.getStates()).hasSize(3);
|
assertThat(snapshot.getStates()).hasSize(3);
|
||||||
@ -374,7 +368,6 @@ public class VaultQueryJavaTests {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void aggregateFunctionsWithoutGroupClause() {
|
public void aggregateFunctionsWithoutGroupClause() {
|
||||||
database.transaction(tx -> {
|
database.transaction(tx -> {
|
||||||
|
|
||||||
Amount<Currency> dollars100 = new Amount<>(100, Currency.getInstance("USD"));
|
Amount<Currency> dollars100 = new Amount<>(100, Currency.getInstance("USD"));
|
||||||
Amount<Currency> dollars200 = new Amount<>(200, Currency.getInstance("USD"));
|
Amount<Currency> dollars200 = new Amount<>(200, Currency.getInstance("USD"));
|
||||||
Amount<Currency> dollars300 = new Amount<>(300, Currency.getInstance("USD"));
|
Amount<Currency> dollars300 = new Amount<>(300, Currency.getInstance("USD"));
|
||||||
@ -420,7 +413,6 @@ public class VaultQueryJavaTests {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void aggregateFunctionsWithSingleGroupClause() {
|
public void aggregateFunctionsWithSingleGroupClause() {
|
||||||
database.transaction(tx -> {
|
database.transaction(tx -> {
|
||||||
|
|
||||||
Amount<Currency> dollars100 = new Amount<>(100, Currency.getInstance("USD"));
|
Amount<Currency> dollars100 = new Amount<>(100, Currency.getInstance("USD"));
|
||||||
Amount<Currency> dollars200 = new Amount<>(200, Currency.getInstance("USD"));
|
Amount<Currency> dollars200 = new Amount<>(200, Currency.getInstance("USD"));
|
||||||
Amount<Currency> dollars300 = new Amount<>(300, Currency.getInstance("USD"));
|
Amount<Currency> dollars300 = new Amount<>(300, Currency.getInstance("USD"));
|
||||||
|
@ -34,7 +34,6 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.core.utilities.unwrap
|
import net.corda.core.utilities.unwrap
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.GBP
|
import net.corda.finance.GBP
|
||||||
import net.corda.finance.USD
|
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
import net.corda.finance.flows.CashIssueFlow
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
import net.corda.finance.flows.CashPaymentFlow
|
import net.corda.finance.flows.CashPaymentFlow
|
||||||
@ -167,6 +166,7 @@ class CordaRPCOpsImplTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `issue and move`() {
|
fun `issue and move`() {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
withPermissions(invokeRpc(CordaRPCOps::stateMachinesFeed),
|
withPermissions(invokeRpc(CordaRPCOps::stateMachinesFeed),
|
||||||
invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed),
|
invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed),
|
||||||
invokeRpc("vaultTrackBy"),
|
invokeRpc("vaultTrackBy"),
|
||||||
@ -178,11 +178,7 @@ class CordaRPCOpsImplTest {
|
|||||||
vaultTrackCash = rpc.vaultTrackBy<Cash.State>().updates
|
vaultTrackCash = rpc.vaultTrackBy<Cash.State>().updates
|
||||||
}
|
}
|
||||||
|
|
||||||
val result = rpc.startFlow(::CashIssueFlow,
|
val result = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notary)
|
||||||
100.DOLLARS,
|
|
||||||
OpaqueBytes(ByteArray(1, { 1 })),
|
|
||||||
notary
|
|
||||||
)
|
|
||||||
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
|
||||||
@ -257,7 +253,7 @@ class CordaRPCOpsImplTest {
|
|||||||
fun `cash command by user not permissioned for cash`() {
|
fun `cash command by user not permissioned for cash`() {
|
||||||
withoutAnyPermissions {
|
withoutAnyPermissions {
|
||||||
assertThatExceptionOfType(PermissionException::class.java).isThrownBy {
|
assertThatExceptionOfType(PermissionException::class.java).isThrownBy {
|
||||||
rpc.startFlow(::CashIssueFlow, Amount(100, USD), OpaqueBytes(ByteArray(1, { 1 })), notary)
|
rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ class NodeTest {
|
|||||||
fun `generateAndSaveNodeInfo works`() {
|
fun `generateAndSaveNodeInfo works`() {
|
||||||
val configuration = createConfig(ALICE_NAME)
|
val configuration = createConfig(ALICE_NAME)
|
||||||
val info = VersionInfo(789, "3.0", "SNAPSHOT", "R3")
|
val info = VersionInfo(789, "3.0", "SNAPSHOT", "R3")
|
||||||
configureDatabase(configuration.dataSourceProperties, configuration.database, { null }, { null }).use { database ->
|
configureDatabase(configuration.dataSourceProperties, configuration.database, { null }, { null }).use {
|
||||||
val node = Node(configuration, info, initialiseSerialization = false)
|
val node = Node(configuration, info, initialiseSerialization = false)
|
||||||
assertEquals(node.generateNodeInfo(), node.generateNodeInfo()) // Node info doesn't change (including the serial)
|
assertEquals(node.generateNodeInfo(), node.generateNodeInfo()) // Node info doesn't change (including the serial)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ import org.apache.activemq.artemis.api.core.SimpleString
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import rx.Notification
|
import rx.Notification
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
|
||||||
import rx.subjects.UnicastSubject
|
import rx.subjects.UnicastSubject
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -38,7 +37,7 @@ class RoundTripObservableSerializerTests {
|
|||||||
.maximumSize(100)
|
.maximumSize(100)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
subMap.put(id, ObservableSubscription(mock<Subscription>()))
|
subMap.put(id, ObservableSubscription(mock()))
|
||||||
|
|
||||||
return subMap
|
return subMap
|
||||||
}
|
}
|
||||||
@ -48,7 +47,7 @@ class RoundTripObservableSerializerTests {
|
|||||||
})
|
})
|
||||||
|
|
||||||
private fun createRpcObservableMap(): Cache<Trace.InvocationId, UnicastSubject<Notification<*>>> {
|
private fun createRpcObservableMap(): Cache<Trace.InvocationId, UnicastSubject<Notification<*>>> {
|
||||||
val onObservableRemove = RemovalListener<Trace.InvocationId, UnicastSubject<Notification<*>>> { key, value, cause ->
|
val onObservableRemove = RemovalListener<Trace.InvocationId, UnicastSubject<Notification<*>>> { key, _, _ ->
|
||||||
val observableId = key!!
|
val observableId = key!!
|
||||||
|
|
||||||
observablesToReap.locked { observables.add(observableId) }
|
observablesToReap.locked { observables.add(observableId) }
|
||||||
@ -85,7 +84,7 @@ class RoundTripObservableSerializerTests {
|
|||||||
|
|
||||||
|
|
||||||
// What we're actually going to serialize then deserialize
|
// What we're actually going to serialize then deserialize
|
||||||
val obs = Observable.create<Int>({ 12 })
|
val obs = Observable.create<Int> { Math.random() }
|
||||||
|
|
||||||
val serverSerializationContext = RpcServerObservableSerializer.createContext(
|
val serverSerializationContext = RpcServerObservableSerializer.createContext(
|
||||||
serializationContext, serverObservableContext)
|
serializationContext, serverObservableContext)
|
||||||
@ -95,6 +94,6 @@ class RoundTripObservableSerializerTests {
|
|||||||
|
|
||||||
|
|
||||||
val blob = SerializationOutput(serverSerializer).serialize(obs, serverSerializationContext)
|
val blob = SerializationOutput(serverSerializer).serialize(obs, serverSerializationContext)
|
||||||
val obs2 = DeserializationInput(clientSerializer).deserialize(blob, clientSerializationContext)
|
DeserializationInput(clientSerializer).deserialize(blob, clientSerializationContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,12 @@ import net.corda.node.internal.serialization.testutils.TestObservableContext
|
|||||||
import net.corda.node.internal.serialization.testutils.serializationContext
|
import net.corda.node.internal.serialization.testutils.serializationContext
|
||||||
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
|
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
|
||||||
import net.corda.node.services.messaging.ObservableSubscription
|
import net.corda.node.services.messaging.ObservableSubscription
|
||||||
|
import net.corda.serialization.internal.AllWhitelist
|
||||||
import net.corda.serialization.internal.amqp.SerializationOutput
|
import net.corda.serialization.internal.amqp.SerializationOutput
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
import net.corda.serialization.internal.AllWhitelist
|
|
||||||
import org.apache.activemq.artemis.api.core.SimpleString
|
import org.apache.activemq.artemis.api.core.SimpleString
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@ -28,7 +27,7 @@ class RpcServerObservableSerializerTests {
|
|||||||
.maximumSize(100)
|
.maximumSize(100)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
subMap.put(Trace.InvocationId("test1", Instant.now()), ObservableSubscription(mock<Subscription>()))
|
subMap.put(Trace.InvocationId("test1", Instant.now()), ObservableSubscription(mock()))
|
||||||
|
|
||||||
return subMap
|
return subMap
|
||||||
}
|
}
|
||||||
@ -72,7 +71,7 @@ class RpcServerObservableSerializerTests {
|
|||||||
register(RpcServerObservableSerializer())
|
register(RpcServerObservableSerializer())
|
||||||
}
|
}
|
||||||
|
|
||||||
val obs = Observable.create<Int>({ 12 })
|
val obs = Observable.create<Int> { Math.random() }
|
||||||
val newContext = RpcServerObservableSerializer.createContext(serializationContext, observable)
|
val newContext = RpcServerObservableSerializer.createContext(serializationContext, observable)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -10,22 +10,8 @@
|
|||||||
|
|
||||||
package net.corda.node.services.events
|
package net.corda.node.services.events
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.any
|
import com.nhaarman.mockito_kotlin.*
|
||||||
import com.nhaarman.mockito_kotlin.argForWhich
|
import net.corda.core.contracts.*
|
||||||
import com.nhaarman.mockito_kotlin.doAnswer
|
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
|
||||||
import com.nhaarman.mockito_kotlin.eq
|
|
||||||
import com.nhaarman.mockito_kotlin.same
|
|
||||||
import com.nhaarman.mockito_kotlin.timeout
|
|
||||||
import com.nhaarman.mockito_kotlin.verify
|
|
||||||
import com.nhaarman.mockito_kotlin.verifyNoMoreInteractions
|
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
|
||||||
import junit.framework.Assert.fail
|
|
||||||
import net.corda.core.contracts.SchedulableState
|
|
||||||
import net.corda.core.contracts.ScheduledActivity
|
|
||||||
import net.corda.core.contracts.ScheduledStateRef
|
|
||||||
import net.corda.core.contracts.StateRef
|
|
||||||
import net.corda.core.contracts.TransactionState
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.FlowLogicRef
|
import net.corda.core.flows.FlowLogicRef
|
||||||
@ -46,11 +32,7 @@ import net.corda.testing.internal.rigorousMock
|
|||||||
import net.corda.testing.internal.spectator
|
import net.corda.testing.internal.spectator
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.TestClock
|
import net.corda.testing.node.TestClock
|
||||||
import org.junit.After
|
import org.junit.*
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Ignore
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.rules.TestWatcher
|
import org.junit.rules.TestWatcher
|
||||||
import org.junit.runner.Description
|
import org.junit.runner.Description
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
@ -61,6 +43,7 @@ import java.util.*
|
|||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
open class NodeSchedulerServiceTestBase {
|
open class NodeSchedulerServiceTestBase {
|
||||||
protected class Event(time: Instant) {
|
protected class Event(time: Instant) {
|
||||||
@ -97,7 +80,7 @@ open class NodeSchedulerServiceTestBase {
|
|||||||
doLookup(transactionStates).whenever(it).loadState(any())
|
doLookup(transactionStates).whenever(it).loadState(any())
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val traces = Collections.synchronizedList(mutableListOf<ScheduledStateRef>())
|
private val traces = Collections.synchronizedList(mutableListOf<ScheduledStateRef>())
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun resetTraces() {
|
fun resetTraces() {
|
||||||
@ -108,7 +91,7 @@ open class NodeSchedulerServiceTestBase {
|
|||||||
doReturn(false).whenever(it).isTraceEnabled
|
doReturn(false).whenever(it).isTraceEnabled
|
||||||
doAnswer {
|
doAnswer {
|
||||||
traces += it.getArgument<ScheduledStateRef>(1)
|
traces += it.getArgument<ScheduledStateRef>(1)
|
||||||
}.whenever(it).trace(eq(NodeSchedulerService.schedulingAsNextFormat), any<Object>())
|
}.whenever(it).trace(eq(NodeSchedulerService.schedulingAsNextFormat), any<Any>())
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun assertWaitingFor(ssr: ScheduledStateRef) {
|
protected fun assertWaitingFor(ssr: ScheduledStateRef) {
|
||||||
@ -283,7 +266,7 @@ class NodeSchedulerServiceTest : NodeSchedulerServiceTestBase() {
|
|||||||
class NodeSchedulerPersistenceTest : NodeSchedulerServiceTestBase() {
|
class NodeSchedulerPersistenceTest : NodeSchedulerServiceTestBase() {
|
||||||
private val databaseConfig: DatabaseConfig = DatabaseConfig()
|
private val databaseConfig: DatabaseConfig = DatabaseConfig()
|
||||||
|
|
||||||
fun createScheduler(db: CordaPersistence): NodeSchedulerService {
|
private fun createScheduler(db: CordaPersistence): NodeSchedulerService {
|
||||||
return NodeSchedulerService(
|
return NodeSchedulerService(
|
||||||
testClock,
|
testClock,
|
||||||
db,
|
db,
|
||||||
@ -295,7 +278,7 @@ class NodeSchedulerPersistenceTest : NodeSchedulerServiceTestBase() {
|
|||||||
log = log).apply { start() }
|
log = log).apply { start() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun transactionStateMock(logicRef: FlowLogicRef, time: Instant): TransactionState<*> {
|
private fun transactionStateMock(logicRef: FlowLogicRef, time: Instant): TransactionState<*> {
|
||||||
return rigorousMock<TransactionState<SchedulableState>>().also {
|
return rigorousMock<TransactionState<SchedulableState>>().also {
|
||||||
doReturn(rigorousMock<SchedulableState>().also {
|
doReturn(rigorousMock<SchedulableState>().also {
|
||||||
doReturn(ScheduledActivity(logicRef, time)).whenever(it).nextScheduledActivity(any(), any())
|
doReturn(ScheduledActivity(logicRef, time)).whenever(it).nextScheduledActivity(any(), any())
|
||||||
|
@ -81,7 +81,6 @@ class ArtemisMessagingTest {
|
|||||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||||
doReturn(NetworkHostAndPort("0.0.0.0", serverPort)).whenever(it).p2pAddress
|
doReturn(NetworkHostAndPort("0.0.0.0", serverPort)).whenever(it).p2pAddress
|
||||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||||
doReturn(emptyList<CertChainPolicyConfig>()).whenever(it).certificateChainCheckPolicies
|
|
||||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
|
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
|
||||||
doReturn(FlowTimeoutConfiguration(5.seconds, 3, backoffBase = 1.0)).whenever(it).flowTimeout
|
doReturn(FlowTimeoutConfiguration(5.seconds, 3, backoffBase = 1.0)).whenever(it).flowTimeout
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ class DBCheckpointStorageTests {
|
|||||||
override fun call() {}
|
override fun call() {}
|
||||||
}
|
}
|
||||||
val frozenLogic = logic.serialize(context = SerializationDefaults.CHECKPOINT_CONTEXT)
|
val frozenLogic = logic.serialize(context = SerializationDefaults.CHECKPOINT_CONTEXT)
|
||||||
val checkpoint = Checkpoint.create(InvocationContext.shell(), FlowStart.Explicit, logic.javaClass, frozenLogic, ALICE, "", SubFlowVersion.CoreFlow(version)).getOrThrow()
|
val checkpoint = Checkpoint.create(InvocationContext.shell(), FlowStart.Explicit, logic.javaClass, frozenLogic, ALICE, SubFlowVersion.CoreFlow(version)).getOrThrow()
|
||||||
return id to checkpoint.serialize(context = SerializationDefaults.CHECKPOINT_CONTEXT)
|
return id to checkpoint.serialize(context = SerializationDefaults.CHECKPOINT_CONTEXT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,18 +17,14 @@ import net.corda.core.crypto.SignatureMetadata
|
|||||||
import net.corda.core.crypto.TransactionSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
|
||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.*
|
||||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
|
||||||
import net.corda.testing.core.TestIdentity
|
|
||||||
import net.corda.testing.core.dummyCommand
|
|
||||||
import net.corda.testing.internal.LogHelper
|
import net.corda.testing.internal.LogHelper
|
||||||
|
import net.corda.testing.internal.createWireTransaction
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
@ -176,7 +172,7 @@ class DBTransactionStorageTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun newTransaction(): SignedTransaction {
|
private fun newTransaction(): SignedTransaction {
|
||||||
val wtx = WireTransaction(
|
val wtx = createWireTransaction(
|
||||||
inputs = listOf(StateRef(SecureHash.randomSHA256(), 0)),
|
inputs = listOf(StateRef(SecureHash.randomSHA256(), 0)),
|
||||||
attachments = emptyList(),
|
attachments = emptyList(),
|
||||||
outputs = emptyList(),
|
outputs = emptyList(),
|
||||||
@ -184,6 +180,9 @@ class DBTransactionStorageTests {
|
|||||||
notary = DUMMY_NOTARY,
|
notary = DUMMY_NOTARY,
|
||||||
timeWindow = null
|
timeWindow = null
|
||||||
)
|
)
|
||||||
return SignedTransaction(wtx, listOf(TransactionSignature(ByteArray(1), ALICE_PUBKEY, SignatureMetadata(1, Crypto.findSignatureScheme(ALICE_PUBKEY).schemeNumberID))))
|
return SignedTransaction(
|
||||||
|
wtx,
|
||||||
|
listOf(TransactionSignature(ByteArray(1), ALICE_PUBKEY, SignatureMetadata(1, Crypto.findSignatureScheme(ALICE_PUBKEY).schemeNumberID)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import com.nhaarman.mockito_kotlin.*
|
|||||||
import net.corda.core.contracts.Amount
|
import net.corda.core.contracts.Amount
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TransactionState
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
@ -39,13 +38,13 @@ import net.corda.finance.schemas.SampleCashSchemaV3
|
|||||||
import net.corda.finance.schemas.CashSchemaV1
|
import net.corda.finance.schemas.CashSchemaV1
|
||||||
import net.corda.finance.utils.sumCash
|
import net.corda.finance.utils.sumCash
|
||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
|
import net.corda.node.services.api.WritableTransactionStorage
|
||||||
import net.corda.node.services.schema.ContractStateAndRef
|
import net.corda.node.services.schema.ContractStateAndRef
|
||||||
import net.corda.node.services.schema.HibernateObserver
|
import net.corda.node.services.schema.HibernateObserver
|
||||||
import net.corda.node.services.schema.NodeSchemaService
|
import net.corda.node.services.schema.NodeSchemaService
|
||||||
import net.corda.node.services.vault.VaultSchemaV1
|
|
||||||
import net.corda.node.services.api.IdentityServiceInternal
|
|
||||||
import net.corda.node.services.api.WritableTransactionStorage
|
|
||||||
import net.corda.node.services.vault.NodeVaultService
|
import net.corda.node.services.vault.NodeVaultService
|
||||||
|
import net.corda.node.services.vault.VaultSchemaV1
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||||
@ -85,18 +84,18 @@ class HibernateConfigurationTest {
|
|||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
lateinit var services: MockServices
|
lateinit var services: MockServices
|
||||||
private lateinit var vaultFiller: VaultFiller
|
private lateinit var vaultFiller: VaultFiller
|
||||||
lateinit var bankServices: MockServices
|
private lateinit var bankServices: MockServices
|
||||||
lateinit var issuerServices: MockServices
|
private lateinit var issuerServices: MockServices
|
||||||
lateinit var notaryServices: MockServices
|
private lateinit var notaryServices: MockServices
|
||||||
lateinit var database: CordaPersistence
|
lateinit var database: CordaPersistence
|
||||||
val vault: VaultService get() = services.vaultService
|
val vault: VaultService get() = services.vaultService
|
||||||
|
|
||||||
// Hibernate configuration objects
|
// Hibernate configuration objects
|
||||||
lateinit var hibernateConfig: HibernateConfiguration
|
lateinit var hibernateConfig: HibernateConfiguration
|
||||||
lateinit var hibernatePersister: HibernateObserver
|
private lateinit var hibernatePersister: HibernateObserver
|
||||||
lateinit var sessionFactory: SessionFactory
|
private lateinit var sessionFactory: SessionFactory
|
||||||
lateinit var entityManager: EntityManager
|
private lateinit var entityManager: EntityManager
|
||||||
lateinit var criteriaBuilder: CriteriaBuilder
|
private lateinit var criteriaBuilder: CriteriaBuilder
|
||||||
|
|
||||||
// Identities used
|
// Identities used
|
||||||
private lateinit var identity: Party
|
private lateinit var identity: Party
|
||||||
@ -104,7 +103,7 @@ class HibernateConfigurationTest {
|
|||||||
private lateinit var notary: Party
|
private lateinit var notary: Party
|
||||||
|
|
||||||
// test States
|
// test States
|
||||||
lateinit var cashStates: List<StateAndRef<Cash.State>>
|
private lateinit var cashStates: List<StateAndRef<Cash.State>>
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
@ -701,7 +700,7 @@ class HibernateConfigurationTest {
|
|||||||
val queryResults = entityManager.createQuery(criteriaQuery).resultList
|
val queryResults = entityManager.createQuery(criteriaQuery).resultList
|
||||||
|
|
||||||
queryResults.forEach {
|
queryResults.forEach {
|
||||||
val cashState = (services.loadState(toStateRef(it.stateRef!!)) as TransactionState<Cash.State>).data
|
val cashState = services.loadState(toStateRef(it.stateRef!!)).data as Cash.State
|
||||||
println("${it.stateRef} with owner: ${cashState.owner.owningKey.toBase58String()}")
|
println("${it.stateRef} with owner: ${cashState.owner.owningKey.toBase58String()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,7 +784,7 @@ class HibernateConfigurationTest {
|
|||||||
// execute query
|
// execute query
|
||||||
val queryResults = entityManager.createQuery(criteriaQuery).resultList
|
val queryResults = entityManager.createQuery(criteriaQuery).resultList
|
||||||
queryResults.forEach {
|
queryResults.forEach {
|
||||||
val cashState = (services.loadState(toStateRef(it.stateRef!!)) as TransactionState<Cash.State>).data
|
val cashState = services.loadState(toStateRef(it.stateRef!!)).data as Cash.State
|
||||||
println("${it.stateRef} with owner ${cashState.owner.owningKey.toBase58String()} and participants ${cashState.participants.map { it.owningKey.toBase58String() }}")
|
println("${it.stateRef} with owner ${cashState.owner.owningKey.toBase58String()} and participants ${cashState.participants.map { it.owningKey.toBase58String() }}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -924,6 +923,6 @@ class HibernateConfigurationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun toStateRef(pStateRef: PersistentStateRef): StateRef {
|
private fun toStateRef(pStateRef: PersistentStateRef): StateRef {
|
||||||
return StateRef(SecureHash.parse(pStateRef.txId!!), pStateRef.index!!)
|
return StateRef(SecureHash.parse(pStateRef.txId), pStateRef.index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,7 @@ import com.google.common.jimfs.Configuration
|
|||||||
import com.google.common.jimfs.Jimfs
|
import com.google.common.jimfs.Jimfs
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.internal.read
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.internal.readAll
|
|
||||||
import net.corda.core.internal.readFully
|
|
||||||
import net.corda.core.internal.write
|
|
||||||
import net.corda.core.internal.writeLines
|
|
||||||
import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
||||||
import net.corda.core.node.services.vault.AttachmentSort
|
import net.corda.core.node.services.vault.AttachmentSort
|
||||||
import net.corda.core.node.services.vault.Builder
|
import net.corda.core.node.services.vault.Builder
|
||||||
@ -69,7 +65,7 @@ class NodeAttachmentStorageTest {
|
|||||||
fun `insert and retrieve`() {
|
fun `insert and retrieve`() {
|
||||||
val (testJar, expectedHash) = makeTestJar()
|
val (testJar, expectedHash) = makeTestJar()
|
||||||
|
|
||||||
val id = testJar.read { storage.importAttachment(it) }
|
val id = testJar.read { storage.importAttachment(it, "test", null) }
|
||||||
assertEquals(expectedHash, id)
|
assertEquals(expectedHash, id)
|
||||||
|
|
||||||
assertNull(storage.openAttachment(SecureHash.randomSHA256()))
|
assertNull(storage.openAttachment(SecureHash.randomSHA256()))
|
||||||
@ -94,7 +90,7 @@ class NodeAttachmentStorageTest {
|
|||||||
val (testJar, expectedHash) = makeTestJar()
|
val (testJar, expectedHash) = makeTestJar()
|
||||||
val (jarB, hashB) = makeTestJar(listOf(Pair("file", "content")))
|
val (jarB, hashB) = makeTestJar(listOf(Pair("file", "content")))
|
||||||
|
|
||||||
val id = testJar.read { storage.importAttachment(it) }
|
val id = testJar.read { storage.importAttachment(it, "test", null) }
|
||||||
assertEquals(expectedHash, id)
|
assertEquals(expectedHash, id)
|
||||||
|
|
||||||
|
|
||||||
@ -109,7 +105,7 @@ class NodeAttachmentStorageTest {
|
|||||||
|
|
||||||
stream.close()
|
stream.close()
|
||||||
|
|
||||||
val idB = jarB.read { storage.importAttachment(it) }
|
val idB = jarB.read { storage.importAttachment(it, "test", null) }
|
||||||
assertEquals(hashB, idB)
|
assertEquals(hashB, idB)
|
||||||
|
|
||||||
storage.openAttachment(id)!!.openAsJAR().use {
|
storage.openAttachment(id)!!.openAsJAR().use {
|
||||||
@ -129,6 +125,7 @@ class NodeAttachmentStorageTest {
|
|||||||
val (jarB, hashB) = makeTestJar(listOf(Pair("file", "content")))
|
val (jarB, hashB) = makeTestJar(listOf(Pair("file", "content")))
|
||||||
val (jarC, hashC) = makeTestJar(listOf(Pair("magic_file", "magic_content_puff")))
|
val (jarC, hashC) = makeTestJar(listOf(Pair("magic_file", "magic_content_puff")))
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
jarA.read { storage.importAttachment(it) }
|
jarA.read { storage.importAttachment(it) }
|
||||||
jarB.read { storage.importAttachment(it, "uploaderB", "fileB.zip") }
|
jarB.read { storage.importAttachment(it, "uploaderB", "fileB.zip") }
|
||||||
jarC.read { storage.importAttachment(it, "uploaderC", "fileC.zip") }
|
jarC.read { storage.importAttachment(it, "uploaderC", "fileC.zip") }
|
||||||
@ -196,11 +193,11 @@ class NodeAttachmentStorageTest {
|
|||||||
fun `duplicates not allowed`() {
|
fun `duplicates not allowed`() {
|
||||||
val (testJar, _) = makeTestJar()
|
val (testJar, _) = makeTestJar()
|
||||||
testJar.read {
|
testJar.read {
|
||||||
storage.importAttachment(it)
|
storage.importAttachment(it, "test", null)
|
||||||
}
|
}
|
||||||
assertFailsWith<FileAlreadyExistsException> {
|
assertFailsWith<FileAlreadyExistsException> {
|
||||||
testJar.read {
|
testJar.read {
|
||||||
storage.importAttachment(it)
|
storage.importAttachment(it, "test", null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,7 +206,7 @@ class NodeAttachmentStorageTest {
|
|||||||
fun `corrupt entry throws exception`() {
|
fun `corrupt entry throws exception`() {
|
||||||
val (testJar, _) = makeTestJar()
|
val (testJar, _) = makeTestJar()
|
||||||
val id = database.transaction {
|
val id = database.transaction {
|
||||||
val id = testJar.read { storage.importAttachment(it) }
|
val id = testJar.read { storage.importAttachment(it, "test", null) }
|
||||||
|
|
||||||
// Corrupt the file in the store.
|
// Corrupt the file in the store.
|
||||||
val bytes = testJar.readAll()
|
val bytes = testJar.readAll()
|
||||||
@ -237,7 +234,7 @@ class NodeAttachmentStorageTest {
|
|||||||
path.writeLines(listOf("Hey", "there!"))
|
path.writeLines(listOf("Hey", "there!"))
|
||||||
path.read {
|
path.read {
|
||||||
assertFailsWith<IllegalArgumentException>("either empty or not a JAR") {
|
assertFailsWith<IllegalArgumentException>("either empty or not a JAR") {
|
||||||
storage.importAttachment(it)
|
storage.importAttachment(it, "test", null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,7 +246,6 @@ class RunOnceServiceTest {
|
|||||||
assertEquals('X', result.id)
|
assertEquals('X', result.id)
|
||||||
assertEquals("machine1", result.machineName)
|
assertEquals("machine1", result.machineName)
|
||||||
assertEquals("123", result.pid)
|
assertEquals("123", result.pid)
|
||||||
assertTrue(result.timestamp is LocalDateTime)
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,7 +255,6 @@ class RunOnceServiceTest {
|
|||||||
assertEquals('X', result.id)
|
assertEquals('X', result.id)
|
||||||
assertEquals("machine2", result.machineName)
|
assertEquals("machine2", result.machineName)
|
||||||
assertEquals("789", result.pid)
|
assertEquals("789", result.pid)
|
||||||
assertTrue(result.timestamp is LocalDateTime)
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import net.corda.testing.node.MockServices
|
|||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.lang.reflect.Method
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import javax.persistence.*
|
import javax.persistence.*
|
||||||
@ -32,7 +33,6 @@ import java.net.URLClassLoader
|
|||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
class SchemaMigrationTest {
|
class SchemaMigrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -82,7 +82,7 @@ class SchemaMigrationTest {
|
|||||||
|
|
||||||
// check that the file was picked up
|
// check that the file was picked up
|
||||||
val nrOfChangesOnDiscoveredFile = db.dataSource.connection.use {
|
val nrOfChangesOnDiscoveredFile = db.dataSource.connection.use {
|
||||||
it.createStatement().executeQuery("select count(*) from DATABASECHANGELOG where filename ='migration/${fileName}'").use { rs ->
|
it.createStatement().executeQuery("select count(*) from DATABASECHANGELOG where filename ='migration/$fileName'").use { rs ->
|
||||||
rs.next()
|
rs.next()
|
||||||
rs.getInt(1)
|
rs.getInt(1)
|
||||||
}
|
}
|
||||||
@ -102,9 +102,11 @@ class SchemaMigrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//hacky way to add a folder to the classpath
|
//hacky way to add a folder to the classpath
|
||||||
fun addToClassPath(file: Path) = URLClassLoader::class.java.getDeclaredMethod("addURL", URL::class.java).apply {
|
private fun addToClassPath(file: Path): Method {
|
||||||
isAccessible = true
|
return URLClassLoader::class.java.getDeclaredMethod("addURL", URL::class.java).apply {
|
||||||
invoke(ClassLoader.getSystemClassLoader(), file.toFile().toURL())
|
isAccessible = true
|
||||||
|
invoke(ClassLoader.getSystemClassLoader(), file.toUri().toURL())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object DummyTestSchema
|
object DummyTestSchema
|
||||||
@ -116,9 +118,9 @@ class SchemaMigrationTest {
|
|||||||
|
|
||||||
@ElementCollection
|
@ElementCollection
|
||||||
@Column(name = "participants")
|
@Column(name = "participants")
|
||||||
@CollectionTable(name = "dummy_test_states_parts", joinColumns = arrayOf(
|
@CollectionTable(name = "dummy_test_states_parts", joinColumns = [
|
||||||
JoinColumn(name = "output_index", referencedColumnName = "output_index"),
|
JoinColumn(name = "output_index", referencedColumnName = "output_index"),
|
||||||
JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id")))
|
JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id")])
|
||||||
override var participants: MutableSet<AbstractParty>? = null,
|
override var participants: MutableSet<AbstractParty>? = null,
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
|
@ -15,6 +15,7 @@ import net.corda.core.crypto.SecureHash
|
|||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.InputStreamAndHash
|
import net.corda.core.internal.InputStreamAndHash
|
||||||
|
import net.corda.core.node.services.AttachmentId
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
@ -31,6 +32,7 @@ import org.assertj.core.api.Assertions.assertThat
|
|||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.io.InputStream
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
@ -67,10 +69,10 @@ class MaxTransactionSizeTests {
|
|||||||
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 2)
|
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 2)
|
||||||
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 3)
|
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 3)
|
||||||
val flow = aliceNode.transaction {
|
val flow = aliceNode.transaction {
|
||||||
val hash1 = aliceNode.services.attachments.importAttachment(bigFile1.inputStream)
|
val hash1 = aliceNode.importAttachment(bigFile1.inputStream)
|
||||||
val hash2 = aliceNode.services.attachments.importAttachment(bigFile2.inputStream)
|
val hash2 = aliceNode.importAttachment(bigFile2.inputStream)
|
||||||
val hash3 = aliceNode.services.attachments.importAttachment(bigFile3.inputStream)
|
val hash3 = aliceNode.importAttachment(bigFile3.inputStream)
|
||||||
val hash4 = aliceNode.services.attachments.importAttachment(bigFile4.inputStream)
|
val hash4 = aliceNode.importAttachment(bigFile4.inputStream)
|
||||||
assertEquals(hash1, bigFile1.sha256)
|
assertEquals(hash1, bigFile1.sha256)
|
||||||
SendLargeTransactionFlow(notary, bob, hash1, hash2, hash3, hash4)
|
SendLargeTransactionFlow(notary, bob, hash1, hash2, hash3, hash4)
|
||||||
}
|
}
|
||||||
@ -90,10 +92,10 @@ class MaxTransactionSizeTests {
|
|||||||
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 2)
|
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 2)
|
||||||
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 3)
|
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 3)
|
||||||
val flow = aliceNode.transaction {
|
val flow = aliceNode.transaction {
|
||||||
val hash1 = aliceNode.services.attachments.importAttachment(bigFile1.inputStream)
|
val hash1 = aliceNode.importAttachment(bigFile1.inputStream)
|
||||||
val hash2 = aliceNode.services.attachments.importAttachment(bigFile2.inputStream)
|
val hash2 = aliceNode.importAttachment(bigFile2.inputStream)
|
||||||
val hash3 = aliceNode.services.attachments.importAttachment(bigFile3.inputStream)
|
val hash3 = aliceNode.importAttachment(bigFile3.inputStream)
|
||||||
val hash4 = aliceNode.services.attachments.importAttachment(bigFile4.inputStream)
|
val hash4 = aliceNode.importAttachment(bigFile4.inputStream)
|
||||||
assertEquals(hash1, bigFile1.sha256)
|
assertEquals(hash1, bigFile1.sha256)
|
||||||
SendLargeTransactionFlow(notary, bob, hash1, hash2, hash3, hash4, verify = false)
|
SendLargeTransactionFlow(notary, bob, hash1, hash2, hash3, hash4, verify = false)
|
||||||
}
|
}
|
||||||
@ -104,6 +106,10 @@ class MaxTransactionSizeTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun StartedMockNode.importAttachment(inputStream: InputStream): AttachmentId {
|
||||||
|
return services.attachments.importAttachment(inputStream, "test", null)
|
||||||
|
}
|
||||||
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
@InitiatingFlow
|
@InitiatingFlow
|
||||||
class SendLargeTransactionFlow(private val notary: Party,
|
class SendLargeTransactionFlow(private val notary: Party,
|
||||||
|
@ -51,7 +51,6 @@ import org.junit.Test
|
|||||||
import org.junit.rules.ExpectedException
|
import org.junit.rules.ExpectedException
|
||||||
import org.junit.rules.ExternalResource
|
import org.junit.rules.ExternalResource
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
@ -113,7 +112,7 @@ open class VaultQueryTestRule : ExternalResource(), VaultQueryParties {
|
|||||||
override val bob = TestIdentity(BOB_NAME, 80)
|
override val bob = TestIdentity(BOB_NAME, 80)
|
||||||
override val cashNotary = TestIdentity(CordaX500Name("Cash Notary Service", "Zurich", "CH"), 21)
|
override val cashNotary = TestIdentity(CordaX500Name("Cash Notary Service", "Zurich", "CH"), 21)
|
||||||
override val charlie = TestIdentity(CHARLIE_NAME, 90)
|
override val charlie = TestIdentity(CHARLIE_NAME, 90)
|
||||||
override val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10)
|
final override val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10)
|
||||||
override val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1)
|
override val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1)
|
||||||
override val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20)
|
override val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20)
|
||||||
override val DUMMY_OBLIGATION_ISSUER = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).party
|
override val DUMMY_OBLIGATION_ISSUER = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).party
|
||||||
@ -143,7 +142,7 @@ open class VaultQueryTestRule : ExternalResource(), VaultQueryParties {
|
|||||||
cordappPackages,
|
cordappPackages,
|
||||||
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity),
|
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity),
|
||||||
megaCorp,
|
megaCorp,
|
||||||
moreKeys = DUMMY_NOTARY_KEY)
|
moreKeys = *arrayOf(DUMMY_NOTARY_KEY))
|
||||||
database = databaseAndServices.first
|
database = databaseAndServices.first
|
||||||
services = databaseAndServices.second
|
services = databaseAndServices.second
|
||||||
vaultFiller = VaultFiller(services, dummyNotary)
|
vaultFiller = VaultFiller(services, dummyNotary)
|
||||||
@ -161,7 +160,7 @@ open class VaultQueryTestRule : ExternalResource(), VaultQueryParties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VaultQueryRollbackRule(val vaultQueryParties: VaultQueryParties) : ExternalResource() {
|
class VaultQueryRollbackRule(private val vaultQueryParties: VaultQueryParties) : ExternalResource() {
|
||||||
|
|
||||||
lateinit var transaction: DatabaseTransaction
|
lateinit var transaction: DatabaseTransaction
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ class VaultWithCashTest {
|
|||||||
private val servicesKey = generateKeyPair()
|
private val servicesKey = generateKeyPair()
|
||||||
lateinit var services: MockServices
|
lateinit var services: MockServices
|
||||||
private lateinit var vaultFiller: VaultFiller
|
private lateinit var vaultFiller: VaultFiller
|
||||||
lateinit var issuerServices: MockServices
|
private lateinit var issuerServices: MockServices
|
||||||
val vaultService: VaultService get() = services.vaultService
|
val vaultService: VaultService get() = services.vaultService
|
||||||
lateinit var database: CordaPersistence
|
lateinit var database: CordaPersistence
|
||||||
private lateinit var notaryServices: MockServices
|
private lateinit var notaryServices: MockServices
|
||||||
@ -88,7 +88,7 @@ class VaultWithCashTest {
|
|||||||
cordappPackages,
|
cordappPackages,
|
||||||
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity),
|
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity),
|
||||||
TestIdentity(MEGA_CORP.name, servicesKey),
|
TestIdentity(MEGA_CORP.name, servicesKey),
|
||||||
moreKeys = dummyNotary.keyPair)
|
moreKeys = *arrayOf(dummyNotary.keyPair))
|
||||||
database = databaseAndServices.first
|
database = databaseAndServices.first
|
||||||
services = databaseAndServices.second
|
services = databaseAndServices.second
|
||||||
vaultFiller = VaultFiller(services, dummyNotary)
|
vaultFiller = VaultFiller(services, dummyNotary)
|
||||||
@ -141,7 +141,7 @@ class VaultWithCashTest {
|
|||||||
database.transaction {
|
database.transaction {
|
||||||
// A tx that spends our money.
|
// A tx that spends our money.
|
||||||
val spendTXBuilder = TransactionBuilder(DUMMY_NOTARY)
|
val spendTXBuilder = TransactionBuilder(DUMMY_NOTARY)
|
||||||
Cash.generateSpend(services, spendTXBuilder, 80.DOLLARS, BOB)
|
Cash.generateSpend(services, spendTXBuilder, 80.DOLLARS, services.myInfo.legalIdentitiesAndCerts.single(), BOB)
|
||||||
val spendPTX = services.signInitialTransaction(spendTXBuilder, freshKey)
|
val spendPTX = services.signInitialTransaction(spendTXBuilder, freshKey)
|
||||||
notaryServices.addSignature(spendPTX)
|
notaryServices.addSignature(spendPTX)
|
||||||
}
|
}
|
||||||
@ -189,7 +189,7 @@ class VaultWithCashTest {
|
|||||||
val first = backgroundExecutor.fork {
|
val first = backgroundExecutor.fork {
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val txn1Builder = TransactionBuilder(DUMMY_NOTARY)
|
val txn1Builder = TransactionBuilder(DUMMY_NOTARY)
|
||||||
Cash.generateSpend(services, txn1Builder, 60.DOLLARS, BOB)
|
Cash.generateSpend(services, txn1Builder, 60.DOLLARS, services.myInfo.legalIdentitiesAndCerts.single(), BOB)
|
||||||
val ptxn1 = notaryServices.signInitialTransaction(txn1Builder)
|
val ptxn1 = notaryServices.signInitialTransaction(txn1Builder)
|
||||||
val txn1 = services.addSignature(ptxn1, freshKey)
|
val txn1 = services.addSignature(ptxn1, freshKey)
|
||||||
println("txn1: ${txn1.id} spent ${((txn1.tx.outputs[0].data) as Cash.State).amount}")
|
println("txn1: ${txn1.id} spent ${((txn1.tx.outputs[0].data) as Cash.State).amount}")
|
||||||
@ -220,7 +220,7 @@ class VaultWithCashTest {
|
|||||||
val second = backgroundExecutor.fork {
|
val second = backgroundExecutor.fork {
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val txn2Builder = TransactionBuilder(DUMMY_NOTARY)
|
val txn2Builder = TransactionBuilder(DUMMY_NOTARY)
|
||||||
Cash.generateSpend(services, txn2Builder, 80.DOLLARS, BOB)
|
Cash.generateSpend(services, txn2Builder, 80.DOLLARS, services.myInfo.legalIdentitiesAndCerts.single(), BOB)
|
||||||
val ptxn2 = notaryServices.signInitialTransaction(txn2Builder)
|
val ptxn2 = notaryServices.signInitialTransaction(txn2Builder)
|
||||||
val txn2 = services.addSignature(ptxn2, freshKey)
|
val txn2 = services.addSignature(ptxn2, freshKey)
|
||||||
println("txn2: ${txn2.id} spent ${((txn2.tx.outputs[0].data) as Cash.State).amount}")
|
println("txn2: ${txn2.id} spent ${((txn2.tx.outputs[0].data) as Cash.State).amount}")
|
||||||
@ -344,7 +344,7 @@ class VaultWithCashTest {
|
|||||||
database.transaction {
|
database.transaction {
|
||||||
// A tx that spends our money.
|
// A tx that spends our money.
|
||||||
val spendTXBuilder = TransactionBuilder(DUMMY_NOTARY)
|
val spendTXBuilder = TransactionBuilder(DUMMY_NOTARY)
|
||||||
Cash.generateSpend(services, spendTXBuilder, 80.DOLLARS, BOB)
|
Cash.generateSpend(services, spendTXBuilder, 80.DOLLARS, services.myInfo.legalIdentitiesAndCerts.single(), BOB)
|
||||||
val spendPTX = notaryServices.signInitialTransaction(spendTXBuilder)
|
val spendPTX = notaryServices.signInitialTransaction(spendTXBuilder)
|
||||||
val spendTX = services.addSignature(spendPTX, freshKey)
|
val spendTX = services.addSignature(spendPTX, freshKey)
|
||||||
services.recordTransactions(spendTX)
|
services.recordTransactions(spendTX)
|
||||||
|
@ -23,7 +23,6 @@ import rx.Observable
|
|||||||
import rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.fail
|
|
||||||
|
|
||||||
class ObservablesTests {
|
class ObservablesTests {
|
||||||
private fun isInDatabaseTransaction() = contextTransactionOrNull != null
|
private fun isInDatabaseTransaction() = contextTransactionOrNull != null
|
||||||
@ -68,7 +67,7 @@ class ObservablesTests {
|
|||||||
assertThat(secondEvent.get()).isEqualTo(0 to false)
|
assertThat(secondEvent.get()).isEqualTo(0 to false)
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestException : Exception("Synthetic exception for tests") {}
|
class TestException : Exception("Synthetic exception for tests")
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `bufferUntilDatabaseCommit swallows if transaction rolled back`() {
|
fun `bufferUntilDatabaseCommit swallows if transaction rolled back`() {
|
||||||
@ -93,7 +92,6 @@ class ObservablesTests {
|
|||||||
assertThat(secondEvent.isDone).isFalse()
|
assertThat(secondEvent.isDone).isFalse()
|
||||||
throw TestException()
|
throw TestException()
|
||||||
}
|
}
|
||||||
fail("Should not have successfully completed transaction")
|
|
||||||
} catch (e: TestException) {
|
} catch (e: TestException) {
|
||||||
}
|
}
|
||||||
assertThat(secondEvent.isDone).isFalse()
|
assertThat(secondEvent.isDone).isFalse()
|
||||||
@ -125,7 +123,6 @@ class ObservablesTests {
|
|||||||
assertThat(secondEvent.isDone).isFalse()
|
assertThat(secondEvent.isDone).isFalse()
|
||||||
throw TestException()
|
throw TestException()
|
||||||
}
|
}
|
||||||
fail("Should not have successfully completed transaction")
|
|
||||||
} catch (e: TestException) {
|
} catch (e: TestException) {
|
||||||
}
|
}
|
||||||
assertThat(secondEvent.isDone).isTrue()
|
assertThat(secondEvent.isDone).isTrue()
|
||||||
|
@ -40,7 +40,6 @@ class LinearStateBatchNotariseContract : Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun verify(tx: LedgerTransaction) {
|
override fun verify(tx: LedgerTransaction) {
|
||||||
val command = tx.commands.requireSingleCommand<Commands>()
|
tx.commands.requireSingleCommand<Commands>()
|
||||||
val timeWindow: TimeWindow? = tx.timeWindow
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,27 +230,6 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
|
|||||||
return Pair(gathered, gatheredAmount)
|
return Pair(gathered, gatheredAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate an transaction exiting fungible assets from the ledger.
|
|
||||||
*
|
|
||||||
* @param tx transaction builder to add states and commands to.
|
|
||||||
* @param amountIssued the amount to be exited, represented as a quantity of issued currency.
|
|
||||||
* @param assetStates the asset states to take funds from. No checks are done about ownership of these states, it is
|
|
||||||
* the responsibility of the caller to check that they do not attempt to exit funds held by others.
|
|
||||||
* @return the public keys which must sign the transaction for it to be valid.
|
|
||||||
*/
|
|
||||||
@Throws(InsufficientBalanceException::class)
|
|
||||||
@JvmStatic
|
|
||||||
@Deprecated("Replaced with generateExit() which takes in a party to pay change to")
|
|
||||||
fun <S : FungibleAsset<T>, T: Any> generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
|
|
||||||
assetStates: List<StateAndRef<S>>,
|
|
||||||
deriveState: (TransactionState<S>, Amount<Issued<T>>, AbstractParty) -> TransactionState<S>,
|
|
||||||
generateMoveCommand: () -> CommandData,
|
|
||||||
generateExitCommand: (Amount<Issued<T>>) -> CommandData): Set<PublicKey> {
|
|
||||||
val owner = assetStates.map { it.state.data.owner }.toSet().firstOrNull() ?: throw InsufficientBalanceException(amountIssued)
|
|
||||||
return generateExit(tx, amountIssued, assetStates, owner, deriveState, generateMoveCommand, generateExitCommand)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate an transaction exiting fungible assets from the ledger.
|
* Generate an transaction exiting fungible assets from the ledger.
|
||||||
*
|
*
|
||||||
@ -323,31 +302,6 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
|
|||||||
|
|
||||||
abstract fun extractCommands(commands: Collection<CommandWithParties<CommandData>>): Collection<CommandWithParties<C>>
|
abstract fun extractCommands(commands: Collection<CommandWithParties<CommandData>>): Collection<CommandWithParties<C>>
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate an transaction exiting assets from the ledger.
|
|
||||||
*
|
|
||||||
* @param tx transaction builder to add states and commands to.
|
|
||||||
* @param amountIssued the amount to be exited, represented as a quantity of issued currency.
|
|
||||||
* @param assetStates the asset states to take funds from. No checks are done about ownership of these states, it is
|
|
||||||
* the responsibility of the caller to check that they do not exit funds held by others.
|
|
||||||
* @param payChangeTo party to pay any change to; this is normally a confidential identity of the calling
|
|
||||||
* party.
|
|
||||||
* @return the public keys which must sign the transaction for it to be valid.
|
|
||||||
*/
|
|
||||||
@Throws(InsufficientBalanceException::class)
|
|
||||||
@Deprecated("Replaced with generateExit() which takes in a party to pay change to")
|
|
||||||
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
|
|
||||||
assetStates: List<StateAndRef<S>>): Set<PublicKey> {
|
|
||||||
return generateExit(
|
|
||||||
tx,
|
|
||||||
amountIssued,
|
|
||||||
assetStates,
|
|
||||||
deriveState = { state, amount, owner -> deriveState(state, amount, owner) },
|
|
||||||
generateMoveCommand = { -> generateMoveCommand() },
|
|
||||||
generateExitCommand = { amount -> generateExitCommand(amount) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate an transaction exiting assets from the ledger.
|
* Generate an transaction exiting assets from the ledger.
|
||||||
*
|
*
|
||||||
@ -367,8 +321,8 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
|
|||||||
assetStates,
|
assetStates,
|
||||||
payChangeTo,
|
payChangeTo,
|
||||||
deriveState = { state, amount, owner -> deriveState(state, amount, owner) },
|
deriveState = { state, amount, owner -> deriveState(state, amount, owner) },
|
||||||
generateMoveCommand = { -> generateMoveCommand() },
|
generateMoveCommand = { generateMoveCommand() },
|
||||||
generateExitCommand = { amount -> generateExitCommand(amount) }
|
generateExitCommand = { generateExitCommand(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,22 +14,18 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import net.corda.core.contracts.Amount
|
import net.corda.core.contracts.Amount
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TransactionState
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.node.services.StatesNotAvailableException
|
import net.corda.core.node.services.StatesNotAvailableException
|
||||||
import net.corda.core.serialization.SerializationDefaults
|
|
||||||
import net.corda.core.serialization.deserialize
|
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.*
|
||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
||||||
|
import net.corda.core.internal.uncheckedCast
|
||||||
import java.sql.*
|
import java.sql.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
|
||||||
import kotlin.concurrent.withLock
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pluggable interface to allow for different cash selection provider implementations
|
* Pluggable interface to allow for different cash selection provider implementations
|
||||||
@ -41,17 +37,17 @@ import kotlin.concurrent.withLock
|
|||||||
abstract class AbstractCashSelection(private val maxRetries: Int = 8, private val retrySleep: Int = 100,
|
abstract class AbstractCashSelection(private val maxRetries: Int = 8, private val retrySleep: Int = 100,
|
||||||
private val retryCap: Int = 2000) {
|
private val retryCap: Int = 2000) {
|
||||||
companion object {
|
companion object {
|
||||||
val instance = AtomicReference<AbstractCashSelection>()
|
private val instance = AtomicReference<AbstractCashSelection>()
|
||||||
|
|
||||||
fun getInstance(metadata: () -> java.sql.DatabaseMetaData): AbstractCashSelection {
|
fun getInstance(metadata: () -> java.sql.DatabaseMetaData): AbstractCashSelection {
|
||||||
return instance.get() ?: {
|
return instance.get() ?: {
|
||||||
val _metadata = metadata()
|
val metadataResult = metadata()
|
||||||
val cashSelectionAlgos = ServiceLoader.load(AbstractCashSelection::class.java).toList()
|
val cashSelectionAlgos = ServiceLoader.load(AbstractCashSelection::class.java).toList()
|
||||||
val cashSelectionAlgo = cashSelectionAlgos.firstOrNull { it.isCompatible(_metadata) }
|
val cashSelectionAlgo = cashSelectionAlgos.firstOrNull { it.isCompatible(metadataResult) }
|
||||||
cashSelectionAlgo?.let {
|
cashSelectionAlgo?.let {
|
||||||
instance.set(cashSelectionAlgo)
|
instance.set(cashSelectionAlgo)
|
||||||
cashSelectionAlgo
|
cashSelectionAlgo
|
||||||
} ?: throw ClassNotFoundException("\nUnable to load compatible cash selection algorithm implementation for JDBC driver ($_metadata)." +
|
} ?: throw ClassNotFoundException("\nUnable to load compatible cash selection algorithm implementation for JDBC driver ($metadataResult)." +
|
||||||
"\nPlease specify an implementation in META-INF/services/${AbstractCashSelection::class.java}")
|
"\nPlease specify an implementation in META-INF/services/${AbstractCashSelection::class.java}")
|
||||||
}.invoke()
|
}.invoke()
|
||||||
}
|
}
|
||||||
@ -84,7 +80,7 @@ abstract class AbstractCashSelection(private val maxRetries: Int = 8, private va
|
|||||||
abstract fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?,
|
abstract fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?,
|
||||||
onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>, withResultSet: (ResultSet) -> Boolean): Boolean
|
onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>, withResultSet: (ResultSet) -> Boolean): Boolean
|
||||||
|
|
||||||
override abstract fun toString(): String
|
abstract override fun toString(): String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query to gather Cash states that are available and retry if they are temporarily unavailable.
|
* Query to gather Cash states that are available and retry if they are temporarily unavailable.
|
||||||
@ -149,7 +145,7 @@ abstract class AbstractCashSelection(private val maxRetries: Int = 8, private va
|
|||||||
|
|
||||||
if (stateRefs.isNotEmpty()) {
|
if (stateRefs.isNotEmpty()) {
|
||||||
// TODO: future implementation to retrieve contract states from a Vault BLOB store
|
// TODO: future implementation to retrieve contract states from a Vault BLOB store
|
||||||
stateAndRefs.addAll(services.loadStates(stateRefs) as Collection<StateAndRef<Cash.State>>)
|
stateAndRefs.addAll(uncheckedCast(services.loadStates(stateRefs)))
|
||||||
}
|
}
|
||||||
|
|
||||||
val success = stateAndRefs.isNotEmpty() && totalPennies >= amount.quantity
|
val success = stateAndRefs.isNotEmpty() && totalPennies >= amount.quantity
|
||||||
|
@ -13,6 +13,9 @@ package com.r3.corda.enterprise.perftestcordapp.flows
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.cash.selection.AbstractCashSelection
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.cash.selection.AbstractCashSelection
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.FINALISING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.GENERATING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.SIGNING_TX
|
||||||
import com.r3.corda.enterprise.perftestcordapp.issuedBy
|
import com.r3.corda.enterprise.perftestcordapp.issuedBy
|
||||||
import net.corda.core.contracts.Amount
|
import net.corda.core.contracts.Amount
|
||||||
import net.corda.core.contracts.InsufficientBalanceException
|
import net.corda.core.contracts.InsufficientBalanceException
|
||||||
@ -60,10 +63,13 @@ class CashExitFlow(private val amount: Amount<Currency>,
|
|||||||
.getInstance { serviceHub.jdbcSession().metaData }
|
.getInstance { serviceHub.jdbcSession().metaData }
|
||||||
.unconsumedCashStatesForSpending(serviceHub, amount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference))
|
.unconsumedCashStatesForSpending(serviceHub, amount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference))
|
||||||
val signers = try {
|
val signers = try {
|
||||||
|
val changeOwner = exitStates.map { it.state.data.owner }.toSet().firstOrNull() ?: throw InsufficientBalanceException(amount)
|
||||||
Cash().generateExit(
|
Cash().generateExit(
|
||||||
builder,
|
builder,
|
||||||
amount.issuedBy(issuer),
|
amount.issuedBy(issuer),
|
||||||
exitStates)
|
exitStates,
|
||||||
|
changeOwner
|
||||||
|
)
|
||||||
} catch (e: InsufficientBalanceException) {
|
} catch (e: InsufficientBalanceException) {
|
||||||
throw CashException("Exiting more cash than exists", e)
|
throw CashException("Exiting more cash than exists", e)
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,10 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.OnLedgerAsset
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.OnLedgerAsset
|
||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.PartyAndAmount
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.PartyAndAmount
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.FINALISING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.GENERATING_ID
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.GENERATING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.SIGNING_TX
|
||||||
import net.corda.confidential.SwapIdentitiesFlow
|
import net.corda.confidential.SwapIdentitiesFlow
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.flows.FlowException
|
import net.corda.core.flows.FlowException
|
||||||
@ -23,6 +27,7 @@ import net.corda.core.flows.StartableByRPC
|
|||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
@ -54,7 +59,7 @@ class CashIssueAndDoublePayment(val amount: Amount<Currency>,
|
|||||||
= txState.copy(data = txState.data.copy(amount = amt, owner = owner))
|
= txState.copy(data = txState.data.copy(amount = amt, owner = owner))
|
||||||
|
|
||||||
val issueResult = subFlow(CashIssueFlow(amount, issueRef, notary))
|
val issueResult = subFlow(CashIssueFlow(amount, issueRef, notary))
|
||||||
val cashStateAndRef = serviceHub.loadStates(setOf(StateRef(issueResult.id, 0))).single() as StateAndRef<Cash.State>
|
val cashStateAndRef: StateAndRef<Cash.State> = uncheckedCast(serviceHub.loadStates(setOf(StateRef(issueResult.id, 0))).single())
|
||||||
|
|
||||||
progressTracker.currentStep = GENERATING_ID
|
progressTracker.currentStep = GENERATING_ID
|
||||||
val txIdentities = if (anonymous) {
|
val txIdentities = if (anonymous) {
|
||||||
@ -86,7 +91,7 @@ class CashIssueAndDoublePayment(val amount: Amount<Currency>,
|
|||||||
progressTracker.currentStep = FINALISING_TX
|
progressTracker.currentStep = FINALISING_TX
|
||||||
val notarised1 = finaliseTx(tx1, setOf(recipient), "Unable to notarise spend first time")
|
val notarised1 = finaliseTx(tx1, setOf(recipient), "Unable to notarise spend first time")
|
||||||
try {
|
try {
|
||||||
val notarised2 = finaliseTx(tx2, setOf(recipient), "Unable to notarise spend second time")
|
finaliseTx(tx2, setOf(recipient), "Unable to notarise spend second time")
|
||||||
} catch (expected: CashException) {
|
} catch (expected: CashException) {
|
||||||
val cause = expected.cause
|
val cause = expected.cause
|
||||||
if (cause is NotaryException) {
|
if (cause is NotaryException) {
|
||||||
|
@ -14,12 +14,17 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.OnLedgerAsset
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.OnLedgerAsset
|
||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.PartyAndAmount
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.PartyAndAmount
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.FINALISING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.GENERATING_ID
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.GENERATING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.SIGNING_TX
|
||||||
import net.corda.confidential.SwapIdentitiesFlow
|
import net.corda.confidential.SwapIdentitiesFlow
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
@ -51,7 +56,7 @@ class CashIssueAndDuplicatePayment(val amount: Amount<Currency>,
|
|||||||
= txState.copy(data = txState.data.copy(amount = amt, owner = owner))
|
= txState.copy(data = txState.data.copy(amount = amt, owner = owner))
|
||||||
|
|
||||||
val issueResult = subFlow(CashIssueFlow(amount, issueRef, notary))
|
val issueResult = subFlow(CashIssueFlow(amount, issueRef, notary))
|
||||||
val cashStateAndRef = serviceHub.loadStates(setOf(StateRef(issueResult.id, 0))).single() as StateAndRef<Cash.State>
|
val cashStateAndRef: StateAndRef<Cash.State> = uncheckedCast(serviceHub.loadStates(setOf(StateRef(issueResult.id, 0))).single())
|
||||||
|
|
||||||
progressTracker.currentStep = GENERATING_ID
|
progressTracker.currentStep = GENERATING_ID
|
||||||
val txIdentities = if (anonymous) {
|
val txIdentities = if (anonymous) {
|
||||||
@ -74,7 +79,7 @@ class CashIssueAndDuplicatePayment(val amount: Amount<Currency>,
|
|||||||
val tx = serviceHub.signInitialTransaction(spendTx, keysForSigning)
|
val tx = serviceHub.signInitialTransaction(spendTx, keysForSigning)
|
||||||
|
|
||||||
progressTracker.currentStep = FINALISING_TX
|
progressTracker.currentStep = FINALISING_TX
|
||||||
val notarised1 = finaliseTx(tx, setOf(recipient), "Unable to notarise spend first time")
|
finaliseTx(tx, setOf(recipient), "Unable to notarise spend first time")
|
||||||
val notarised2 = finaliseTx(tx, setOf(recipient), "Unable to notarise spend second time")
|
val notarised2 = finaliseTx(tx, setOf(recipient), "Unable to notarise spend second time")
|
||||||
|
|
||||||
return Result(notarised2.id, recipient)
|
return Result(notarised2.id, recipient)
|
||||||
|
@ -14,12 +14,17 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.OnLedgerAsset
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.OnLedgerAsset
|
||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.PartyAndAmount
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.PartyAndAmount
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.FINALISING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.GENERATING_ID
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.GENERATING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.SIGNING_TX
|
||||||
import net.corda.confidential.SwapIdentitiesFlow
|
import net.corda.confidential.SwapIdentitiesFlow
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
@ -51,7 +56,7 @@ class CashIssueAndPaymentNoSelection(val amount: Amount<Currency>,
|
|||||||
|
|
||||||
progressTracker.currentStep = GENERATING_TX
|
progressTracker.currentStep = GENERATING_TX
|
||||||
val issueResult = subFlow(CashIssueFlow(amount, issueRef, notary))
|
val issueResult = subFlow(CashIssueFlow(amount, issueRef, notary))
|
||||||
val cashStateAndRef = serviceHub.loadStates(setOf(StateRef(issueResult.id, 0))).single() as StateAndRef<Cash.State>
|
val cashStateAndRef: StateAndRef<Cash.State> = uncheckedCast(serviceHub.loadStates(setOf(StateRef(issueResult.id, 0))).single())
|
||||||
|
|
||||||
progressTracker.currentStep = GENERATING_ID
|
progressTracker.currentStep = GENERATING_ID
|
||||||
val txIdentities = if (anonymous) {
|
val txIdentities = if (anonymous) {
|
||||||
|
@ -12,6 +12,9 @@ package com.r3.corda.enterprise.perftestcordapp.flows
|
|||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.FINALISING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.GENERATING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.SIGNING_TX
|
||||||
import com.r3.corda.enterprise.perftestcordapp.issuedBy
|
import com.r3.corda.enterprise.perftestcordapp.issuedBy
|
||||||
import net.corda.core.contracts.Amount
|
import net.corda.core.contracts.Amount
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
|
@ -12,6 +12,10 @@ package com.r3.corda.enterprise.perftestcordapp.flows
|
|||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.FINALISING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.GENERATING_ID
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.GENERATING_TX
|
||||||
|
import com.r3.corda.enterprise.perftestcordapp.flows.AbstractCashFlow.Companion.SIGNING_TX
|
||||||
import net.corda.confidential.SwapIdentitiesFlow
|
import net.corda.confidential.SwapIdentitiesFlow
|
||||||
import net.corda.core.contracts.Amount
|
import net.corda.core.contracts.Amount
|
||||||
import net.corda.core.contracts.InsufficientBalanceException
|
import net.corda.core.contracts.InsufficientBalanceException
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user