mirror of
https://github.com/corda/corda.git
synced 2024-12-29 09:18:58 +00:00
Merge pull request #93 from corda/christians_os_merge_20171106
Merge OS -> enterprise as of 20171106
This commit is contained in:
commit
fd94732261
@ -2344,66 +2344,6 @@ public class net.corda.core.schemas.MappedSchema extends java.lang.Object
|
|||||||
public final int getVersion()
|
public final int getVersion()
|
||||||
@org.jetbrains.annotations.NotNull public String toString()
|
@org.jetbrains.annotations.NotNull public String toString()
|
||||||
##
|
##
|
||||||
public final class net.corda.core.schemas.NodeInfoSchema extends java.lang.Object
|
|
||||||
public static final net.corda.core.schemas.NodeInfoSchema INSTANCE
|
|
||||||
##
|
|
||||||
public final class net.corda.core.schemas.NodeInfoSchemaV1 extends net.corda.core.schemas.MappedSchema
|
|
||||||
public static final net.corda.core.schemas.NodeInfoSchemaV1 INSTANCE
|
|
||||||
##
|
|
||||||
@javax.persistence.Entity public static final class net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort extends java.lang.Object
|
|
||||||
public <init>()
|
|
||||||
public <init>(net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort)
|
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort copy(net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort)
|
|
||||||
public boolean equals(Object)
|
|
||||||
public int hashCode()
|
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.utilities.NetworkHostAndPort toHostAndPort()
|
|
||||||
public String toString()
|
|
||||||
public static final net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort$Companion Companion
|
|
||||||
##
|
|
||||||
public static final class net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort$Companion extends java.lang.Object
|
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort fromHostAndPort(net.corda.core.utilities.NetworkHostAndPort)
|
|
||||||
##
|
|
||||||
@javax.persistence.Entity @javax.persistence.Table public static final class net.corda.core.schemas.NodeInfoSchemaV1$DBPartyAndCertificate extends java.lang.Object
|
|
||||||
public <init>()
|
|
||||||
public <init>(String, String, byte[], boolean, Set)
|
|
||||||
public <init>(net.corda.core.identity.PartyAndCertificate, boolean)
|
|
||||||
@org.jetbrains.annotations.NotNull public final String component1()
|
|
||||||
@org.jetbrains.annotations.NotNull public final String component2()
|
|
||||||
@org.jetbrains.annotations.NotNull public final byte[] component3()
|
|
||||||
public final boolean component4()
|
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$DBPartyAndCertificate copy(String, String, byte[], boolean, Set)
|
|
||||||
public boolean equals(Object)
|
|
||||||
@org.jetbrains.annotations.NotNull public final String getName()
|
|
||||||
@org.jetbrains.annotations.NotNull public final String getOwningKeyHash()
|
|
||||||
@org.jetbrains.annotations.NotNull public final byte[] getPartyCertBinary()
|
|
||||||
public int hashCode()
|
|
||||||
public final boolean isMain()
|
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.PartyAndCertificate toLegalIdentityAndCert()
|
|
||||||
public String toString()
|
|
||||||
##
|
|
||||||
@javax.persistence.Embeddable public static final class net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort extends java.lang.Object implements java.io.Serializable
|
|
||||||
public <init>()
|
|
||||||
public <init>(String, Integer)
|
|
||||||
@org.jetbrains.annotations.Nullable public final String component1()
|
|
||||||
@org.jetbrains.annotations.Nullable public final Integer component2()
|
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort copy(String, Integer)
|
|
||||||
public boolean equals(Object)
|
|
||||||
@org.jetbrains.annotations.Nullable public final String getHost()
|
|
||||||
@org.jetbrains.annotations.Nullable public final Integer getPort()
|
|
||||||
public int hashCode()
|
|
||||||
public String toString()
|
|
||||||
##
|
|
||||||
@javax.persistence.Entity @javax.persistence.Table public static final class net.corda.core.schemas.NodeInfoSchemaV1$PersistentNodeInfo extends java.lang.Object
|
|
||||||
public <init>()
|
|
||||||
public <init>(int, List, List, int, long)
|
|
||||||
@org.jetbrains.annotations.NotNull public final List getAddresses()
|
|
||||||
public final int getId()
|
|
||||||
@org.jetbrains.annotations.NotNull public final List getLegalIdentitiesAndCerts()
|
|
||||||
public final int getPlatformVersion()
|
|
||||||
public final long getSerial()
|
|
||||||
public final void setId(int)
|
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.NodeInfo toNodeInfo()
|
|
||||||
##
|
|
||||||
@javax.persistence.MappedSuperclass @net.corda.core.serialization.CordaSerializable public class net.corda.core.schemas.PersistentState extends java.lang.Object implements net.corda.core.schemas.StatePersistable
|
@javax.persistence.MappedSuperclass @net.corda.core.serialization.CordaSerializable public class net.corda.core.schemas.PersistentState extends java.lang.Object implements net.corda.core.schemas.StatePersistable
|
||||||
public <init>()
|
public <init>()
|
||||||
public <init>(net.corda.core.schemas.PersistentStateRef)
|
public <init>(net.corda.core.schemas.PersistentStateRef)
|
||||||
|
@ -52,6 +52,10 @@ if [ $abstractCount -gt 0 ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
badChanges=$(($removalCount + $abstractCount))
|
badChanges=$(($removalCount + $abstractCount))
|
||||||
|
if [ $badChanges -gt 255 ]; then
|
||||||
|
echo "OVERFLOW! Number of bad API changes: $badChanges"
|
||||||
|
badChanges=255
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Exiting with exit code" $badChanges
|
echo "Exiting with exit code" $badChanges
|
||||||
exit $badChanges
|
exit $badChanges
|
||||||
|
9
.idea/compiler.xml
generated
9
.idea/compiler.xml
generated
@ -1,7 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel>
|
<bytecodeTargetLevel target="1.8">
|
||||||
|
<module name="api-scanner_main" target="1.8" />
|
||||||
|
<module name="api-scanner_test" target="1.8" />
|
||||||
<module name="attachment-demo_integrationTest" target="1.8" />
|
<module name="attachment-demo_integrationTest" target="1.8" />
|
||||||
<module name="attachment-demo_main" target="1.8" />
|
<module name="attachment-demo_main" target="1.8" />
|
||||||
<module name="attachment-demo_test" target="1.8" />
|
<module name="attachment-demo_test" target="1.8" />
|
||||||
@ -21,7 +23,12 @@
|
|||||||
<module name="cordapp_test" target="1.8" />
|
<module name="cordapp_test" target="1.8" />
|
||||||
<module name="cordform-common_main" target="1.8" />
|
<module name="cordform-common_main" target="1.8" />
|
||||||
<module name="cordform-common_test" target="1.8" />
|
<module name="cordform-common_test" target="1.8" />
|
||||||
|
<module name="cordformation_main" target="1.8" />
|
||||||
|
<module name="cordformation_runnodes" target="1.8" />
|
||||||
|
<module name="cordformation_test" target="1.8" />
|
||||||
|
<module name="core_integrationTest" target="1.8" />
|
||||||
<module name="core_main" target="1.8" />
|
<module name="core_main" target="1.8" />
|
||||||
|
<module name="core_smokeTest" target="1.8" />
|
||||||
<module name="core_test" target="1.8" />
|
<module name="core_test" target="1.8" />
|
||||||
<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" />
|
||||||
|
@ -267,9 +267,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank B,OU=corda,L=London,C=GB"
|
name "O=Bank B,OU=corda,L=London,C=GB"
|
||||||
p2pPort 10007
|
p2pAddress "localhost:10007"
|
||||||
rpcPort 10008
|
rpcAddress "localhost:10008"
|
||||||
webPort 10009
|
webAddress "localhost:10009"
|
||||||
cordapps = []
|
cordapps = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,39 +26,45 @@ import net.corda.finance.USD
|
|||||||
import net.corda.finance.flows.CashExitFlow
|
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.FlowPermissions.Companion.startFlowPermission
|
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||||
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.node.DriverBasedTest
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
|
||||||
class NodeMonitorModelTest : DriverBasedTest() {
|
class NodeMonitorModelTest {
|
||||||
lateinit var aliceNode: NodeInfo
|
private lateinit var aliceNode: NodeInfo
|
||||||
lateinit var bobNode: NodeInfo
|
private lateinit var bobNode: NodeInfo
|
||||||
lateinit var notaryParty: Party
|
private lateinit var notaryParty: Party
|
||||||
|
|
||||||
lateinit var rpc: CordaRPCOps
|
private lateinit var rpc: CordaRPCOps
|
||||||
lateinit var rpcBob: CordaRPCOps
|
private lateinit var rpcBob: CordaRPCOps
|
||||||
lateinit var stateMachineTransactionMapping: Observable<StateMachineTransactionMapping>
|
private lateinit var stateMachineTransactionMapping: Observable<StateMachineTransactionMapping>
|
||||||
lateinit var stateMachineUpdates: Observable<StateMachineUpdate>
|
private lateinit var stateMachineUpdates: Observable<StateMachineUpdate>
|
||||||
lateinit var stateMachineUpdatesBob: Observable<StateMachineUpdate>
|
private lateinit var stateMachineUpdatesBob: Observable<StateMachineUpdate>
|
||||||
lateinit var progressTracking: Observable<ProgressTrackingEvent>
|
private lateinit var progressTracking: Observable<ProgressTrackingEvent>
|
||||||
lateinit var transactions: Observable<SignedTransaction>
|
private lateinit var transactions: Observable<SignedTransaction>
|
||||||
lateinit var vaultUpdates: Observable<Vault.Update<ContractState>>
|
private lateinit var vaultUpdates: Observable<Vault.Update<ContractState>>
|
||||||
lateinit var networkMapUpdates: Observable<NetworkMapCache.MapChange>
|
private lateinit var networkMapUpdates: Observable<NetworkMapCache.MapChange>
|
||||||
lateinit var newNode: (CordaX500Name) -> NodeInfo
|
private lateinit var newNode: (CordaX500Name) -> NodeInfo
|
||||||
|
|
||||||
override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
private fun setup(runTest: () -> Unit) {
|
||||||
|
driver(extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
||||||
val cashUser = User("user1", "test", permissions = setOf(
|
val cashUser = User("user1", "test", permissions = setOf(
|
||||||
startFlowPermission<CashIssueFlow>(),
|
startFlow<CashIssueFlow>(),
|
||||||
startFlowPermission<CashPaymentFlow>(),
|
startFlow<CashPaymentFlow>(),
|
||||||
startFlowPermission<CashExitFlow>())
|
startFlow<CashExitFlow>(),
|
||||||
|
invokeRpc(CordaRPCOps::notaryIdentities),
|
||||||
|
invokeRpc("vaultTrackBy"),
|
||||||
|
invokeRpc("vaultQueryBy"),
|
||||||
|
invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed),
|
||||||
|
invokeRpc(CordaRPCOps::stateMachineRecordedTransactionMappingFeed),
|
||||||
|
invokeRpc(CordaRPCOps::stateMachinesFeed),
|
||||||
|
invokeRpc(CordaRPCOps::networkMapFeed))
|
||||||
)
|
)
|
||||||
val aliceNodeFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser))
|
val aliceNodeHandle = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)).getOrThrow()
|
||||||
val notaryHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow()
|
|
||||||
val aliceNodeHandle = aliceNodeFuture.getOrThrow()
|
|
||||||
aliceNode = aliceNodeHandle.nodeInfo
|
aliceNode = aliceNodeHandle.nodeInfo
|
||||||
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
|
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
|
||||||
val monitor = NodeMonitorModel()
|
val monitor = NodeMonitorModel()
|
||||||
@ -71,7 +77,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
|
|||||||
|
|
||||||
monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
|
monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
|
||||||
rpc = monitor.proxyObservable.value!!
|
rpc = monitor.proxyObservable.value!!
|
||||||
notaryParty = notaryHandle.nodeInfo.legalIdentities[1]
|
notaryParty = defaultNotaryIdentity
|
||||||
|
|
||||||
val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow()
|
val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow()
|
||||||
bobNode = bobNodeHandle.nodeInfo
|
bobNode = bobNodeHandle.nodeInfo
|
||||||
@ -81,9 +87,10 @@ class NodeMonitorModelTest : DriverBasedTest() {
|
|||||||
rpcBob = monitorBob.proxyObservable.value!!
|
rpcBob = monitorBob.proxyObservable.value!!
|
||||||
runTest()
|
runTest()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `network map update`() {
|
fun `network map update`() = setup {
|
||||||
val charlieNode = newNode(CHARLIE.name)
|
val charlieNode = newNode(CHARLIE.name)
|
||||||
val nonServiceIdentities = aliceNode.legalIdentitiesAndCerts + bobNode.legalIdentitiesAndCerts + charlieNode.legalIdentitiesAndCerts
|
val nonServiceIdentities = aliceNode.legalIdentitiesAndCerts + bobNode.legalIdentitiesAndCerts + charlieNode.legalIdentitiesAndCerts
|
||||||
networkMapUpdates.filter { it.node.legalIdentitiesAndCerts.any { it in nonServiceIdentities } }
|
networkMapUpdates.filter { it.node.legalIdentitiesAndCerts.any { it in nonServiceIdentities } }
|
||||||
@ -104,7 +111,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `cash issue works end to end`() {
|
fun `cash issue works end to end`() = setup {
|
||||||
rpc.startFlow(::CashIssueFlow,
|
rpc.startFlow(::CashIssueFlow,
|
||||||
Amount(100, USD),
|
Amount(100, USD),
|
||||||
OpaqueBytes(ByteArray(1, { 1 })),
|
OpaqueBytes(ByteArray(1, { 1 })),
|
||||||
@ -128,7 +135,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `cash issue and move`() {
|
fun `cash issue and move`() = setup {
|
||||||
val (_, issueIdentity) = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryParty).returnValue.getOrThrow()
|
val (_, issueIdentity) = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryParty).returnValue.getOrThrow()
|
||||||
rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.chooseIdentity()).returnValue.getOrThrow()
|
rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.chooseIdentity()).returnValue.getOrThrow()
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ class ContractStateModel {
|
|||||||
return this.map { stateAndRef ->
|
return this.map { stateAndRef ->
|
||||||
if (stateAndRef.state.data is Cash.State) {
|
if (stateAndRef.state.data is Cash.State) {
|
||||||
// Kotlin doesn't unify here for some reason
|
// Kotlin doesn't unify here for some reason
|
||||||
uncheckedCast(stateAndRef)
|
uncheckedCast<StateAndRef<ContractState>, StateAndRef<Cash.State>>(stateAndRef)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import com.google.common.cache.CacheLoader
|
|||||||
import javafx.beans.value.ObservableValue
|
import javafx.beans.value.ObservableValue
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
import javafx.collections.ObservableList
|
import javafx.collections.ObservableList
|
||||||
|
import net.corda.client.jfx.utils.ChosenList
|
||||||
import net.corda.client.jfx.utils.filterNotNull
|
import net.corda.client.jfx.utils.filterNotNull
|
||||||
import net.corda.client.jfx.utils.fold
|
import net.corda.client.jfx.utils.fold
|
||||||
import net.corda.client.jfx.utils.map
|
import net.corda.client.jfx.utils.map
|
||||||
@ -12,7 +13,6 @@ import net.corda.core.identity.AnonymousParty
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.NetworkMapCache.MapChange
|
import net.corda.core.node.services.NetworkMapCache.MapChange
|
||||||
import net.corda.nodeapi.internal.ServiceType
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
class NetworkIdentityModel {
|
class NetworkIdentityModel {
|
||||||
@ -36,16 +36,10 @@ class NetworkIdentityModel {
|
|||||||
.build<PublicKey, ObservableValue<NodeInfo?>>(CacheLoader.from { publicKey ->
|
.build<PublicKey, ObservableValue<NodeInfo?>>(CacheLoader.from { publicKey ->
|
||||||
publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } }
|
publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } }
|
||||||
})
|
})
|
||||||
|
val notaries = ChosenList(rpcProxy.map { FXCollections.observableList(it?.notaryIdentities() ?: emptyList()) })
|
||||||
val notaries: ObservableList<Party> = networkIdentities.map {
|
|
||||||
it.legalIdentitiesAndCerts.find { it.name.commonName?.let { ServiceType.parse(it).isNotary() } == true }
|
|
||||||
}.map { it?.party }.filterNotNull()
|
|
||||||
|
|
||||||
val notaryNodes: ObservableList<NodeInfo> = notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }.filterNotNull()
|
val notaryNodes: ObservableList<NodeInfo> = notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }.filterNotNull()
|
||||||
val parties: ObservableList<NodeInfo> = networkIdentities
|
val parties: ObservableList<NodeInfo> = networkIdentities
|
||||||
.filtered { it.legalIdentities.all { it !in notaries } }
|
.filtered { it.legalIdentities.all { it !in notaries } }
|
||||||
// TODO: REMOVE THIS HACK WHEN NETWORK MAP REDESIGN WORK IS COMPLETED.
|
|
||||||
.filtered { it.legalIdentities.all { it.name.organisation != "Network Map Service" } }
|
|
||||||
val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party }
|
val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party }
|
||||||
|
|
||||||
fun partyFromPublicKey(publicKey: PublicKey): ObservableValue<NodeInfo?> = identityCache[publicKey]
|
fun partyFromPublicKey(publicKey: PublicKey): ObservableValue<NodeInfo?> = identityCache[publicKey]
|
||||||
|
@ -26,7 +26,8 @@ import static java.util.Objects.requireNonNull;
|
|||||||
import static kotlin.test.AssertionsKt.assertEquals;
|
import static kotlin.test.AssertionsKt.assertEquals;
|
||||||
import static net.corda.finance.Currencies.DOLLARS;
|
import static net.corda.finance.Currencies.DOLLARS;
|
||||||
import static net.corda.finance.contracts.GetBalances.getCashBalance;
|
import static net.corda.finance.contracts.GetBalances.getCashBalance;
|
||||||
import static net.corda.node.services.FlowPermissions.startFlowPermission;
|
import static net.corda.node.services.Permissions.invokeRpc;
|
||||||
|
import static net.corda.node.services.Permissions.startFlow;
|
||||||
import static net.corda.testing.TestConstants.getALICE;
|
import static net.corda.testing.TestConstants.getALICE;
|
||||||
|
|
||||||
public class CordaRPCJavaClientTest extends NodeBasedTest {
|
public class CordaRPCJavaClientTest extends NodeBasedTest {
|
||||||
@ -34,7 +35,12 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
|
|||||||
super(Arrays.asList("net.corda.finance.contracts", CashSchemaV1.class.getPackage().getName()));
|
super(Arrays.asList("net.corda.finance.contracts", CashSchemaV1.class.getPackage().getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> perms = Arrays.asList(startFlowPermission(CashPaymentFlow.class), startFlowPermission(CashIssueFlow.class));
|
private List<String> perms = Arrays.asList(
|
||||||
|
startFlow(CashPaymentFlow.class),
|
||||||
|
startFlow(CashIssueFlow.class),
|
||||||
|
invokeRpc("nodeInfo"),
|
||||||
|
invokeRpc("vaultQueryBy"),
|
||||||
|
invokeRpc("vaultQueryByCriteria"));
|
||||||
private Set<String> permSet = new HashSet<>(perms);
|
private Set<String> permSet = new HashSet<>(perms);
|
||||||
private User rpcUser = new User("user1", "test", permSet);
|
private User rpcUser = new User("user1", "test", permSet);
|
||||||
|
|
||||||
|
@ -4,10 +4,7 @@ import net.corda.core.crypto.random63BitValue
|
|||||||
import net.corda.core.flows.FlowInitiator
|
import net.corda.core.flows.FlowInitiator
|
||||||
import net.corda.core.internal.concurrent.flatMap
|
import net.corda.core.internal.concurrent.flatMap
|
||||||
import net.corda.core.internal.packageName
|
import net.corda.core.internal.packageName
|
||||||
import net.corda.core.messaging.FlowProgressHandle
|
import net.corda.core.messaging.*
|
||||||
import net.corda.core.messaging.StateMachineUpdate
|
|
||||||
import net.corda.core.messaging.startFlow
|
|
||||||
import net.corda.core.messaging.startTrackedFlow
|
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
@ -20,7 +17,8 @@ import net.corda.finance.flows.CashPaymentFlow
|
|||||||
import net.corda.finance.schemas.CashSchemaV1
|
import net.corda.finance.schemas.CashSchemaV1
|
||||||
import net.corda.node.internal.Node
|
import net.corda.node.internal.Node
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||||
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
@ -36,9 +34,12 @@ import kotlin.test.assertTrue
|
|||||||
|
|
||||||
class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", CashSchemaV1::class.packageName)) {
|
class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", CashSchemaV1::class.packageName)) {
|
||||||
private val rpcUser = User("user1", "test", permissions = setOf(
|
private val rpcUser = User("user1", "test", permissions = setOf(
|
||||||
startFlowPermission<CashIssueFlow>(),
|
startFlow<CashIssueFlow>(),
|
||||||
startFlowPermission<CashPaymentFlow>()
|
startFlow<CashPaymentFlow>(),
|
||||||
))
|
invokeRpc("vaultQueryBy"),
|
||||||
|
invokeRpc(CordaRPCOps::stateMachinesFeed),
|
||||||
|
invokeRpc("vaultQueryByCriteria"))
|
||||||
|
)
|
||||||
private lateinit var node: StartedNode<Node>
|
private lateinit var node: StartedNode<Node>
|
||||||
private lateinit var client: CordaRPCClient
|
private lateinit var client: CordaRPCClient
|
||||||
private var connection: CordaRPCConnection? = null
|
private var connection: CordaRPCConnection? = null
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.core.messaging.CordaRPCOps
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
|
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
|
||||||
import net.corda.nodeapi.ConnectionDirection
|
import net.corda.nodeapi.ConnectionDirection
|
||||||
|
import net.corda.nodeapi.internal.serialization.AMQP_RPC_CLIENT_CONTEXT
|
||||||
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
|
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|
||||||
|
@ -5,6 +5,11 @@ import net.corda.core.serialization.SerializationContext
|
|||||||
import net.corda.core.serialization.SerializationDefaults
|
import net.corda.core.serialization.SerializationDefaults
|
||||||
import net.corda.core.utilities.ByteSequence
|
import net.corda.core.utilities.ByteSequence
|
||||||
import net.corda.nodeapi.internal.serialization.*
|
import net.corda.nodeapi.internal.serialization.*
|
||||||
|
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.RPCKryo
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
|
class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
|
||||||
|
@ -6,7 +6,7 @@ import net.corda.core.internal.concurrent.openFuture
|
|||||||
import net.corda.core.internal.concurrent.thenMatch
|
import net.corda.core.internal.concurrent.thenMatch
|
||||||
import net.corda.core.messaging.RPCOps
|
import net.corda.core.messaging.RPCOps
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.services.messaging.getRpcContext
|
import net.corda.node.services.messaging.rpcContext
|
||||||
import net.corda.testing.RPCDriverExposedDSLInterface
|
import net.corda.testing.RPCDriverExposedDSLInterface
|
||||||
import net.corda.testing.rpcDriver
|
import net.corda.testing.rpcDriver
|
||||||
import net.corda.testing.rpcTestUser
|
import net.corda.testing.rpcTestUser
|
||||||
@ -65,7 +65,7 @@ class ClientRPCInfrastructureTests : AbstractRPCTest() {
|
|||||||
override fun makeComplicatedObservable() = complicatedObservable
|
override fun makeComplicatedObservable() = complicatedObservable
|
||||||
override fun makeComplicatedListenableFuture() = complicatedListenableFuturee
|
override fun makeComplicatedListenableFuture() = complicatedListenableFuturee
|
||||||
override fun addedLater(): Unit = throw IllegalStateException()
|
override fun addedLater(): Unit = throw IllegalStateException()
|
||||||
override fun captureUser(): String = getRpcContext().currentUser.username
|
override fun captureUser(): String = rpcContext().currentUser.username
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package net.corda.client.rpc
|
package net.corda.client.rpc
|
||||||
|
|
||||||
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.messaging.RPCOps
|
import net.corda.core.messaging.RPCOps
|
||||||
import net.corda.node.services.messaging.getRpcContext
|
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||||
|
import net.corda.node.services.messaging.rpcContext
|
||||||
import net.corda.node.services.messaging.requirePermission
|
import net.corda.node.services.messaging.requirePermission
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.RPCDriverExposedDSLInterface
|
import net.corda.testing.RPCDriverExposedDSLInterface
|
||||||
@ -9,6 +11,8 @@ import net.corda.testing.rpcDriver
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.Parameterized
|
import org.junit.runners.Parameterized
|
||||||
|
import kotlin.reflect.KVisibility
|
||||||
|
import kotlin.reflect.full.declaredMemberFunctions
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
@RunWith(Parameterized::class)
|
@RunWith(Parameterized::class)
|
||||||
@ -28,7 +32,7 @@ class RPCPermissionsTests : AbstractRPCTest() {
|
|||||||
|
|
||||||
class TestOpsImpl : TestOps {
|
class TestOpsImpl : TestOps {
|
||||||
override val protocolVersion = 1
|
override val protocolVersion = 1
|
||||||
override fun validatePermission(str: String) = getRpcContext().requirePermission(str)
|
override fun validatePermission(str: String) { rpcContext().requirePermission(str) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,4 +93,19 @@ class RPCPermissionsTests : AbstractRPCTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `fine grained permissions are enforced`() {
|
||||||
|
val allPermissions = CordaRPCOps::class.declaredMemberFunctions.filter { it.visibility == KVisibility.PUBLIC }.map { invokeRpc(it) }
|
||||||
|
allPermissions.forEach { permission ->
|
||||||
|
rpcDriver {
|
||||||
|
val user = userOf("Mark", setOf(permission))
|
||||||
|
val proxy = testProxyFor(user)
|
||||||
|
|
||||||
|
proxy.validatePermission(permission)
|
||||||
|
(allPermissions - permission).forEach { notOwnedPermission ->
|
||||||
|
assertFailsWith(PermissionException::class, { proxy.validatePermission(notOwnedPermission) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,11 @@ import net.corda.finance.DOLLARS
|
|||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
import net.corda.finance.flows.CashIssueAndPaymentFlow
|
import net.corda.finance.flows.CashIssueAndPaymentFlow
|
||||||
import net.corda.finance.flows.CashPaymentFlow
|
import net.corda.finance.flows.CashPaymentFlow
|
||||||
import net.corda.testing.*
|
import net.corda.testing.ALICE_NAME
|
||||||
|
import net.corda.testing.BOB_NAME
|
||||||
|
import net.corda.testing.CHARLIE_NAME
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
|
import net.corda.testing.singleIdentity
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -24,12 +27,16 @@ import kotlin.test.assertNotNull
|
|||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
class IdentitySyncFlowTests {
|
class IdentitySyncFlowTests {
|
||||||
lateinit var mockNet: MockNetwork
|
private lateinit var mockNet: MockNetwork
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun before() {
|
fun before() {
|
||||||
// We run this in parallel threads to help catch any race conditions that may exist.
|
// We run this in parallel threads to help catch any race conditions that may exist.
|
||||||
mockNet = MockNetwork(networkSendManuallyPumped = false, threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
mockNet = MockNetwork(
|
||||||
|
networkSendManuallyPumped = false,
|
||||||
|
threadPerNode = true,
|
||||||
|
cordappPackages = listOf("net.corda.finance.contracts.asset")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -40,12 +47,11 @@ class IdentitySyncFlowTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `sync confidential identities`() {
|
fun `sync confidential identities`() {
|
||||||
// Set up values we'll need
|
// Set up values we'll need
|
||||||
val notaryNode = mockNet.createNotaryNode()
|
|
||||||
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
||||||
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
||||||
val alice: Party = aliceNode.info.singleIdentity()
|
val alice: Party = aliceNode.info.singleIdentity()
|
||||||
val bob: Party = bobNode.info.singleIdentity()
|
val bob: Party = bobNode.info.singleIdentity()
|
||||||
val notary = notaryNode.services.getDefaultNotary()
|
val notary = mockNet.defaultNotaryIdentity
|
||||||
bobNode.internals.registerInitiatedFlow(Receive::class.java)
|
bobNode.internals.registerInitiatedFlow(Receive::class.java)
|
||||||
|
|
||||||
// Alice issues then pays some cash to a new confidential identity that Bob doesn't know about
|
// Alice issues then pays some cash to a new confidential identity that Bob doesn't know about
|
||||||
@ -70,14 +76,13 @@ class IdentitySyncFlowTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `don't offer other's identities confidential identities`() {
|
fun `don't offer other's identities confidential identities`() {
|
||||||
// Set up values we'll need
|
// Set up values we'll need
|
||||||
val notaryNode = mockNet.createNotaryNode()
|
|
||||||
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
||||||
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
||||||
val charlieNode = mockNet.createPartyNode(CHARLIE_NAME)
|
val charlieNode = mockNet.createPartyNode(CHARLIE_NAME)
|
||||||
val alice: Party = aliceNode.info.singleIdentity()
|
val alice: Party = aliceNode.info.singleIdentity()
|
||||||
val bob: Party = bobNode.info.singleIdentity()
|
val bob: Party = bobNode.info.singleIdentity()
|
||||||
val charlie: Party = charlieNode.info.singleIdentity()
|
val charlie: Party = charlieNode.info.singleIdentity()
|
||||||
val notary = notaryNode.services.getDefaultNotary()
|
val notary = mockNet.defaultNotaryIdentity
|
||||||
bobNode.internals.registerInitiatedFlow(Receive::class.java)
|
bobNode.internals.registerInitiatedFlow(Receive::class.java)
|
||||||
|
|
||||||
// Charlie issues then pays some cash to a new confidential identity
|
// Charlie issues then pays some cash to a new confidential identity
|
||||||
@ -105,7 +110,7 @@ class IdentitySyncFlowTests {
|
|||||||
* Very lightweight wrapping flow to trigger the counterparty flow that receives the identities.
|
* Very lightweight wrapping flow to trigger the counterparty flow that receives the identities.
|
||||||
*/
|
*/
|
||||||
@InitiatingFlow
|
@InitiatingFlow
|
||||||
class Initiator(val otherSide: Party, val tx: WireTransaction): FlowLogic<Boolean>() {
|
class Initiator(private val otherSide: Party, private val tx: WireTransaction): FlowLogic<Boolean>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): Boolean {
|
override fun call(): Boolean {
|
||||||
val session = initiateFlow(otherSide)
|
val session = initiateFlow(otherSide)
|
||||||
@ -116,7 +121,7 @@ class IdentitySyncFlowTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@InitiatedBy(IdentitySyncFlowTests.Initiator::class)
|
@InitiatedBy(IdentitySyncFlowTests.Initiator::class)
|
||||||
class Receive(val otherSideSession: FlowSession): FlowLogic<Unit>() {
|
class Receive(private val otherSideSession: FlowSession): FlowLogic<Unit>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call() {
|
override fun call() {
|
||||||
subFlow(IdentitySyncFlow.Receive(otherSideSession))
|
subFlow(IdentitySyncFlow.Receive(otherSideSession))
|
||||||
|
@ -6,17 +6,22 @@ import net.corda.core.identity.Party
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
class SwapIdentitiesFlowTests {
|
class SwapIdentitiesFlowTests {
|
||||||
|
private lateinit var mockNet: MockNetwork
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
// We run this in parallel threads to help catch any race conditions that may exist.
|
||||||
|
mockNet = MockNetwork(networkSendManuallyPumped = false, threadPerNode = true)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `issue key`() {
|
fun `issue key`() {
|
||||||
// We run this in parallel threads to help catch any race conditions that may exist.
|
|
||||||
val mockNet = MockNetwork(threadPerNode = true)
|
|
||||||
|
|
||||||
// Set up values we'll need
|
// Set up values we'll need
|
||||||
mockNet.createNotaryNode()
|
|
||||||
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||||
val bobNode = mockNet.createPartyNode(BOB.name)
|
val bobNode = mockNet.createPartyNode(BOB.name)
|
||||||
val alice = aliceNode.info.singleIdentity()
|
val alice = aliceNode.info.singleIdentity()
|
||||||
@ -52,19 +57,16 @@ class SwapIdentitiesFlowTests {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun `verifies identity name`() {
|
fun `verifies identity name`() {
|
||||||
// We run this in parallel threads to help catch any race conditions that may exist.
|
|
||||||
val mockNet = MockNetwork(threadPerNode = true)
|
|
||||||
|
|
||||||
// Set up values we'll need
|
// Set up values we'll need
|
||||||
val notaryNode = mockNet.createNotaryNode()
|
|
||||||
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||||
val bobNode = mockNet.createPartyNode(BOB.name)
|
val bobNode = mockNet.createPartyNode(BOB.name)
|
||||||
|
val charlieNode = mockNet.createPartyNode(CHARLIE.name)
|
||||||
val bob: Party = bobNode.services.myInfo.singleIdentity()
|
val bob: Party = bobNode.services.myInfo.singleIdentity()
|
||||||
val notBob = notaryNode.database.transaction {
|
val notBob = charlieNode.database.transaction {
|
||||||
notaryNode.services.keyManagementService.freshKeyAndCert(notaryNode.services.myInfo.chooseIdentityAndCert(), false)
|
charlieNode.services.keyManagementService.freshKeyAndCert(charlieNode.services.myInfo.chooseIdentityAndCert(), false)
|
||||||
}
|
}
|
||||||
val sigData = SwapIdentitiesFlow.buildDataToSign(notBob)
|
val sigData = SwapIdentitiesFlow.buildDataToSign(notBob)
|
||||||
val signature = notaryNode.services.keyManagementService.sign(sigData, notBob.owningKey)
|
val signature = charlieNode.services.keyManagementService.sign(sigData, notBob.owningKey)
|
||||||
assertFailsWith<SwapIdentitiesException>("Certificate subject must match counterparty's well known identity.") {
|
assertFailsWith<SwapIdentitiesException>("Certificate subject must match counterparty's well known identity.") {
|
||||||
SwapIdentitiesFlow.validateAndRegisterIdentity(aliceNode.services.identityService, bob, notBob, signature.withoutKey())
|
SwapIdentitiesFlow.validateAndRegisterIdentity(aliceNode.services.identityService, bob, notBob, signature.withoutKey())
|
||||||
}
|
}
|
||||||
@ -77,11 +79,8 @@ class SwapIdentitiesFlowTests {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun `verifies signature`() {
|
fun `verifies signature`() {
|
||||||
// We run this in parallel threads to help catch any race conditions that may exist.
|
|
||||||
val mockNet = MockNetwork(threadPerNode = true)
|
|
||||||
|
|
||||||
// Set up values we'll need
|
// Set up values we'll need
|
||||||
val notaryNode = mockNet.createNotaryNode()
|
val notaryNode = mockNet.defaultNotaryNode
|
||||||
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||||
val bobNode = mockNet.createPartyNode(BOB.name)
|
val bobNode = mockNet.createPartyNode(BOB.name)
|
||||||
val bob: Party = bobNode.services.myInfo.singleIdentity()
|
val bob: Party = bobNode.services.myInfo.singleIdentity()
|
||||||
|
@ -4,8 +4,4 @@ trustStorePassword : "trustpass"
|
|||||||
p2pAddress : "localhost:10005"
|
p2pAddress : "localhost:10005"
|
||||||
rpcAddress : "localhost:10006"
|
rpcAddress : "localhost:10006"
|
||||||
webAddress : "localhost:10007"
|
webAddress : "localhost:10007"
|
||||||
networkMapService : {
|
|
||||||
address : "localhost:10000"
|
|
||||||
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
|
|
||||||
}
|
|
||||||
useHTTPS : false
|
useHTTPS : false
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
gradlePluginsVersion=2.0.6
|
gradlePluginsVersion=2.0.8
|
||||||
kotlinVersion=1.1.50
|
kotlinVersion=1.1.50
|
||||||
guavaVersion=21.0
|
guavaVersion=21.0
|
||||||
bouncycastleVersion=1.57
|
bouncycastleVersion=1.57
|
||||||
|
@ -7,6 +7,52 @@ apply plugin: 'com.jfrog.artifactory'
|
|||||||
|
|
||||||
description 'Corda core'
|
description 'Corda core'
|
||||||
|
|
||||||
|
evaluationDependsOn(':node:capsule')
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
integrationTestCompile.extendsFrom testCompile
|
||||||
|
integrationTestRuntime.extendsFrom testRuntime
|
||||||
|
|
||||||
|
smokeTestCompile.extendsFrom compile
|
||||||
|
smokeTestRuntime.extendsFrom runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
integrationTest {
|
||||||
|
kotlin {
|
||||||
|
compileClasspath += main.output + test.output
|
||||||
|
runtimeClasspath += main.output + test.output
|
||||||
|
srcDir file('src/integration-test/kotlin')
|
||||||
|
}
|
||||||
|
java {
|
||||||
|
compileClasspath += main.output + test.output
|
||||||
|
runtimeClasspath += main.output + test.output
|
||||||
|
srcDir file('src/integration-test/java')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
smokeTest {
|
||||||
|
kotlin {
|
||||||
|
// We must NOT have any Node code on the classpath, so do NOT
|
||||||
|
// include the test or integrationTest dependencies here.
|
||||||
|
compileClasspath += main.output
|
||||||
|
runtimeClasspath += main.output
|
||||||
|
srcDir file('src/smoke-test/kotlin')
|
||||||
|
}
|
||||||
|
java {
|
||||||
|
compileClasspath += main.output
|
||||||
|
runtimeClasspath += main.output
|
||||||
|
srcDir file('src/smoke-test/java')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processSmokeTestResources {
|
||||||
|
// Bring in the fully built corda.jar for use by NodeFactory in the smoke tests
|
||||||
|
from(project(':node:capsule').tasks['buildCordaJAR']) {
|
||||||
|
rename 'corda-(.*)', 'corda.jar'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -14,7 +60,6 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
testCompile "junit:junit:$junit_version"
|
testCompile "junit:junit:$junit_version"
|
||||||
testCompile "commons-fileupload:commons-fileupload:$fileupload_version"
|
testCompile "commons-fileupload:commons-fileupload:$fileupload_version"
|
||||||
|
|
||||||
@ -48,6 +93,11 @@ dependencies {
|
|||||||
// Guava: Google utilities library.
|
// Guava: Google utilities library.
|
||||||
testCompile "com.google.guava:guava:$guava_version"
|
testCompile "com.google.guava:guava:$guava_version"
|
||||||
|
|
||||||
|
// Smoke tests do NOT have any Node code on the classpath!
|
||||||
|
smokeTestCompile project(':smoke-test-utils')
|
||||||
|
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
|
||||||
|
smokeTestCompile "junit:junit:$junit_version"
|
||||||
|
|
||||||
// RxJava: observable streams of events.
|
// RxJava: observable streams of events.
|
||||||
compile "io.reactivex:rxjava:$rxjava_version"
|
compile "io.reactivex:rxjava:$rxjava_version"
|
||||||
|
|
||||||
@ -87,6 +137,22 @@ task testJar(type: Jar) {
|
|||||||
from sourceSets.test.output
|
from sourceSets.test.output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task integrationTest(type: Test) {
|
||||||
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
|
}
|
||||||
|
|
||||||
|
task smokeTestJar(type: Jar) {
|
||||||
|
classifier 'smokeTests'
|
||||||
|
from sourceSets.smokeTest.output
|
||||||
|
}
|
||||||
|
|
||||||
|
task smokeTest(type: Test) {
|
||||||
|
dependsOn smokeTestJar
|
||||||
|
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
||||||
|
classpath = sourceSets.smokeTest.runtimeClasspath
|
||||||
|
}
|
||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
testArtifacts testJar
|
testArtifacts testJar
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ interface CordaFuture<V> : Future<V> {
|
|||||||
* If the completion thread is problematic for you e.g. deadlock, you can submit to an executor manually.
|
* If the completion thread is problematic for you e.g. deadlock, you can submit to an executor manually.
|
||||||
* If callback fails, its throwable is logged.
|
* If callback fails, its throwable is logged.
|
||||||
*/
|
*/
|
||||||
fun <W> then(callback: (CordaFuture<V>) -> W): Unit
|
fun <W> then(callback: (CordaFuture<V>) -> W)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a new [CompletableFuture] with the same outcome as this Future.
|
* @return a new [CompletableFuture] with the same outcome as this Future.
|
||||||
|
@ -34,7 +34,9 @@ fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature
|
|||||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||||
*/
|
*/
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||||
fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey) = DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes)
|
fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
||||||
|
return DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to sign with a key pair.
|
* Helper function to sign with a key pair.
|
||||||
@ -45,11 +47,11 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey) = DigitalSigna
|
|||||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||||
*/
|
*/
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||||
fun KeyPair.sign(bytesToSign: ByteArray) = private.sign(bytesToSign, public)
|
fun KeyPair.sign(bytesToSign: ByteArray): DigitalSignature.WithKey = private.sign(bytesToSign, public)
|
||||||
|
|
||||||
/** Helper function to sign the bytes of [bytesToSign] with a key pair. */
|
/** Helper function to sign the bytes of [bytesToSign] with a key pair. */
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||||
fun KeyPair.sign(bytesToSign: OpaqueBytes) = sign(bytesToSign.bytes)
|
fun KeyPair.sign(bytesToSign: OpaqueBytes): DigitalSignature.WithKey = sign(bytesToSign.bytes)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for signing a [SignableData] object.
|
* Helper function for signing a [SignableData] object.
|
||||||
|
@ -49,6 +49,7 @@ class NotaryFlow {
|
|||||||
progressTracker.currentStep = REQUESTING
|
progressTracker.currentStep = REQUESTING
|
||||||
|
|
||||||
val notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary")
|
val notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary")
|
||||||
|
check(serviceHub.networkMapCache.isNotary(notaryParty)) { "$notaryParty is not a notary on the network" }
|
||||||
check(stx.inputs.all { stateRef -> serviceHub.loadState(stateRef).notary == notaryParty }) {
|
check(stx.inputs.all { stateRef -> serviceHub.loadState(stateRef).notary == notaryParty }) {
|
||||||
"Input states must have the same Notary"
|
"Input states must have the same Notary"
|
||||||
}
|
}
|
||||||
@ -115,6 +116,9 @@ class NotaryFlow {
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): Void? {
|
override fun call(): Void? {
|
||||||
|
check(serviceHub.myInfo.legalIdentities.any { serviceHub.networkMapCache.isNotary(it) }) {
|
||||||
|
"We are not a notary on the network"
|
||||||
|
}
|
||||||
val (id, inputs, timeWindow, notary) = receiveAndVerifyTx()
|
val (id, inputs, timeWindow, notary) = receiveAndVerifyTx()
|
||||||
checkNotary(notary)
|
checkNotary(notary)
|
||||||
service.validateTimeWindow(timeWindow)
|
service.validateTimeWindow(timeWindow)
|
||||||
@ -135,7 +139,7 @@ class NotaryFlow {
|
|||||||
protected fun checkNotary(notary: Party?) {
|
protected fun checkNotary(notary: Party?) {
|
||||||
// TODO This check implies that it's OK to use the node's main identity. Shouldn't it be just limited to the
|
// TODO This check implies that it's OK to use the node's main identity. Shouldn't it be just limited to the
|
||||||
// notary identities?
|
// notary identities?
|
||||||
if (notary !in serviceHub.myInfo.legalIdentities) {
|
if (notary == null || !serviceHub.myInfo.isLegalIdentity(notary)) {
|
||||||
throw NotaryException(NotaryError.WrongNotary)
|
throw NotaryException(NotaryError.WrongNotary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package net.corda.core.schemas
|
package net.corda.core.internal.schemas
|
||||||
|
|
||||||
import net.corda.core.crypto.toStringShort
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.core.serialization.SerializationDefaults
|
import net.corda.core.serialization.SerializationDefaults
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
@ -100,21 +100,13 @@ interface CordaRPCOps : RPCOps {
|
|||||||
// Java Helpers
|
// Java Helpers
|
||||||
|
|
||||||
// DOCSTART VaultQueryAPIHelpers
|
// DOCSTART VaultQueryAPIHelpers
|
||||||
fun <T : ContractState> vaultQuery(contractStateType: Class<out T>): Vault.Page<T> {
|
fun <T : ContractState> vaultQuery(contractStateType: Class<out T>): Vault.Page<T>
|
||||||
return vaultQueryBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : ContractState> vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class<out T>): Vault.Page<T> {
|
fun <T : ContractState> vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class<out T>): Vault.Page<T>
|
||||||
return vaultQueryBy(criteria, PageSpecification(), Sort(emptySet()), contractStateType)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : ContractState> vaultQueryByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): Vault.Page<T> {
|
fun <T : ContractState> vaultQueryByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): Vault.Page<T>
|
||||||
return vaultQueryBy(criteria, paging, Sort(emptySet()), contractStateType)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : ContractState> vaultQueryByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T> {
|
fun <T : ContractState> vaultQueryByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T>
|
||||||
return vaultQueryBy(criteria, PageSpecification(), sorting, contractStateType)
|
|
||||||
}
|
|
||||||
// DOCEND VaultQueryAPIHelpers
|
// DOCEND VaultQueryAPIHelpers
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,21 +133,13 @@ interface CordaRPCOps : RPCOps {
|
|||||||
// Java Helpers
|
// Java Helpers
|
||||||
|
|
||||||
// DOCSTART VaultTrackAPIHelpers
|
// DOCSTART VaultTrackAPIHelpers
|
||||||
fun <T : ContractState> vaultTrack(contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
fun <T : ContractState> vaultTrack(contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>>
|
||||||
return vaultTrackBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : ContractState> vaultTrackByCriteria(contractStateType: Class<out T>, criteria: QueryCriteria): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
fun <T : ContractState> vaultTrackByCriteria(contractStateType: Class<out T>, criteria: QueryCriteria): DataFeed<Vault.Page<T>, Vault.Update<T>>
|
||||||
return vaultTrackBy(criteria, PageSpecification(), Sort(emptySet()), contractStateType)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : ContractState> vaultTrackByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
fun <T : ContractState> vaultTrackByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): DataFeed<Vault.Page<T>, Vault.Update<T>>
|
||||||
return vaultTrackBy(criteria, paging, Sort(emptySet()), contractStateType)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : ContractState> vaultTrackByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
fun <T : ContractState> vaultTrackByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): DataFeed<Vault.Page<T>, Vault.Update<T>>
|
||||||
return vaultTrackBy(criteria, PageSpecification(), sorting, contractStateType)
|
|
||||||
}
|
|
||||||
// DOCEND VaultTrackAPIHelpers
|
// DOCEND VaultTrackAPIHelpers
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
package net.corda.core.node
|
||||||
|
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.serialization.CordaSerializable
|
||||||
|
import java.time.Duration
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property minimumPlatformVersion
|
||||||
|
* @property notaries
|
||||||
|
* @property eventHorizon
|
||||||
|
* @property maxMessageSize Maximum P2P message sent over the wire in bytes.
|
||||||
|
* @property maxTransactionSize Maximum permitted transaction size in bytes.
|
||||||
|
* @property modifiedTime
|
||||||
|
* @property epoch Version number of the network parameters. Starting from 1, this will always increment on each new set
|
||||||
|
* of parameters.
|
||||||
|
*/
|
||||||
|
// TODO Wire up the parameters
|
||||||
|
@CordaSerializable
|
||||||
|
data class NetworkParameters(
|
||||||
|
val minimumPlatformVersion: Int,
|
||||||
|
val notaries: List<NotaryInfo>,
|
||||||
|
val eventHorizon: Duration,
|
||||||
|
val maxMessageSize: Int,
|
||||||
|
val maxTransactionSize: Int,
|
||||||
|
val modifiedTime: Instant,
|
||||||
|
val epoch: Int
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
require(minimumPlatformVersion > 0) { "minimumPlatformVersion must be at least 1" }
|
||||||
|
require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" }
|
||||||
|
require(epoch > 0) { "epoch must be at least 1" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@CordaSerializable
|
||||||
|
data class NotaryInfo(val identity: Party, val validating: Boolean)
|
@ -52,7 +52,6 @@ interface NetworkMapCacheBase {
|
|||||||
*
|
*
|
||||||
* Note that the identities are sorted based on legal name, and the ordering might change once new notaries are introduced.
|
* Note that the identities are sorted based on legal name, and the ordering might change once new notaries are introduced.
|
||||||
*/
|
*/
|
||||||
// TODO this list will be taken from NetworkParameters distributed by NetworkMap.
|
|
||||||
val notaryIdentities: List<Party>
|
val notaryIdentities: List<Party>
|
||||||
// DOCEND 1
|
// DOCEND 1
|
||||||
|
|
||||||
@ -116,12 +115,15 @@ interface NetworkMapCacheBase {
|
|||||||
fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name }
|
fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name }
|
||||||
// DOCEND 2
|
// DOCEND 2
|
||||||
|
|
||||||
/** Checks whether a given party is an advertised notary identity. */
|
/** Returns true if and only if the given [Party] is a notary, which is defined by the network parameters. */
|
||||||
fun isNotary(party: Party): Boolean = party in notaryIdentities
|
fun isNotary(party: Party): Boolean = party in notaryIdentities
|
||||||
|
|
||||||
/** Checks whether a given party is an validating notary identity. */
|
/**
|
||||||
// TODO This implementation will change after introducing of NetworkParameters.
|
* Returns true if and only if the given [Party] is validating notary. For every party that is a validating notary,
|
||||||
fun isValidatingNotary(party: Party): Boolean = isNotary(party) && "validating" in party.name.commonName!!
|
* [isNotary] is also true.
|
||||||
|
* @see isNotary
|
||||||
|
*/
|
||||||
|
fun isValidatingNotary(party: Party): Boolean
|
||||||
|
|
||||||
/** Clear all network map data from local node cache. */
|
/** Clear all network map data from local node cache. */
|
||||||
fun clearNetworkMapCache()
|
fun clearNetworkMapCache()
|
||||||
|
@ -108,7 +108,7 @@ abstract class TraversableTransaction(open val componentGroups: List<ComponentGr
|
|||||||
* @param groupHashes the roots of the transaction component groups.
|
* @param groupHashes the roots of the transaction component groups.
|
||||||
*/
|
*/
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
class FilteredTransaction private constructor(
|
class FilteredTransaction internal constructor(
|
||||||
override val id: SecureHash,
|
override val id: SecureHash,
|
||||||
val filteredComponentGroups: List<FilteredComponentGroup>,
|
val filteredComponentGroups: List<FilteredComponentGroup>,
|
||||||
val groupHashes: List<SecureHash>
|
val groupHashes: List<SecureHash>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.node
|
package net.corda.core.cordapp
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
@ -11,7 +11,6 @@ import net.corda.core.internal.list
|
|||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.unwrap
|
import net.corda.core.utilities.unwrap
|
||||||
import net.corda.node.internal.cordapp.CordappLoader
|
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.smoketesting.NodeConfig
|
import net.corda.smoketesting.NodeConfig
|
||||||
import net.corda.smoketesting.NodeProcess
|
import net.corda.smoketesting.NodeProcess
|
||||||
@ -23,6 +22,7 @@ import kotlin.streams.toList
|
|||||||
|
|
||||||
class CordappSmokeTest {
|
class CordappSmokeTest {
|
||||||
private companion object {
|
private companion object {
|
||||||
|
private const val CORDAPPS_DIR_NAME = "cordapps"
|
||||||
val user = User("user1", "test", permissions = setOf("ALL"))
|
val user = User("user1", "test", permissions = setOf("ALL"))
|
||||||
val port = AtomicInteger(15100)
|
val port = AtomicInteger(15100)
|
||||||
}
|
}
|
||||||
@ -38,9 +38,10 @@ class CordappSmokeTest {
|
|||||||
users = listOf(user)
|
users = listOf(user)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `FlowContent appName returns the filename of the CorDapp jar`() {
|
fun `FlowContent appName returns the filename of the CorDapp jar`() {
|
||||||
val cordappsDir = (factory.baseDirectory(aliceConfig) / CordappLoader.CORDAPPS_DIR_NAME).createDirectories()
|
val cordappsDir = (factory.baseDirectory(aliceConfig) / CORDAPPS_DIR_NAME).createDirectories()
|
||||||
// Find the jar file for the smoke tests of this module
|
// Find the jar file for the smoke tests of this module
|
||||||
val selfCordapp = Paths.get("build", "libs").list {
|
val selfCordapp = Paths.get("build", "libs").list {
|
||||||
it.filter { "-smokeTests" in it.toString() }.toList().single()
|
it.filter { "-smokeTests" in it.toString() }.toList().single()
|
||||||
@ -61,7 +62,7 @@ class CordappSmokeTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `empty cordapps directory`() {
|
fun `empty cordapps directory`() {
|
||||||
(factory.baseDirectory(aliceConfig) / CordappLoader.CORDAPPS_DIR_NAME).createDirectories()
|
(factory.baseDirectory(aliceConfig) / CORDAPPS_DIR_NAME).createDirectories()
|
||||||
factory.create(aliceConfig).close()
|
factory.create(aliceConfig).close()
|
||||||
}
|
}
|
||||||
|
|
@ -18,14 +18,12 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
|||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class FlowsInJavaTest {
|
public class FlowsInJavaTest {
|
||||||
|
|
||||||
private final MockNetwork mockNet = new MockNetwork();
|
private final MockNetwork mockNet = new MockNetwork();
|
||||||
private StartedNode<MockNetwork.MockNode> aliceNode;
|
private StartedNode<MockNetwork.MockNode> aliceNode;
|
||||||
private StartedNode<MockNetwork.MockNode> bobNode;
|
private StartedNode<MockNetwork.MockNode> bobNode;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
mockNet.createNotaryNode();
|
|
||||||
aliceNode = mockNet.createPartyNode(TestConstants.getALICE().getName());
|
aliceNode = mockNet.createPartyNode(TestConstants.getALICE().getName());
|
||||||
bobNode = mockNet.createPartyNode(TestConstants.getBOB().getName());
|
bobNode = mockNet.createPartyNode(TestConstants.getBOB().getName());
|
||||||
mockNet.runNetwork();
|
mockNet.runNetwork();
|
||||||
|
@ -14,7 +14,6 @@ import net.corda.testing.ALICE
|
|||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import net.corda.testing.node.MockNodeArgs
|
|
||||||
import net.corda.testing.node.MockNodeParameters
|
import net.corda.testing.node.MockNodeParameters
|
||||||
import net.corda.testing.singleIdentity
|
import net.corda.testing.singleIdentity
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
@ -109,15 +108,13 @@ class AttachmentTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `malicious response`() {
|
fun maliciousResponse() {
|
||||||
// Make a node that doesn't do sanity checking at load time.
|
// Make a node that doesn't do sanity checking at load time.
|
||||||
val aliceNode = mockNet.createNotaryNode(MockNodeParameters(legalName = ALICE.name), nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> {
|
val aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE.name), nodeFactory = { args ->
|
||||||
override fun create(args: MockNodeArgs): MockNetwork.MockNode {
|
object : MockNetwork.MockNode(args) {
|
||||||
return object : MockNetwork.MockNode(args) {
|
|
||||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
|
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}, validating = false)
|
|
||||||
val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name))
|
val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name))
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME)
|
val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME)
|
||||||
|
@ -27,19 +27,18 @@ class CollectSignaturesFlowTests {
|
|||||||
private val cordappPackages = listOf("net.corda.testing.contracts")
|
private val cordappPackages = listOf("net.corda.testing.contracts")
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var mockNet: MockNetwork
|
private lateinit var mockNet: MockNetwork
|
||||||
lateinit var aliceNode: StartedNode<MockNetwork.MockNode>
|
private lateinit var aliceNode: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var bobNode: StartedNode<MockNetwork.MockNode>
|
private lateinit var bobNode: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var charlieNode: StartedNode<MockNetwork.MockNode>
|
private lateinit var charlieNode: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var alice: Party
|
private lateinit var alice: Party
|
||||||
lateinit var bob: Party
|
private lateinit var bob: Party
|
||||||
lateinit var charlie: Party
|
private lateinit var charlie: Party
|
||||||
lateinit var notary: Party
|
private lateinit var notary: Party
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
mockNet = MockNetwork(cordappPackages = cordappPackages)
|
mockNet = MockNetwork(cordappPackages = cordappPackages)
|
||||||
val notaryNode = mockNet.createNotaryNode()
|
|
||||||
aliceNode = mockNet.createPartyNode(ALICE.name)
|
aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||||
bobNode = mockNet.createPartyNode(BOB.name)
|
bobNode = mockNet.createPartyNode(BOB.name)
|
||||||
charlieNode = mockNet.createPartyNode(CHARLIE.name)
|
charlieNode = mockNet.createPartyNode(CHARLIE.name)
|
||||||
@ -47,7 +46,7 @@ class CollectSignaturesFlowTests {
|
|||||||
alice = aliceNode.info.singleIdentity()
|
alice = aliceNode.info.singleIdentity()
|
||||||
bob = bobNode.info.singleIdentity()
|
bob = bobNode.info.singleIdentity()
|
||||||
charlie = charlieNode.info.singleIdentity()
|
charlie = charlieNode.info.singleIdentity()
|
||||||
notary = notaryNode.services.getDefaultNotary()
|
notary = mockNet.defaultNotaryIdentity
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -16,9 +16,9 @@ import net.corda.finance.USD
|
|||||||
import net.corda.finance.`issued by`
|
import net.corda.finance.`issued by`
|
||||||
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.node.internal.CordaRPCOpsImpl
|
import net.corda.node.internal.SecureCordaRPCOps
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
@ -41,14 +41,13 @@ class ContractUpgradeFlowTest {
|
|||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows"))
|
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows"))
|
||||||
val notaryNode = mockNet.createNotaryNode()
|
|
||||||
aliceNode = mockNet.createPartyNode(ALICE.name)
|
aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||||
bobNode = mockNet.createPartyNode(BOB.name)
|
bobNode = mockNet.createPartyNode(BOB.name)
|
||||||
|
|
||||||
// Process registration
|
// Process registration
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
|
||||||
notary = notaryNode.services.getDefaultNotary()
|
notary = mockNet.defaultNotaryIdentity
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -118,7 +117,7 @@ class ContractUpgradeFlowTest {
|
|||||||
return startRpcClient<CordaRPCOps>(
|
return startRpcClient<CordaRPCOps>(
|
||||||
rpcAddress = startRpcServer(
|
rpcAddress = startRpcServer(
|
||||||
rpcUser = user,
|
rpcUser = user,
|
||||||
ops = CordaRPCOpsImpl(node.services, node.smm, node.database, node.services)
|
ops = SecureCordaRPCOps(node.services, node.smm, node.database, node.services)
|
||||||
).get().broker.hostAndPort!!,
|
).get().broker.hostAndPort!!,
|
||||||
username = user.username,
|
username = user.username,
|
||||||
password = user.password
|
password = user.password
|
||||||
@ -134,10 +133,10 @@ class ContractUpgradeFlowTest {
|
|||||||
val stx = bobNode.services.addSignature(signedByA)
|
val stx = bobNode.services.addSignature(signedByA)
|
||||||
|
|
||||||
val user = rpcTestUser.copy(permissions = setOf(
|
val user = rpcTestUser.copy(permissions = setOf(
|
||||||
startFlowPermission<FinalityInvoker>(),
|
startFlow<FinalityInvoker>(),
|
||||||
startFlowPermission<ContractUpgradeFlow.Initiate<*, *>>(),
|
startFlow<ContractUpgradeFlow.Initiate<*, *>>(),
|
||||||
startFlowPermission<ContractUpgradeFlow.Authorise>(),
|
startFlow<ContractUpgradeFlow.Authorise>(),
|
||||||
startFlowPermission<ContractUpgradeFlow.Deauthorise>()
|
startFlow<ContractUpgradeFlow.Deauthorise>()
|
||||||
))
|
))
|
||||||
val rpcA = startProxy(aliceNode, user)
|
val rpcA = startProxy(aliceNode, user)
|
||||||
val rpcB = startProxy(bobNode, user)
|
val rpcB = startProxy(bobNode, user)
|
||||||
|
@ -26,7 +26,6 @@ class FinalityFlowTests {
|
|||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||||
val notaryNode = mockNet.createNotaryNode()
|
|
||||||
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
||||||
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
@ -34,7 +33,7 @@ class FinalityFlowTests {
|
|||||||
bobServices = bobNode.services
|
bobServices = bobNode.services
|
||||||
alice = aliceNode.info.singleIdentity()
|
alice = aliceNode.info.singleIdentity()
|
||||||
bob = bobNode.info.singleIdentity()
|
bob = bobNode.info.singleIdentity()
|
||||||
notary = notaryNode.services.getDefaultNotary()
|
notary = aliceServices.getDefaultNotary()
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -13,7 +13,7 @@ import org.junit.Test
|
|||||||
class ReceiveMultipleFlowTests {
|
class ReceiveMultipleFlowTests {
|
||||||
@Test
|
@Test
|
||||||
fun `receive all messages in parallel using map style`() {
|
fun `receive all messages in parallel using map style`() {
|
||||||
network(3) { nodes, _ ->
|
network(3) { nodes ->
|
||||||
val doubleValue = 5.0
|
val doubleValue = 5.0
|
||||||
nodes[1].registerAnswer(AlgorithmDefinition::class, doubleValue)
|
nodes[1].registerAnswer(AlgorithmDefinition::class, doubleValue)
|
||||||
val stringValue = "Thriller"
|
val stringValue = "Thriller"
|
||||||
@ -30,7 +30,7 @@ class ReceiveMultipleFlowTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `receive all messages in parallel using list style`() {
|
fun `receive all messages in parallel using list style`() {
|
||||||
network(3) { nodes, _ ->
|
network(3) { nodes ->
|
||||||
val value1 = 5.0
|
val value1 = 5.0
|
||||||
nodes[1].registerAnswer(ParallelAlgorithmList::class, value1)
|
nodes[1].registerAnswer(ParallelAlgorithmList::class, value1)
|
||||||
val value2 = 6.0
|
val value2 = 6.0
|
||||||
|
@ -8,7 +8,9 @@ import net.corda.core.transactions.SignedTransaction
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.sequence
|
import net.corda.core.utilities.sequence
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.testing.*
|
import net.corda.testing.MEGA_CORP
|
||||||
|
import net.corda.testing.MINI_CORP
|
||||||
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.getDefaultNotary
|
import net.corda.testing.getDefaultNotary
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
@ -26,18 +28,18 @@ import kotlin.test.assertNull
|
|||||||
|
|
||||||
// DOCSTART 3
|
// DOCSTART 3
|
||||||
class ResolveTransactionsFlowTest {
|
class ResolveTransactionsFlowTest {
|
||||||
lateinit var mockNet: MockNetwork
|
private lateinit var mockNet: MockNetwork
|
||||||
lateinit var notaryNode: StartedNode<MockNetwork.MockNode>
|
private lateinit var notaryNode: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var megaCorpNode: StartedNode<MockNetwork.MockNode>
|
private lateinit var megaCorpNode: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var miniCorpNode: StartedNode<MockNetwork.MockNode>
|
private lateinit var miniCorpNode: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var megaCorp: Party
|
private lateinit var megaCorp: Party
|
||||||
lateinit var miniCorp: Party
|
private lateinit var miniCorp: Party
|
||||||
lateinit var notary: Party
|
private lateinit var notary: Party
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
|
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
|
||||||
notaryNode = mockNet.createNotaryNode()
|
notaryNode = mockNet.defaultNotaryNode
|
||||||
megaCorpNode = mockNet.createPartyNode(MEGA_CORP.name)
|
megaCorpNode = mockNet.createPartyNode(MEGA_CORP.name)
|
||||||
miniCorpNode = mockNet.createPartyNode(MINI_CORP.name)
|
miniCorpNode = mockNet.createPartyNode(MINI_CORP.name)
|
||||||
megaCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java)
|
megaCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java)
|
||||||
|
@ -17,7 +17,6 @@ import net.corda.node.services.persistence.NodeAttachmentService
|
|||||||
import net.corda.node.utilities.currentDBSession
|
import net.corda.node.utilities.currentDBSession
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import net.corda.testing.node.MockNodeArgs
|
|
||||||
import net.corda.testing.node.MockNodeParameters
|
import net.corda.testing.node.MockNodeParameters
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -156,12 +155,10 @@ class AttachmentSerializationTest {
|
|||||||
|
|
||||||
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
||||||
client.dispose()
|
client.dispose()
|
||||||
client = mockNet.createNode(MockNodeParameters(client.internals.id), object : MockNetwork.Factory<MockNetwork.MockNode> {
|
client = mockNet.createNode(MockNodeParameters(client.internals.id), { args ->
|
||||||
override fun create(args: MockNodeArgs): MockNetwork.MockNode {
|
object : MockNetwork.MockNode(args) {
|
||||||
return object : MockNetwork.MockNode(args) {
|
|
||||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return (client.smm.allStateMachines[0].stateMachine.resultFuture.apply { mockNet.runNetwork() }.getOrThrow() as ClientResult).attachmentContent
|
return (client.smm.allStateMachines[0].stateMachine.resultFuture.apply { mockNet.runNetwork() }.getOrThrow() as ClientResult).attachmentContent
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.transactions
|
||||||
|
|
||||||
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.ComponentGroupEnum.*
|
import net.corda.core.contracts.ComponentGroupEnum.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.transactions.*
|
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
@ -399,8 +399,7 @@ class CompatibleTransactionTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `FilteredTransaction signer manipulation tests`() {
|
fun `FilteredTransaction signer manipulation tests`() {
|
||||||
// Required to call the private constructor.
|
// Required to call the private constructor.
|
||||||
val ftxConstructor = FilteredTransaction::class.java.declaredConstructors[1]
|
val ftxConstructor = ::FilteredTransaction
|
||||||
ftxConstructor.isAccessible = true
|
|
||||||
|
|
||||||
// 1st and 3rd commands require a signature from KEY_1.
|
// 1st and 3rd commands require a signature from KEY_1.
|
||||||
val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public))
|
val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public))
|
||||||
@ -479,12 +478,12 @@ class CompatibleTransactionTests {
|
|||||||
// A command with no corresponding signer detected
|
// A command with no corresponding signer detected
|
||||||
// because the pointer of CommandData (3rd leaf) cannot find a corresponding (3rd) signer.
|
// because the pointer of CommandData (3rd leaf) cannot find a corresponding (3rd) signer.
|
||||||
val updatedFilteredComponentsNoSignersKey1SamePMT = listOf(key1CommandsFtx.filteredComponentGroups[0], noLastSignerGroupSamePartialTree)
|
val updatedFilteredComponentsNoSignersKey1SamePMT = listOf(key1CommandsFtx.filteredComponentGroups[0], noLastSignerGroupSamePartialTree)
|
||||||
assertFails { ftxConstructor.newInstance(key1CommandsFtx.id, updatedFilteredComponentsNoSignersKey1SamePMT, key1CommandsFtx.groupHashes) }
|
assertFails { ftxConstructor.invoke(key1CommandsFtx.id, updatedFilteredComponentsNoSignersKey1SamePMT, key1CommandsFtx.groupHashes) }
|
||||||
|
|
||||||
// Remove both last signer (KEY1) and related command.
|
// Remove both last signer (KEY1) and related command.
|
||||||
// Update partial Merkle tree for signers.
|
// Update partial Merkle tree for signers.
|
||||||
val updatedFilteredComponentsNoLastCommandAndSigners = listOf(noLastCommandDataGroup, noLastSignerGroup)
|
val updatedFilteredComponentsNoLastCommandAndSigners = listOf(noLastCommandDataGroup, noLastSignerGroup)
|
||||||
val ftxNoLastCommandAndSigners = ftxConstructor.newInstance(key1CommandsFtx.id, updatedFilteredComponentsNoLastCommandAndSigners, key1CommandsFtx.groupHashes) as FilteredTransaction
|
val ftxNoLastCommandAndSigners = ftxConstructor.invoke(key1CommandsFtx.id, updatedFilteredComponentsNoLastCommandAndSigners, key1CommandsFtx.groupHashes) as FilteredTransaction
|
||||||
// verify() will pass as the transaction is well-formed.
|
// verify() will pass as the transaction is well-formed.
|
||||||
ftxNoLastCommandAndSigners.verify()
|
ftxNoLastCommandAndSigners.verify()
|
||||||
// checkCommandVisibility() will not pass, because checkAllComponentsVisible(ComponentGroupEnum.SIGNERS_GROUP) will fail.
|
// checkCommandVisibility() will not pass, because checkAllComponentsVisible(ComponentGroupEnum.SIGNERS_GROUP) will fail.
|
||||||
@ -493,7 +492,7 @@ class CompatibleTransactionTests {
|
|||||||
// Remove last signer for which there is no pointer from a visible commandData. This is the case of Key2.
|
// Remove last signer for which there is no pointer from a visible commandData. This is the case of Key2.
|
||||||
// Do not change partial Merkle tree for signers.
|
// Do not change partial Merkle tree for signers.
|
||||||
// This time the object can be constructed as there is no pointer mismatch.
|
// This time the object can be constructed as there is no pointer mismatch.
|
||||||
val ftxNoLastSigner = ftxConstructor.newInstance(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2SamePMT, key2CommandsFtx.groupHashes) as FilteredTransaction
|
val ftxNoLastSigner = ftxConstructor.invoke(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2SamePMT, key2CommandsFtx.groupHashes) as FilteredTransaction
|
||||||
// verify() will fail as we didn't change the partial Merkle tree.
|
// verify() will fail as we didn't change the partial Merkle tree.
|
||||||
assertFailsWith<FilteredTransactionVerificationException> { ftxNoLastSigner.verify() }
|
assertFailsWith<FilteredTransactionVerificationException> { ftxNoLastSigner.verify() }
|
||||||
// checkCommandVisibility() will not pass.
|
// checkCommandVisibility() will not pass.
|
||||||
@ -501,7 +500,7 @@ class CompatibleTransactionTests {
|
|||||||
|
|
||||||
// Remove last signer for which there is no pointer from a visible commandData. This is the case of Key2.
|
// Remove last signer for which there is no pointer from a visible commandData. This is the case of Key2.
|
||||||
// Update partial Merkle tree for signers.
|
// Update partial Merkle tree for signers.
|
||||||
val ftxNoLastSignerB = ftxConstructor.newInstance(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2, key2CommandsFtx.groupHashes) as FilteredTransaction
|
val ftxNoLastSignerB = ftxConstructor.invoke(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2, key2CommandsFtx.groupHashes) as FilteredTransaction
|
||||||
// verify() will pass, the transaction is well-formed.
|
// verify() will pass, the transaction is well-formed.
|
||||||
ftxNoLastSignerB.verify()
|
ftxNoLastSignerB.verify()
|
||||||
// But, checkAllComponentsVisible() will not pass.
|
// But, checkAllComponentsVisible() will not pass.
|
||||||
@ -526,20 +525,18 @@ class CompatibleTransactionTests {
|
|||||||
val alterFilteredComponents = listOf(key1CommandsFtx.filteredComponentGroups[0], alterSignerGroup)
|
val alterFilteredComponents = listOf(key1CommandsFtx.filteredComponentGroups[0], alterSignerGroup)
|
||||||
|
|
||||||
// Do not update groupHashes.
|
// Do not update groupHashes.
|
||||||
val ftxAlterSigner = ftxConstructor.newInstance(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes) as FilteredTransaction
|
val ftxAlterSigner = ftxConstructor.invoke(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes) as FilteredTransaction
|
||||||
// Visible components in signers group cannot be verified against their partial Merkle tree.
|
// Visible components in signers group cannot be verified against their partial Merkle tree.
|
||||||
assertFailsWith<FilteredTransactionVerificationException> { ftxAlterSigner.verify() }
|
assertFailsWith<FilteredTransactionVerificationException> { ftxAlterSigner.verify() }
|
||||||
// Also, checkAllComponentsVisible() will not pass (groupHash matching will fail).
|
// Also, checkAllComponentsVisible() will not pass (groupHash matching will fail).
|
||||||
assertFailsWith<ComponentVisibilityException> { ftxAlterSigner.checkCommandVisibility(DUMMY_KEY_1.public) }
|
assertFailsWith<ComponentVisibilityException> { ftxAlterSigner.checkCommandVisibility(DUMMY_KEY_1.public) }
|
||||||
|
|
||||||
// Update groupHashes.
|
// Update groupHashes.
|
||||||
val ftxAlterSignerB = ftxConstructor.newInstance(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes.subList(0, 6) + alterMTree.hash) as FilteredTransaction
|
val ftxAlterSignerB = ftxConstructor.invoke(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes.subList(0, 6) + alterMTree.hash) as FilteredTransaction
|
||||||
// Visible components in signers group cannot be verified against their partial Merkle tree.
|
// Visible components in signers group cannot be verified against their partial Merkle tree.
|
||||||
assertFailsWith<FilteredTransactionVerificationException> { ftxAlterSignerB.verify() }
|
assertFailsWith<FilteredTransactionVerificationException> { ftxAlterSignerB.verify() }
|
||||||
// Also, checkAllComponentsVisible() will not pass (top level Merkle tree cannot be verified against transaction's id).
|
// Also, checkAllComponentsVisible() will not pass (top level Merkle tree cannot be verified against transaction's id).
|
||||||
assertFailsWith<ComponentVisibilityException> { ftxAlterSignerB.checkCommandVisibility(DUMMY_KEY_1.public) }
|
assertFailsWith<ComponentVisibilityException> { ftxAlterSignerB.checkCommandVisibility(DUMMY_KEY_1.public) }
|
||||||
|
|
||||||
ftxConstructor.isAccessible = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.transactions
|
||||||
|
|
||||||
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
@ -1,7 +1,9 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.transactions
|
||||||
|
|
||||||
|
import net.corda.core.contracts.Contract
|
||||||
|
import net.corda.core.contracts.ContractState
|
||||||
|
import net.corda.core.contracts.requireThat
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.`issued by`
|
import net.corda.finance.`issued by`
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
@ -12,7 +14,7 @@ import org.junit.Test
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
|
|
||||||
val TEST_TIMELOCK_ID = "net.corda.core.contracts.TransactionEncumbranceTests\$DummyTimeLock"
|
val TEST_TIMELOCK_ID = "net.corda.core.transactions.TransactionEncumbranceTests\$DummyTimeLock"
|
||||||
|
|
||||||
class TransactionEncumbranceTests {
|
class TransactionEncumbranceTests {
|
||||||
val defaultIssuer = MEGA_CORP.ref(1)
|
val defaultIssuer = MEGA_CORP.ref(1)
|
@ -1,11 +1,9 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.transactions
|
||||||
|
|
||||||
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.CompositeKey
|
import net.corda.core.crypto.CompositeKey
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
|
||||||
import net.corda.core.transactions.WireTransaction
|
|
||||||
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY
|
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
@ -3,7 +3,7 @@ Building a Corda Network on Azure Marketplace
|
|||||||
|
|
||||||
To help you design, build and test applications on Corda, called CorDapps, a Corda network can be deployed on the `Microsoft Azure Marketplace <https://azure.microsoft.com/en-gb/overview/what-is-azure>`_
|
To help you design, build and test applications on Corda, called CorDapps, a Corda network can be deployed on the `Microsoft Azure Marketplace <https://azure.microsoft.com/en-gb/overview/what-is-azure>`_
|
||||||
|
|
||||||
This Corda network offering builds a pre-configured network of Corda nodes as Ubuntu virtual machines (VM). The network comprises of a Network Map Service node, a Notary node and up to nine Corda nodes using a version of Corda of your choosing. The following guide will also show you how to load a simple Yo! CorDapp which demonstrates the basic principles of Corda. When you are ready to go further with developing on Corda and start making contributions to the project head over to the `Corda.net <https://www.corda.net/>`_.
|
This Corda network offering builds a pre-configured network of Corda nodes as Ubuntu virtual machines (VM). The network comprises of a Notary node and up to nine Corda nodes using a version of Corda of your choosing. The following guide will also show you how to load a simple Yo! CorDapp which demonstrates the basic principles of Corda. When you are ready to go further with developing on Corda and start making contributions to the project head over to the `Corda.net <https://www.corda.net/>`_.
|
||||||
|
|
||||||
Pre-requisites
|
Pre-requisites
|
||||||
--------------
|
--------------
|
||||||
|
@ -8,6 +8,8 @@ UNRELEASED
|
|||||||
----------
|
----------
|
||||||
* ``ConfigUtilities`` now read system properties for a node. This allow to specify data source properties at runtime.
|
* ``ConfigUtilities`` now read system properties for a node. This allow to specify data source properties at runtime.
|
||||||
|
|
||||||
|
* ``CordaRPCOps`` implementation now checks permissions for any function invocation, rather than just when starting flows.
|
||||||
|
|
||||||
* ``OpaqueBytes.bytes`` now returns a clone of its underlying ``ByteArray``, and has been redeclared as ``final``.
|
* ``OpaqueBytes.bytes`` now returns a clone of its underlying ``ByteArray``, and has been redeclared as ``final``.
|
||||||
This is a minor change to the public API, but is required to ensure that classes like ``SecureHash`` are immutable.
|
This is a minor change to the public API, but is required to ensure that classes like ``SecureHash`` are immutable.
|
||||||
|
|
||||||
@ -72,6 +74,9 @@ UNRELEASED
|
|||||||
* The ``ReceiveTransactionFlow`` can now be told to record the transaction at the same time as receiving it. Using this
|
* The ``ReceiveTransactionFlow`` can now be told to record the transaction at the same time as receiving it. Using this
|
||||||
feature, better support for observer/regulator nodes has been added. See :doc:`tutorial-observer-nodes`.
|
feature, better support for observer/regulator nodes has been added. See :doc:`tutorial-observer-nodes`.
|
||||||
|
|
||||||
|
* Moved ``NodeInfoSchema`` to internal package as the node info's database schema is not part of the public API. This is
|
||||||
|
needed to allow new ``node_info_hash`` column to be added for the network map redesign work.
|
||||||
|
|
||||||
.. _changelog_v1:
|
.. _changelog_v1:
|
||||||
|
|
||||||
Release 1.0
|
Release 1.0
|
||||||
|
@ -41,7 +41,7 @@ The syntax for adding an RPC user is:
|
|||||||
...
|
...
|
||||||
]
|
]
|
||||||
|
|
||||||
Currently, users need special permissions to start flows via RPC. These permissions are added as follows:
|
Users need permissions to invoke any RPC call. By default, nothing is allowed. These permissions are specified as follows:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
@ -62,6 +62,15 @@ Currently, users need special permissions to start flows via RPC. These permissi
|
|||||||
.. note:: Currently, the node's web server has super-user access, meaning that it can run any RPC operation without
|
.. note:: Currently, the node's web server has super-user access, meaning that it can run any RPC operation without
|
||||||
logging in. This will be changed in a future release.
|
logging in. This will be changed in a future release.
|
||||||
|
|
||||||
|
Permissions Syntax
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Fine grained permissions allow a user to invoke a specific RPC operation, or to start a specific flow. The syntax is:
|
||||||
|
|
||||||
|
- to start a specific flow: ``StartFlow.<fully qualified flow name>`` e.g., ``StartFlow.net.corda.flows.ExampleFlow1``.
|
||||||
|
- to invoke a RPC operation: ``InvokeRpc.<rpc method name>`` e.g., ``InvokeRpc.nodeInfo``.
|
||||||
|
.. note:: Permission ``InvokeRpc.startFlow`` allows a user to initiate all flows.
|
||||||
|
|
||||||
Observables
|
Observables
|
||||||
-----------
|
-----------
|
||||||
The RPC system handles observables in a special way. When a method returns an observable, whether directly or
|
The RPC system handles observables in a special way. When a method returns an observable, whether directly or
|
||||||
|
@ -118,10 +118,6 @@ path to the node's base directory.
|
|||||||
|
|
||||||
Only one of ``raft``, ``bftSMaRt`` or ``custom`` configuration values may be specified.
|
Only one of ``raft``, ``bftSMaRt`` or ``custom`` configuration values may be specified.
|
||||||
|
|
||||||
:minimumPlatformVersion: Used by the node if it's running the network map service to enforce a minimum version requirement
|
|
||||||
on registrations - any node on a Platform Version lower than this value will have their registration rejected.
|
|
||||||
Defaults to 1 if absent.
|
|
||||||
|
|
||||||
:useHTTPS: If false the node's web server will be plain HTTP. If true the node will use the same certificate and private
|
:useHTTPS: If false the node's web server will be plain HTTP. If true the node will use the same certificate and private
|
||||||
key from the ``<workspace>/certificates/sslkeystore.jks`` file as the ArtemisMQ port for HTTPS. If HTTPS is enabled
|
key from the ``<workspace>/certificates/sslkeystore.jks`` file as the ArtemisMQ port for HTTPS. If HTTPS is enabled
|
||||||
then unencrypted HTTP traffic to the node's **webAddress** port is not supported.
|
then unencrypted HTTP traffic to the node's **webAddress** port is not supported.
|
||||||
|
@ -16,7 +16,7 @@ Running DemoBench
|
|||||||
Configuring a Node
|
Configuring a Node
|
||||||
Each node must have a unique name to identify it to the network map service. DemoBench will suggest node names, nearest cities and local port numbers to use.
|
Each node must have a unique name to identify it to the network map service. DemoBench will suggest node names, nearest cities and local port numbers to use.
|
||||||
|
|
||||||
The first node will host the network map service, and we are forcing that node also to be a notary. Hence only notary services will be available to be selected in the ``Services`` list. For subsequent nodes you may also select any of Corda's other built-in services.
|
The first node will be a notary. Hence only notary services will be available to be selected in the ``Services`` list. For subsequent nodes you may also select any of Corda's other built-in services.
|
||||||
|
|
||||||
Press the ``Start node`` button to launch the Corda node with your configuration.
|
Press the ``Start node`` button to launch the Corda node with your configuration.
|
||||||
|
|
||||||
|
@ -41,11 +41,12 @@ notary node:
|
|||||||
cordapps = []
|
cordapps = []
|
||||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
||||||
}
|
}
|
||||||
|
// Example of explicit addresses being used.
|
||||||
node {
|
node {
|
||||||
name "CN=NodeC,O=NodeC,L=Paris,C=FR"
|
name "CN=NodeC,O=NodeC,L=Paris,C=FR"
|
||||||
p2pPort 10011
|
p2pAddress "localhost:10011"
|
||||||
rpcPort 10012
|
rpcAddress "localhost:10012"
|
||||||
webPort 10013
|
webAddress "localhost:10013"
|
||||||
cordapps = []
|
cordapps = []
|
||||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.docs
|
package net.corda.docs
|
||||||
|
|
||||||
import net.corda.core.internal.concurrent.transpose
|
import net.corda.core.internal.concurrent.transpose
|
||||||
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.messaging.vaultTrackBy
|
import net.corda.core.messaging.vaultTrackBy
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
@ -10,7 +11,8 @@ import net.corda.finance.DOLLARS
|
|||||||
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
|
||||||
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||||
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
@ -24,17 +26,22 @@ class IntegrationTestingTutorial {
|
|||||||
driver(startNodesInProcess = true,
|
driver(startNodesInProcess = true,
|
||||||
extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset")) {
|
extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset")) {
|
||||||
val aliceUser = User("aliceUser", "testPassword1", permissions = setOf(
|
val aliceUser = User("aliceUser", "testPassword1", permissions = setOf(
|
||||||
startFlowPermission<CashIssueFlow>(),
|
startFlow<CashIssueFlow>(),
|
||||||
startFlowPermission<CashPaymentFlow>()
|
startFlow<CashPaymentFlow>(),
|
||||||
|
invokeRpc("vaultTrackBy"),
|
||||||
|
invokeRpc(CordaRPCOps::notaryIdentities),
|
||||||
|
invokeRpc(CordaRPCOps::networkMapFeed)
|
||||||
))
|
))
|
||||||
val bobUser = User("bobUser", "testPassword2", permissions = setOf(
|
val bobUser = User("bobUser", "testPassword2", permissions = setOf(
|
||||||
startFlowPermission<CashPaymentFlow>()
|
startFlow<CashPaymentFlow>(),
|
||||||
|
invokeRpc("vaultTrackBy"),
|
||||||
|
invokeRpc(CordaRPCOps::networkMapFeed)
|
||||||
))
|
))
|
||||||
val (alice, bob) = listOf(
|
val (alice, bob) = listOf(
|
||||||
startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)),
|
startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)),
|
||||||
startNode(providedName = BOB.name, rpcUsers = listOf(bobUser)),
|
startNode(providedName = BOB.name, rpcUsers = listOf(bobUser))
|
||||||
startNotaryNode(DUMMY_NOTARY.name)
|
|
||||||
).transpose().getOrThrow()
|
).transpose().getOrThrow()
|
||||||
|
|
||||||
// END 1
|
// END 1
|
||||||
|
|
||||||
// START 2
|
// START 2
|
||||||
@ -43,9 +50,6 @@ class IntegrationTestingTutorial {
|
|||||||
|
|
||||||
val bobClient = bob.rpcClientToNode()
|
val bobClient = bob.rpcClientToNode()
|
||||||
val bobProxy = bobClient.start("bobUser", "testPassword2").proxy
|
val bobProxy = bobClient.start("bobUser", "testPassword2").proxy
|
||||||
|
|
||||||
aliceProxy.waitUntilNetworkReady().getOrThrow()
|
|
||||||
bobProxy.waitUntilNetworkReady().getOrThrow()
|
|
||||||
// END 2
|
// END 2
|
||||||
|
|
||||||
// START 3
|
// START 3
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
package net.corda.docs
|
package net.corda.docs
|
||||||
|
|
||||||
import net.corda.core.contracts.Amount
|
import net.corda.core.contracts.Amount
|
||||||
@ -8,16 +10,15 @@ import net.corda.core.serialization.CordaSerializable
|
|||||||
import net.corda.core.serialization.SerializationWhitelist
|
import net.corda.core.serialization.SerializationWhitelist
|
||||||
import net.corda.core.transactions.SignedTransaction
|
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.finance.USD
|
import net.corda.finance.USD
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
import net.corda.finance.flows.CashExitFlow
|
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.FlowPermissions.Companion.startFlowPermission
|
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||||
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import org.graphstream.graph.Edge
|
import org.graphstream.graph.Edge
|
||||||
import org.graphstream.graph.Node
|
import org.graphstream.graph.Node
|
||||||
@ -42,19 +43,18 @@ fun main(args: Array<String>) {
|
|||||||
val printOrVisualise = PrintOrVisualise.valueOf(args[0])
|
val printOrVisualise = PrintOrVisualise.valueOf(args[0])
|
||||||
|
|
||||||
val baseDirectory = Paths.get("build/rpc-api-tutorial")
|
val baseDirectory = Paths.get("build/rpc-api-tutorial")
|
||||||
val user = User("user", "password", permissions = setOf(startFlowPermission<CashIssueFlow>(),
|
val user = User("user", "password", permissions = setOf(startFlow<CashIssueFlow>(),
|
||||||
startFlowPermission<CashPaymentFlow>(),
|
startFlow<CashPaymentFlow>(),
|
||||||
startFlowPermission<CashExitFlow>()))
|
startFlow<CashExitFlow>(),
|
||||||
|
invokeRpc(CordaRPCOps::nodeInfo)
|
||||||
|
))
|
||||||
driver(driverDirectory = baseDirectory, extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
driver(driverDirectory = baseDirectory, extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
||||||
startNotaryNode(DUMMY_NOTARY.name)
|
|
||||||
val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)).get()
|
val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)).get()
|
||||||
// END 1
|
// END 1
|
||||||
|
|
||||||
// START 2
|
// START 2
|
||||||
val client = node.rpcClientToNode()
|
val client = node.rpcClientToNode()
|
||||||
val proxy = client.start("user", "password").proxy
|
val proxy = client.start("user", "password").proxy
|
||||||
proxy.waitUntilNetworkReady().getOrThrow()
|
|
||||||
|
|
||||||
thread {
|
thread {
|
||||||
generateTransactions(proxy)
|
generateTransactions(proxy)
|
||||||
|
@ -55,7 +55,6 @@ class TutorialMockNetwork {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lateinit private var mockNet: MockNetwork
|
lateinit private var mockNet: MockNetwork
|
||||||
lateinit private var notary: StartedNode<MockNetwork.MockNode>
|
|
||||||
lateinit private var nodeA: StartedNode<MockNetwork.MockNode>
|
lateinit private var nodeA: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit private var nodeB: StartedNode<MockNetwork.MockNode>
|
lateinit private var nodeB: StartedNode<MockNetwork.MockNode>
|
||||||
|
|
||||||
@ -66,7 +65,6 @@ class TutorialMockNetwork {
|
|||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
mockNet = MockNetwork()
|
mockNet = MockNetwork()
|
||||||
notary = mockNet.createNotaryNode()
|
|
||||||
nodeA = mockNet.createPartyNode()
|
nodeA = mockNet.createPartyNode()
|
||||||
nodeB = mockNet.createPartyNode()
|
nodeB = mockNet.createPartyNode()
|
||||||
|
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
myLegalName : "O=Bank A,L=London,C=GB"
|
myLegalName : "O=Bank A,L=London,C=GB"
|
||||||
p2pAddress : "my-corda-node:10002"
|
p2pAddress : "my-corda-node:10002"
|
||||||
webAddress : "localhost:10003"
|
webAddress : "localhost:10003"
|
||||||
networkMapService : {
|
|
||||||
address : "my-network-map:10000"
|
|
||||||
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
|
|
||||||
}
|
|
||||||
verifierType: "OutOfProcess"
|
verifierType: "OutOfProcess"
|
||||||
|
@ -10,7 +10,8 @@ import net.corda.finance.contracts.getCashBalances
|
|||||||
import net.corda.finance.flows.CashIssueFlow
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
import net.corda.finance.schemas.CashSchemaV1
|
import net.corda.finance.schemas.CashSchemaV1
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.testing.*
|
import net.corda.testing.chooseIdentity
|
||||||
|
import net.corda.testing.getDefaultNotary
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
@ -19,23 +20,20 @@ import org.junit.Test
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CustomVaultQueryTest {
|
class CustomVaultQueryTest {
|
||||||
|
private lateinit var mockNet: MockNetwork
|
||||||
lateinit var mockNet: MockNetwork
|
private lateinit var nodeA: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var nodeA: StartedNode<MockNetwork.MockNode>
|
private lateinit var nodeB: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var nodeB: StartedNode<MockNetwork.MockNode>
|
private lateinit var notary: Party
|
||||||
lateinit var notary: Party
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
mockNet = MockNetwork(
|
mockNet = MockNetwork(threadPerNode = true,
|
||||||
threadPerNode = true,
|
|
||||||
cordappPackages = listOf(
|
cordappPackages = listOf(
|
||||||
"net.corda.finance.contracts.asset",
|
"net.corda.finance.contracts.asset",
|
||||||
CashSchemaV1::class.packageName,
|
CashSchemaV1::class.packageName,
|
||||||
"net.corda.docs"
|
"net.corda.docs"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
mockNet.createNotaryNode()
|
|
||||||
nodeA = mockNet.createPartyNode()
|
nodeA = mockNet.createPartyNode()
|
||||||
nodeB = mockNet.createPartyNode()
|
nodeB = mockNet.createPartyNode()
|
||||||
nodeA.internals.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java)
|
nodeA.internals.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java)
|
||||||
|
@ -10,7 +10,8 @@ import net.corda.finance.contracts.getCashBalances
|
|||||||
import net.corda.finance.flows.CashIssueFlow
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
import net.corda.finance.schemas.CashSchemaV1
|
import net.corda.finance.schemas.CashSchemaV1
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.testing.*
|
import net.corda.testing.chooseIdentity
|
||||||
|
import net.corda.testing.getDefaultNotary
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -18,15 +19,14 @@ import org.junit.Test
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class FxTransactionBuildTutorialTest {
|
class FxTransactionBuildTutorialTest {
|
||||||
lateinit var mockNet: MockNetwork
|
private lateinit var mockNet: MockNetwork
|
||||||
lateinit var nodeA: StartedNode<MockNetwork.MockNode>
|
private lateinit var nodeA: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var nodeB: StartedNode<MockNetwork.MockNode>
|
private lateinit var nodeB: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var notary: Party
|
private lateinit var notary: Party
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName))
|
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName))
|
||||||
mockNet.createNotaryNode()
|
|
||||||
nodeA = mockNet.createPartyNode()
|
nodeA = mockNet.createPartyNode()
|
||||||
nodeB = mockNet.createPartyNode()
|
nodeB = mockNet.createPartyNode()
|
||||||
nodeB.internals.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java)
|
nodeB.internals.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java)
|
||||||
|
@ -10,7 +10,8 @@ import net.corda.core.node.services.vault.QueryCriteria
|
|||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.services.api.StartedNodeServices
|
import net.corda.node.services.api.StartedNodeServices
|
||||||
import net.corda.testing.*
|
import net.corda.testing.ALICE_NAME
|
||||||
|
import net.corda.testing.BOB_NAME
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -18,11 +19,11 @@ import org.junit.Test
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class WorkflowTransactionBuildTutorialTest {
|
class WorkflowTransactionBuildTutorialTest {
|
||||||
lateinit var mockNet: MockNetwork
|
private lateinit var mockNet: MockNetwork
|
||||||
lateinit var aliceServices: StartedNodeServices
|
private lateinit var aliceServices: StartedNodeServices
|
||||||
lateinit var bobServices: StartedNodeServices
|
private lateinit var bobServices: StartedNodeServices
|
||||||
lateinit var alice: Party
|
private lateinit var alice: Party
|
||||||
lateinit var bob: Party
|
private lateinit var bob: Party
|
||||||
|
|
||||||
// Helper method to locate the latest Vault version of a LinearState
|
// Helper method to locate the latest Vault version of a LinearState
|
||||||
private inline fun <reified T : LinearState> ServiceHub.latest(ref: UniqueIdentifier): StateAndRef<T> {
|
private inline fun <reified T : LinearState> ServiceHub.latest(ref: UniqueIdentifier): StateAndRef<T> {
|
||||||
@ -33,8 +34,6 @@ class WorkflowTransactionBuildTutorialTest {
|
|||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.docs"))
|
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.docs"))
|
||||||
// While we don't use the notary, we need there to be one on the network
|
|
||||||
mockNet.createNotaryNode()
|
|
||||||
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
||||||
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
||||||
aliceNode.internals.registerInitiatedFlow(RecordCompletionFlow::class.java)
|
aliceNode.internals.registerInitiatedFlow(RecordCompletionFlow::class.java)
|
||||||
|
@ -65,11 +65,6 @@ for maintenance and other minor purposes.
|
|||||||
corresponding bridge is used to forward the message to an advertising peer's p2p queue. Once a peer is picked the
|
corresponding bridge is used to forward the message to an advertising peer's p2p queue. Once a peer is picked the
|
||||||
session continues on as normal.
|
session continues on as normal.
|
||||||
|
|
||||||
:``internal.networkmap``:
|
|
||||||
This is another private queue just for the node which functions in a similar manner to the ``internal.peers.*`` queues
|
|
||||||
except this is used to form a connection to the network map node. The node running the network map service is treated
|
|
||||||
differently as it provides information about the rest of the network.
|
|
||||||
|
|
||||||
:``rpc.requests``:
|
:``rpc.requests``:
|
||||||
RPC clients send their requests here, and it's only open for sending by clients authenticated as RPC users.
|
RPC clients send their requests here, and it's only open for sending by clients authenticated as RPC users.
|
||||||
|
|
||||||
|
@ -139,28 +139,17 @@ physical host and port information required for the physical
|
|||||||
``ArtemisMQ`` messaging layer.
|
``ArtemisMQ`` messaging layer.
|
||||||
|
|
||||||
|
|
||||||
PersistentNetworkMapService and NetworkMapService
|
PersistentNetworkMapService
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The ``NetworkMapService`` is a node internal component responsible for
|
The ``PersistentNetworkMapService`` keeps track of ``NodeInfo`` and
|
||||||
managing and communicating the directory of authenticated registered
|
persists it to the database. It and will include nodes that are not currently active.
|
||||||
nodes and advertised services in the Corda network. Only a single node
|
The networking layer will persist any messages directed at such inactive
|
||||||
in the network (in future this will be a clustered service) should host
|
|
||||||
the NetworkMapService implementation. All other Corda nodes initiate
|
|
||||||
their remote connection to the ``NetworkMapService`` early in the
|
|
||||||
start-up sequence and wait to synchronise their local
|
|
||||||
``NetworkMapCache`` before activating any flows. For the
|
|
||||||
``PersistentNetworkMapService`` registered ``NodeInfo`` data is
|
|
||||||
persisted and will include nodes that are not currently active. The
|
|
||||||
networking layer will persist any messages directed at such inactive
|
|
||||||
nodes with the expectation that they will be delivered eventually, or
|
nodes with the expectation that they will be delivered eventually, or
|
||||||
else that the source flow will be terminated by admin intervention.
|
else that the source flow will be terminated by admin intervention.
|
||||||
An ``InMemoryNetworkMapService`` is also available for unit tests
|
An ``InMemoryNetworkMapService`` is also available for unit tests
|
||||||
without a database.
|
without a database.
|
||||||
|
|
||||||
The ``NetworkMapService`` should not be used by any flows, or
|
|
||||||
contracts. Instead they should access the NetworkMapCache service to
|
|
||||||
access this data.
|
|
||||||
|
|
||||||
Storage and persistence related services
|
Storage and persistence related services
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
Creating a Corda network
|
Creating a Corda network
|
||||||
========================
|
========================
|
||||||
|
|
||||||
A Corda network consists of a number of machines running nodes, including a single node operating as the network map
|
A Corda network consists of a number of machines running nodes. These nodes communicate using persistent protocols in order to create and validate transactions.
|
||||||
service. These nodes communicate using persistent protocols in order to create and validate transactions.
|
|
||||||
|
|
||||||
There are four broader categories of functionality one such node may have. These pieces of functionality are provided as
|
There are four broader categories of functionality one such node may have. These pieces of functionality are provided as
|
||||||
services, and one node may run several of them.
|
services, and one node may run several of them.
|
||||||
|
@ -122,7 +122,7 @@ In the instructions above the server node permissions are configured programmati
|
|||||||
.. code-block:: text
|
.. code-block:: text
|
||||||
|
|
||||||
driver(driverDirectory = baseDirectory) {
|
driver(driverDirectory = baseDirectory) {
|
||||||
val user = User("user", "password", permissions = setOf(startFlowPermission<CashFlow>()))
|
val user = User("user", "password", permissions = setOf(startFlow<CashFlow>()))
|
||||||
val node = startNode("CN=Alice Corp,O=Alice Corp,L=London,C=GB", rpcUsers = listOf(user)).get()
|
val node = startNode("CN=Alice Corp,O=Alice Corp,L=London,C=GB", rpcUsers = listOf(user)).get()
|
||||||
|
|
||||||
When starting a standalone node using a configuration file we must supply the RPC credentials as follows:
|
When starting a standalone node using a configuration file we must supply the RPC credentials as follows:
|
||||||
|
@ -1,41 +1,38 @@
|
|||||||
package net.corda.finance.contracts.asset
|
package net.corda.finance.contracts.asset
|
||||||
|
|
||||||
import net.corda.core.internal.packageName
|
|
||||||
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.flows.CashException
|
import net.corda.finance.flows.CashException
|
||||||
import net.corda.finance.flows.CashPaymentFlow
|
import net.corda.finance.flows.CashPaymentFlow
|
||||||
import net.corda.finance.schemas.CashSchemaV1
|
|
||||||
import net.corda.testing.chooseIdentity
|
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import net.corda.testing.node.MockNodeParameters
|
import net.corda.testing.node.MockNodeParameters
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
|
import org.junit.After
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
|
|
||||||
class CashSelectionH2Test {
|
class CashSelectionH2Test {
|
||||||
|
private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance"))
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun cleanUp() {
|
||||||
|
mockNet.stopNodes()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `check does not hold connection over retries`() {
|
fun `check does not hold connection over retries`() {
|
||||||
val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName))
|
val bankA = mockNet.createNode(MockNodeParameters(configOverrides = {
|
||||||
try {
|
|
||||||
val notaryNode = mockNet.createNotaryNode()
|
|
||||||
val bankA = mockNet.createNode(MockNodeParameters(configOverrides = { existingConfig ->
|
|
||||||
// Tweak connections to be minimal to make this easier (1 results in a hung node during start up, so use 2 connections).
|
// Tweak connections to be minimal to make this easier (1 results in a hung node during start up, so use 2 connections).
|
||||||
existingConfig.dataSourceProperties.setProperty("maximumPoolSize", "2")
|
it.dataSourceProperties.setProperty("maximumPoolSize", "2")
|
||||||
}))
|
}))
|
||||||
mockNet.startNodes()
|
val notary = mockNet.defaultNotaryIdentity
|
||||||
|
|
||||||
// Start more cash spends than we have connections. If spend leaks a connection on retry, we will run out of connections.
|
// Start more cash spends than we have connections. If spend leaks a connection on retry, we will run out of connections.
|
||||||
val flow1 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity()))
|
val flow1 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary))
|
||||||
val flow2 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity()))
|
val flow2 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary))
|
||||||
val flow3 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity()))
|
val flow3 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary))
|
||||||
|
|
||||||
assertThatThrownBy { flow1.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
assertThatThrownBy { flow1.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
||||||
assertThatThrownBy { flow2.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
assertThatThrownBy { flow2.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
||||||
assertThatThrownBy { flow3.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
assertThatThrownBy { flow3.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
||||||
} finally {
|
|
||||||
mockNet.stopNodes()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,9 @@ import net.corda.finance.DOLLARS
|
|||||||
import net.corda.finance.`issued by`
|
import net.corda.finance.`issued by`
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.testing.*
|
import net.corda.testing.BOC
|
||||||
|
import net.corda.testing.chooseIdentity
|
||||||
|
import net.corda.testing.getDefaultNotary
|
||||||
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import net.corda.testing.node.MockNetwork.MockNode
|
import net.corda.testing.node.MockNetwork.MockNode
|
||||||
@ -23,15 +25,13 @@ class CashExitFlowTests {
|
|||||||
private val ref = OpaqueBytes.of(0x01)
|
private val ref = OpaqueBytes.of(0x01)
|
||||||
private lateinit var bankOfCordaNode: StartedNode<MockNode>
|
private lateinit var bankOfCordaNode: StartedNode<MockNode>
|
||||||
private lateinit var bankOfCorda: Party
|
private lateinit var bankOfCorda: Party
|
||||||
private lateinit var notaryNode: StartedNode<MockNode>
|
|
||||||
private lateinit var notary: Party
|
private lateinit var notary: Party
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun start() {
|
fun start() {
|
||||||
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(),
|
||||||
notaryNode = mockNet.createNotaryNode()
|
cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||||
bankOfCordaNode = mockNet.createPartyNode(BOC.name)
|
bankOfCordaNode = mockNet.createPartyNode(BOC.name)
|
||||||
notary = notaryNode.services.getDefaultNotary()
|
|
||||||
bankOfCorda = bankOfCordaNode.info.chooseIdentity()
|
bankOfCorda = bankOfCordaNode.info.chooseIdentity()
|
||||||
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
@ -7,9 +7,8 @@ import net.corda.finance.DOLLARS
|
|||||||
import net.corda.finance.`issued by`
|
import net.corda.finance.`issued by`
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.testing.chooseIdentity
|
|
||||||
import net.corda.testing.getDefaultNotary
|
|
||||||
import net.corda.testing.BOC
|
import net.corda.testing.BOC
|
||||||
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import net.corda.testing.node.MockNetwork.MockNode
|
import net.corda.testing.node.MockNetwork.MockNode
|
||||||
@ -23,16 +22,14 @@ class CashIssueFlowTests {
|
|||||||
private lateinit var mockNet: MockNetwork
|
private lateinit var mockNet: MockNetwork
|
||||||
private lateinit var bankOfCordaNode: StartedNode<MockNode>
|
private lateinit var bankOfCordaNode: StartedNode<MockNode>
|
||||||
private lateinit var bankOfCorda: Party
|
private lateinit var bankOfCorda: Party
|
||||||
private lateinit var notaryNode: StartedNode<MockNode>
|
|
||||||
private lateinit var notary: Party
|
private lateinit var notary: Party
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun start() {
|
fun start() {
|
||||||
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||||
notaryNode = mockNet.createNotaryNode()
|
|
||||||
bankOfCordaNode = mockNet.createPartyNode(BOC.name)
|
bankOfCordaNode = mockNet.createPartyNode(BOC.name)
|
||||||
bankOfCorda = bankOfCordaNode.info.chooseIdentity()
|
bankOfCorda = bankOfCordaNode.info.chooseIdentity()
|
||||||
notary = notaryNode.services.getDefaultNotary()
|
notary = mockNet.defaultNotaryIdentity
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,18 +26,16 @@ class CashPaymentFlowTests {
|
|||||||
private val ref = OpaqueBytes.of(0x01)
|
private val ref = OpaqueBytes.of(0x01)
|
||||||
private lateinit var bankOfCordaNode: StartedNode<MockNode>
|
private lateinit var bankOfCordaNode: StartedNode<MockNode>
|
||||||
private lateinit var bankOfCorda: Party
|
private lateinit var bankOfCorda: Party
|
||||||
private lateinit var notaryNode: StartedNode<MockNode>
|
private lateinit var aliceNode: StartedNode<MockNode>
|
||||||
private lateinit var notary: Party
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun start() {
|
fun start() {
|
||||||
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||||
notaryNode = mockNet.createNotaryNode()
|
|
||||||
bankOfCordaNode = mockNet.createPartyNode(BOC.name)
|
bankOfCordaNode = mockNet.createPartyNode(BOC.name)
|
||||||
|
aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||||
bankOfCorda = bankOfCordaNode.info.chooseIdentity()
|
bankOfCorda = bankOfCordaNode.info.chooseIdentity()
|
||||||
notary = notaryNode.services.getDefaultNotary()
|
|
||||||
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, mockNet.defaultNotaryIdentity)).resultFuture
|
||||||
future.getOrThrow()
|
future.getOrThrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +46,7 @@ class CashPaymentFlowTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `pay some cash`() {
|
fun `pay some cash`() {
|
||||||
val payTo = notaryNode.info.chooseIdentity()
|
val payTo = aliceNode.info.chooseIdentity()
|
||||||
val expectedPayment = 500.DOLLARS
|
val expectedPayment = 500.DOLLARS
|
||||||
val expectedChange = 1500.DOLLARS
|
val expectedChange = 1500.DOLLARS
|
||||||
|
|
||||||
@ -56,7 +54,7 @@ class CashPaymentFlowTests {
|
|||||||
// Register for vault updates
|
// Register for vault updates
|
||||||
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||||
val (_, vaultUpdatesBoc) = bankOfCordaNode.services.vaultService.trackBy<Cash.State>(criteria)
|
val (_, vaultUpdatesBoc) = bankOfCordaNode.services.vaultService.trackBy<Cash.State>(criteria)
|
||||||
val (_, vaultUpdatesBankClient) = notaryNode.services.vaultService.trackBy<Cash.State>(criteria)
|
val (_, vaultUpdatesBankClient) = aliceNode.services.vaultService.trackBy<Cash.State>(criteria)
|
||||||
|
|
||||||
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expectedPayment,
|
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expectedPayment,
|
||||||
payTo)).resultFuture
|
payTo)).resultFuture
|
||||||
@ -88,7 +86,7 @@ class CashPaymentFlowTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `pay more than we have`() {
|
fun `pay more than we have`() {
|
||||||
val payTo = notaryNode.info.chooseIdentity()
|
val payTo = aliceNode.info.chooseIdentity()
|
||||||
val expected = 4000.DOLLARS
|
val expected = 4000.DOLLARS
|
||||||
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected,
|
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected,
|
||||||
payTo)).resultFuture
|
payTo)).resultFuture
|
||||||
@ -100,7 +98,7 @@ class CashPaymentFlowTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `pay zero cash`() {
|
fun `pay zero cash`() {
|
||||||
val payTo = notaryNode.info.chooseIdentity()
|
val payTo = aliceNode.info.chooseIdentity()
|
||||||
val expected = 0.DOLLARS
|
val expected = 0.DOLLARS
|
||||||
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected,
|
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected,
|
||||||
payTo)).resultFuture
|
payTo)).resultFuture
|
||||||
|
@ -61,7 +61,8 @@ class CordappPlugin : Plugin<Project> {
|
|||||||
}
|
}
|
||||||
filteredDeps.forEach {
|
filteredDeps.forEach {
|
||||||
// net.corda or com.r3.corda.enterprise may be a core dependency which shouldn't be included in this cordapp so give a warning
|
// net.corda or com.r3.corda.enterprise may be a core dependency which shouldn't be included in this cordapp so give a warning
|
||||||
if ((it.group.startsWith("net.corda.") || it.group.startsWith("com.r3.corda.enterprise."))) {
|
val group = it.group?.toString() ?: ""
|
||||||
|
if (group.startsWith("net.corda.") || group.startsWith("com.r3.corda.enterprise.")) {
|
||||||
project.logger.warn("You appear to have included a Corda platform component ($it) using a 'compile' or 'runtime' dependency." +
|
project.logger.warn("You appear to have included a Corda platform component ($it) using a 'compile' or 'runtime' dependency." +
|
||||||
"This can cause node stability problems. Please use 'corda' instead." +
|
"This can cause node stability problems. Please use 'corda' instead." +
|
||||||
"See http://docs.corda.net/cordapp-build-systems.html")
|
"See http://docs.corda.net/cordapp-build-systems.html")
|
||||||
|
@ -58,7 +58,16 @@ public class CordformNode implements NodeDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the Artemis P2P port for this node.
|
* Get the artemis address for this node.
|
||||||
|
*
|
||||||
|
* @return This node's P2P address.
|
||||||
|
*/
|
||||||
|
public String getP2pAddress() {
|
||||||
|
return config.getString("p2pAddress");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Artemis P2P port for this node on localhost.
|
||||||
*
|
*
|
||||||
* @param p2pPort The Artemis messaging queue port.
|
* @param p2pPort The Artemis messaging queue port.
|
||||||
*/
|
*/
|
||||||
@ -67,7 +76,16 @@ public class CordformNode implements NodeDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the Artemis RPC port for this node.
|
* Set the Artemis P2P address for this node.
|
||||||
|
*
|
||||||
|
* @param p2pAddress The Artemis messaging queue host and port.
|
||||||
|
*/
|
||||||
|
public void p2pAddress(String p2pAddress) {
|
||||||
|
config = config.withValue("p2pAddress", ConfigValueFactory.fromAnyRef(p2pAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Artemis RPC port for this node on localhost.
|
||||||
*
|
*
|
||||||
* @param rpcPort The Artemis RPC queue port.
|
* @param rpcPort The Artemis RPC queue port.
|
||||||
*/
|
*/
|
||||||
@ -75,6 +93,15 @@ public class CordformNode implements NodeDefinition {
|
|||||||
config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + rpcPort));
|
config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + rpcPort));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Artemis RPC address for this node.
|
||||||
|
*
|
||||||
|
* @param rpcAddress The Artemis RPC queue host and port.
|
||||||
|
*/
|
||||||
|
public void rpcAddress(String rpcAddress) {
|
||||||
|
config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(rpcAddress));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the path to a file with optional properties, which are appended to the generated node.conf file.
|
* Set the path to a file with optional properties, which are appended to the generated node.conf file.
|
||||||
*
|
*
|
||||||
|
@ -61,7 +61,7 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the HTTP web server port for this node.
|
* Set the HTTP web server port for this node. Will use localhost as the address.
|
||||||
*
|
*
|
||||||
* @param webPort The web port number for this node.
|
* @param webPort The web port number for this node.
|
||||||
*/
|
*/
|
||||||
@ -70,6 +70,16 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$webPort"))
|
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$webPort"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the HTTP web server address and port for this node.
|
||||||
|
*
|
||||||
|
* @param webAddress The web address for this node.
|
||||||
|
*/
|
||||||
|
fun webAddress(webAddress: String) {
|
||||||
|
config = config.withValue("webAddress",
|
||||||
|
ConfigValueFactory.fromAnyRef(webAddress))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the network map address for this node.
|
* Set the network map address for this node.
|
||||||
*
|
*
|
||||||
@ -107,15 +117,6 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
appendOptionalConfig()
|
appendOptionalConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the artemis address for this node.
|
|
||||||
*
|
|
||||||
* @return This node's P2P address.
|
|
||||||
*/
|
|
||||||
fun getP2PAddress(): String {
|
|
||||||
return config.getString("p2pAddress")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun rootDir(rootDir: Path) {
|
internal fun rootDir(rootDir: Path) {
|
||||||
if(name == null) {
|
if(name == null) {
|
||||||
project.logger.error("Node has a null name - cannot create node")
|
project.logger.error("Node has a null name - cannot create node")
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
#Thu Aug 24 12:32:31 BST 2017
|
#Mon Nov 06 15:05:49 GMT 2017
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package net.corda.nodeapi.internal
|
|
||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A container for additional information for an advertised service.
|
|
||||||
*
|
|
||||||
* @param type the ServiceType identifier
|
|
||||||
* @param name the service name, used for differentiating multiple services of the same type. Can also be used as a
|
|
||||||
* grouping identifier for nodes collectively running a distributed service.
|
|
||||||
*/
|
|
||||||
@CordaSerializable
|
|
||||||
data class ServiceInfo(val type: ServiceType, val name: CordaX500Name? = null) {
|
|
||||||
companion object {
|
|
||||||
fun parse(encoded: String): ServiceInfo {
|
|
||||||
val parts = encoded.split("|")
|
|
||||||
require(parts.size in 1..2) { "Invalid number of elements found" }
|
|
||||||
val type = ServiceType.parse(parts[0])
|
|
||||||
val name = parts.getOrNull(1)
|
|
||||||
val principal = name?.let { CordaX500Name.parse(it) }
|
|
||||||
return ServiceInfo(type, principal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString() = if (name != null) "$type|$name" else type.toString()
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package net.corda.nodeapi.internal
|
|
||||||
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Identifier for service types a node can expose over the network to other peers. These types are placed into network
|
|
||||||
* map advertisements. Services that are purely local and are not providing functionality to other parts of the network
|
|
||||||
* don't need a declared service type.
|
|
||||||
*/
|
|
||||||
@CordaSerializable
|
|
||||||
class ServiceType private constructor(val id: String) {
|
|
||||||
init {
|
|
||||||
// Enforce:
|
|
||||||
//
|
|
||||||
// * IDs must start with a lower case letter
|
|
||||||
// * IDs can only contain alphanumeric, full stop and underscore ASCII characters
|
|
||||||
require(id.matches(Regex("[a-z][a-zA-Z0-9._]+"))) { id }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val corda: ServiceType
|
|
||||||
get() {
|
|
||||||
val stack = Throwable().stackTrace
|
|
||||||
val caller = stack.first().className
|
|
||||||
require(caller.startsWith("net.corda.")) { "Corda ServiceType namespace is reserved for Corda core components" }
|
|
||||||
return ServiceType("corda")
|
|
||||||
}
|
|
||||||
|
|
||||||
val notary: ServiceType = corda.getSubType("notary")
|
|
||||||
|
|
||||||
fun parse(id: String): ServiceType = ServiceType(id)
|
|
||||||
|
|
||||||
private fun baseWithSubType(baseId: String, subTypeId: String) = ServiceType("$baseId.$subTypeId")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSubType(subTypeId: String): ServiceType = baseWithSubType(id, subTypeId)
|
|
||||||
|
|
||||||
fun isSubTypeOf(superType: ServiceType) = (id == superType.id) || id.startsWith(superType.id + ".")
|
|
||||||
fun isNotary() = isSubTypeOf(notary)
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean = other === this || other is ServiceType && other.id == this.id
|
|
||||||
override fun hashCode(): Int = id.hashCode()
|
|
||||||
override fun toString(): String = id
|
|
||||||
}
|
|
@ -4,15 +4,25 @@ package net.corda.nodeapi.internal.serialization
|
|||||||
|
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.SerializationDefaults
|
import net.corda.core.serialization.SerializationDefaults
|
||||||
|
import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Serialisation contexts for the client.
|
* Serialisation contexts for the client.
|
||||||
* These have been refactored into a separate file to prevent
|
* These have been refactored into a separate file to prevent
|
||||||
* servers from trying to instantiate any of them.
|
* servers from trying to instantiate any of them.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val KRYO_RPC_CLIENT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
val KRYO_RPC_CLIENT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
||||||
SerializationDefaults.javaClass.classLoader,
|
SerializationDefaults.javaClass.classLoader,
|
||||||
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
||||||
emptyMap(),
|
emptyMap(),
|
||||||
true,
|
true,
|
||||||
SerializationContext.UseCase.RPCClient)
|
SerializationContext.UseCase.RPCClient)
|
||||||
|
|
||||||
|
val AMQP_RPC_CLIENT_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
|
||||||
|
SerializationDefaults.javaClass.classLoader,
|
||||||
|
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
||||||
|
emptyMap(),
|
||||||
|
true,
|
||||||
|
SerializationContext.UseCase.RPCClient)
|
||||||
|
@ -12,6 +12,7 @@ import net.corda.core.serialization.CordaSerializable
|
|||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.hasAnnotationInHierarchy
|
import net.corda.nodeapi.internal.serialization.amqp.hasAnnotationInHierarchy
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.ThrowableSerializer
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.lang.reflect.Modifier.isAbstract
|
import java.lang.reflect.Modifier.isAbstract
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
|
@ -1,26 +1,13 @@
|
|||||||
package net.corda.nodeapi.internal.serialization
|
package net.corda.nodeapi.internal.serialization
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Fiber
|
|
||||||
import co.paralleluniverse.io.serialization.kryo.KryoSerializer
|
|
||||||
import com.esotericsoftware.kryo.Kryo
|
|
||||||
import com.esotericsoftware.kryo.KryoException
|
|
||||||
import com.esotericsoftware.kryo.Serializer
|
|
||||||
import com.esotericsoftware.kryo.io.Input
|
|
||||||
import com.esotericsoftware.kryo.io.Output
|
|
||||||
import com.esotericsoftware.kryo.pool.KryoPool
|
|
||||||
import com.esotericsoftware.kryo.serializers.ClosureSerializer
|
|
||||||
import com.google.common.cache.Cache
|
import com.google.common.cache.Cache
|
||||||
import com.google.common.cache.CacheBuilder
|
import com.google.common.cache.CacheBuilder
|
||||||
import net.corda.core.contracts.Attachment
|
import net.corda.core.contracts.Attachment
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.internal.LazyPool
|
|
||||||
import net.corda.core.internal.uncheckedCast
|
|
||||||
import net.corda.core.serialization.*
|
import net.corda.core.serialization.*
|
||||||
import net.corda.core.utilities.ByteSequence
|
import net.corda.core.utilities.ByteSequence
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
|
||||||
import net.corda.nodeapi.internal.AttachmentsClassLoader
|
import net.corda.nodeapi.internal.AttachmentsClassLoader
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
@ -158,124 +145,8 @@ open class SerializationFactoryImpl : SerializationFactory() {
|
|||||||
override fun hashCode(): Int = registeredSchemes.hashCode()
|
override fun hashCode(): Int = registeredSchemes.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
private object AutoCloseableSerialisationDetector : Serializer<AutoCloseable>() {
|
|
||||||
override fun write(kryo: Kryo, output: Output, closeable: AutoCloseable) {
|
|
||||||
val message = "${closeable.javaClass.name}, which is a closeable resource, has been detected during flow checkpointing. " +
|
|
||||||
"Restoring such resources across node restarts is not supported. Make sure code accessing it is " +
|
|
||||||
"confined to a private method or the reference is nulled out."
|
|
||||||
throw UnsupportedOperationException(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(kryo: Kryo, input: Input, type: Class<AutoCloseable>) = throw IllegalStateException("Should not reach here!")
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class AbstractKryoSerializationScheme : SerializationScheme {
|
|
||||||
private val kryoPoolsForContexts = ConcurrentHashMap<Pair<ClassWhitelist, ClassLoader>, KryoPool>()
|
|
||||||
|
|
||||||
protected abstract fun rpcClientKryoPool(context: SerializationContext): KryoPool
|
|
||||||
protected abstract fun rpcServerKryoPool(context: SerializationContext): KryoPool
|
|
||||||
|
|
||||||
private fun getPool(context: SerializationContext): KryoPool {
|
|
||||||
return kryoPoolsForContexts.computeIfAbsent(Pair(context.whitelist, context.deserializationClassLoader)) {
|
|
||||||
when (context.useCase) {
|
|
||||||
SerializationContext.UseCase.Checkpoint ->
|
|
||||||
KryoPool.Builder {
|
|
||||||
val serializer = Fiber.getFiberSerializer(false) as KryoSerializer
|
|
||||||
val classResolver = CordaClassResolver(context).apply { setKryo(serializer.kryo) }
|
|
||||||
// TODO The ClassResolver can only be set in the Kryo constructor and Quasar doesn't provide us with a way of doing that
|
|
||||||
val field = Kryo::class.java.getDeclaredField("classResolver").apply { isAccessible = true }
|
|
||||||
serializer.kryo.apply {
|
|
||||||
field.set(this, classResolver)
|
|
||||||
DefaultKryoCustomizer.customize(this)
|
|
||||||
addDefaultSerializer(AutoCloseable::class.java, AutoCloseableSerialisationDetector)
|
|
||||||
register(ClosureSerializer.Closure::class.java, CordaClosureSerializer)
|
|
||||||
classLoader = it.second
|
|
||||||
}
|
|
||||||
}.build()
|
|
||||||
SerializationContext.UseCase.RPCClient ->
|
|
||||||
rpcClientKryoPool(context)
|
|
||||||
SerializationContext.UseCase.RPCServer ->
|
|
||||||
rpcServerKryoPool(context)
|
|
||||||
else ->
|
|
||||||
KryoPool.Builder {
|
|
||||||
DefaultKryoCustomizer.customize(CordaKryo(CordaClassResolver(context))).apply { classLoader = it.second }
|
|
||||||
}.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T : Any> withContext(kryo: Kryo, context: SerializationContext, block: (Kryo) -> T): T {
|
|
||||||
kryo.context.ensureCapacity(context.properties.size)
|
|
||||||
context.properties.forEach { kryo.context.put(it.key, it.value) }
|
|
||||||
try {
|
|
||||||
return block(kryo)
|
|
||||||
} finally {
|
|
||||||
kryo.context.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
|
|
||||||
val pool = getPool(context)
|
|
||||||
val headerSize = KryoHeaderV0_1.size
|
|
||||||
val header = byteSequence.take(headerSize)
|
|
||||||
if (header != KryoHeaderV0_1) {
|
|
||||||
throw KryoException("Serialized bytes header does not match expected format.")
|
|
||||||
}
|
|
||||||
Input(byteSequence.bytes, byteSequence.offset + headerSize, byteSequence.size - headerSize).use { input ->
|
|
||||||
return pool.run { kryo ->
|
|
||||||
withContext(kryo, context) {
|
|
||||||
if (context.objectReferencesEnabled) {
|
|
||||||
uncheckedCast(kryo.readClassAndObject(input))
|
|
||||||
} else {
|
|
||||||
kryo.withoutReferences { uncheckedCast<Any?, T>(kryo.readClassAndObject(input)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
|
|
||||||
val pool = getPool(context)
|
|
||||||
return pool.run { kryo ->
|
|
||||||
withContext(kryo, context) {
|
|
||||||
serializeOutputStreamPool.run { stream ->
|
|
||||||
serializeBufferPool.run { buffer ->
|
|
||||||
Output(buffer).use {
|
|
||||||
it.outputStream = stream
|
|
||||||
it.writeBytes(KryoHeaderV0_1.bytes)
|
|
||||||
if (context.objectReferencesEnabled) {
|
|
||||||
kryo.writeClassAndObject(it, obj)
|
|
||||||
} else {
|
|
||||||
kryo.withoutReferences { kryo.writeClassAndObject(it, obj) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SerializedBytes(stream.toByteArray())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val serializeBufferPool = LazyPool(
|
|
||||||
newInstance = { ByteArray(64 * 1024) }
|
|
||||||
)
|
|
||||||
private val serializeOutputStreamPool = LazyPool(
|
|
||||||
clear = ByteArrayOutputStream::reset,
|
|
||||||
shouldReturnToPool = { it.size() < 256 * 1024 }, // Discard if it grew too large
|
|
||||||
newInstance = { ByteArrayOutputStream(64 * 1024) }
|
|
||||||
)
|
|
||||||
|
|
||||||
// "corda" + majorVersionByte + minorVersionMSB + minorVersionLSB
|
|
||||||
val KryoHeaderV0_1: OpaqueBytes = OpaqueBytes("corda\u0000\u0000\u0001".toByteArray(Charsets.UTF_8))
|
|
||||||
|
|
||||||
|
|
||||||
val KRYO_P2P_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
|
||||||
SerializationDefaults.javaClass.classLoader,
|
|
||||||
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
|
||||||
emptyMap(),
|
|
||||||
true,
|
|
||||||
SerializationContext.UseCase.P2P)
|
|
||||||
|
|
||||||
interface SerializationScheme {
|
interface SerializationScheme {
|
||||||
// byteSequence expected to just be the 8 bytes necessary for versioning
|
// byteSequence expected to just be the 8 bytes necessary for versioning
|
||||||
|
@ -6,6 +6,11 @@ import net.corda.core.serialization.ClassWhitelist
|
|||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.SerializationDefaults
|
import net.corda.core.serialization.SerializationDefaults
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
|
import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||||
|
|
||||||
|
object QuasarWhitelist : ClassWhitelist {
|
||||||
|
override fun hasListed(type: Class<*>): Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Serialisation contexts for the server.
|
* Serialisation contexts for the server.
|
||||||
@ -16,18 +21,28 @@ import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
|
|||||||
* CANNOT always be instantiated outside of the server and so
|
* CANNOT always be instantiated outside of the server and so
|
||||||
* MUST be kept separate!
|
* MUST be kept separate!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val KRYO_RPC_SERVER_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
val KRYO_RPC_SERVER_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
||||||
SerializationDefaults.javaClass.classLoader,
|
SerializationDefaults.javaClass.classLoader,
|
||||||
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
||||||
emptyMap(),
|
emptyMap(),
|
||||||
true,
|
true,
|
||||||
SerializationContext.UseCase.RPCServer)
|
SerializationContext.UseCase.RPCServer)
|
||||||
|
|
||||||
val KRYO_STORAGE_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
val KRYO_STORAGE_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
||||||
SerializationDefaults.javaClass.classLoader,
|
SerializationDefaults.javaClass.classLoader,
|
||||||
AllButBlacklisted,
|
AllButBlacklisted,
|
||||||
emptyMap(),
|
emptyMap(),
|
||||||
true,
|
true,
|
||||||
SerializationContext.UseCase.Storage)
|
SerializationContext.UseCase.Storage)
|
||||||
|
|
||||||
|
val KRYO_P2P_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
||||||
|
SerializationDefaults.javaClass.classLoader,
|
||||||
|
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
||||||
|
emptyMap(),
|
||||||
|
true,
|
||||||
|
SerializationContext.UseCase.P2P)
|
||||||
|
|
||||||
val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
||||||
SerializationDefaults.javaClass.classLoader,
|
SerializationDefaults.javaClass.classLoader,
|
||||||
QuasarWhitelist,
|
QuasarWhitelist,
|
||||||
@ -35,9 +50,6 @@ val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
|||||||
true,
|
true,
|
||||||
SerializationContext.UseCase.Checkpoint)
|
SerializationContext.UseCase.Checkpoint)
|
||||||
|
|
||||||
object QuasarWhitelist : ClassWhitelist {
|
|
||||||
override fun hasListed(type: Class<*>): Boolean = true
|
|
||||||
}
|
|
||||||
|
|
||||||
val AMQP_STORAGE_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
|
val AMQP_STORAGE_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
|
||||||
SerializationDefaults.javaClass.classLoader,
|
SerializationDefaults.javaClass.classLoader,
|
||||||
@ -45,3 +57,18 @@ val AMQP_STORAGE_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
|
|||||||
emptyMap(),
|
emptyMap(),
|
||||||
true,
|
true,
|
||||||
SerializationContext.UseCase.Storage)
|
SerializationContext.UseCase.Storage)
|
||||||
|
|
||||||
|
val AMQP_P2P_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
|
||||||
|
SerializationDefaults.javaClass.classLoader,
|
||||||
|
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
||||||
|
emptyMap(),
|
||||||
|
true,
|
||||||
|
SerializationContext.UseCase.P2P)
|
||||||
|
|
||||||
|
val AMQP_RPC_SERVER_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
|
||||||
|
SerializationDefaults.javaClass.classLoader,
|
||||||
|
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
||||||
|
emptyMap(),
|
||||||
|
true,
|
||||||
|
SerializationContext.UseCase.RPCServer)
|
||||||
|
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
@file:JvmName("AMQPSerializationScheme")
|
@file:JvmName("AMQPSerializationScheme")
|
||||||
|
|
||||||
package net.corda.nodeapi.internal.serialization
|
package net.corda.nodeapi.internal.serialization.amqp
|
||||||
|
|
||||||
import net.corda.core.serialization.*
|
import net.corda.core.serialization.*
|
||||||
import net.corda.core.utilities.ByteSequence
|
import net.corda.core.utilities.ByteSequence
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
|
import net.corda.nodeapi.internal.serialization.DefaultWhitelist
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput
|
import net.corda.nodeapi.internal.serialization.MutableClassWhitelist
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput
|
import net.corda.nodeapi.internal.serialization.SerializationScheme
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ fun SerializerFactory.addToWhitelist(vararg types: Class<*>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class AbstractAMQPSerializationScheme : SerializationScheme {
|
abstract class AbstractAMQPSerializationScheme : SerializationScheme {
|
||||||
internal companion object {
|
companion object {
|
||||||
private val serializationWhitelists: List<SerializationWhitelist> by lazy {
|
private val serializationWhitelists: List<SerializationWhitelist> by lazy {
|
||||||
ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist
|
ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist
|
||||||
}
|
}
|
||||||
@ -132,9 +131,3 @@ class AMQPClientSerializationScheme : AbstractAMQPSerializationScheme() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val AMQP_P2P_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
|
|
||||||
SerializationDefaults.javaClass.classLoader,
|
|
||||||
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
|
||||||
emptyMap(),
|
|
||||||
true,
|
|
||||||
SerializationContext.UseCase.P2P)
|
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.nodeapi.internal.serialization
|
package net.corda.nodeapi.internal.serialization.kryo
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.Kryo
|
import com.esotericsoftware.kryo.Kryo
|
||||||
import com.esotericsoftware.kryo.io.Output
|
import com.esotericsoftware.kryo.io.Output
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.nodeapi.internal.serialization
|
package net.corda.nodeapi.internal.serialization.kryo
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.Kryo
|
import com.esotericsoftware.kryo.Kryo
|
||||||
import com.esotericsoftware.kryo.Serializer
|
import com.esotericsoftware.kryo.Serializer
|
||||||
@ -23,6 +23,10 @@ import net.corda.core.transactions.SignedTransaction
|
|||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.NonEmptySet
|
import net.corda.core.utilities.NonEmptySet
|
||||||
import net.corda.core.utilities.toNonEmptySet
|
import net.corda.core.utilities.toNonEmptySet
|
||||||
|
import net.corda.nodeapi.internal.serialization.CordaClassResolver
|
||||||
|
import net.corda.nodeapi.internal.serialization.DefaultWhitelist
|
||||||
|
import net.corda.nodeapi.internal.serialization.GeneratedAttachment
|
||||||
|
import net.corda.nodeapi.internal.serialization.MutableClassWhitelist
|
||||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
||||||
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.nodeapi.internal.serialization
|
package net.corda.nodeapi.internal.serialization.kryo
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.*
|
import com.esotericsoftware.kryo.*
|
||||||
import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory
|
import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory
|
||||||
@ -21,11 +21,9 @@ import net.corda.core.serialization.SerializedBytes
|
|||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
import net.corda.core.toObservable
|
import net.corda.core.toObservable
|
||||||
import net.corda.core.transactions.*
|
import net.corda.core.transactions.*
|
||||||
import net.corda.core.transactions.CoreTransaction
|
|
||||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
|
||||||
import net.corda.core.transactions.WireTransaction
|
|
||||||
import net.corda.core.utilities.SgxSupport
|
import net.corda.core.utilities.SgxSupport
|
||||||
|
import net.corda.nodeapi.internal.serialization.CordaClassResolver
|
||||||
|
import net.corda.nodeapi.internal.serialization.serializationContextKey
|
||||||
import org.bouncycastle.asn1.ASN1InputStream
|
import org.bouncycastle.asn1.ASN1InputStream
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
@ -288,7 +286,7 @@ object SignedTransactionSerializer : Serializer<SignedTransaction>() {
|
|||||||
|
|
||||||
sealed class UseCaseSerializer<T>(private val allowedUseCases: EnumSet<SerializationContext.UseCase>) : Serializer<T>() {
|
sealed class UseCaseSerializer<T>(private val allowedUseCases: EnumSet<SerializationContext.UseCase>) : Serializer<T>() {
|
||||||
protected fun checkUseCase() {
|
protected fun checkUseCase() {
|
||||||
checkUseCase(allowedUseCases)
|
net.corda.nodeapi.internal.serialization.checkUseCase(allowedUseCases)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,132 @@
|
|||||||
|
package net.corda.nodeapi.internal.serialization.kryo
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import co.paralleluniverse.fibers.Fiber
|
||||||
|
import co.paralleluniverse.io.serialization.kryo.KryoSerializer
|
||||||
|
import com.esotericsoftware.kryo.Kryo
|
||||||
|
import com.esotericsoftware.kryo.KryoException
|
||||||
|
import com.esotericsoftware.kryo.Serializer
|
||||||
|
import com.esotericsoftware.kryo.io.Input
|
||||||
|
import com.esotericsoftware.kryo.io.Output
|
||||||
|
import com.esotericsoftware.kryo.pool.KryoPool
|
||||||
|
import com.esotericsoftware.kryo.serializers.ClosureSerializer
|
||||||
|
import net.corda.core.internal.uncheckedCast
|
||||||
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
import net.corda.core.utilities.ByteSequence
|
||||||
|
import net.corda.core.serialization.*
|
||||||
|
import net.corda.core.internal.LazyPool
|
||||||
|
import net.corda.nodeapi.internal.serialization.CordaClassResolver
|
||||||
|
import net.corda.nodeapi.internal.serialization.SerializationScheme
|
||||||
|
|
||||||
|
// "corda" + majorVersionByte + minorVersionMSB + minorVersionLSB
|
||||||
|
val KryoHeaderV0_1: OpaqueBytes = OpaqueBytes("corda\u0000\u0000\u0001".toByteArray(Charsets.UTF_8))
|
||||||
|
|
||||||
|
private object AutoCloseableSerialisationDetector : Serializer<AutoCloseable>() {
|
||||||
|
override fun write(kryo: Kryo, output: Output, closeable: AutoCloseable) {
|
||||||
|
val message = "${closeable.javaClass.name}, which is a closeable resource, has been detected during flow checkpointing. " +
|
||||||
|
"Restoring such resources across node restarts is not supported. Make sure code accessing it is " +
|
||||||
|
"confined to a private method or the reference is nulled out."
|
||||||
|
throw UnsupportedOperationException(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(kryo: Kryo, input: Input, type: Class<AutoCloseable>) = throw IllegalStateException("Should not reach here!")
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractKryoSerializationScheme : SerializationScheme {
|
||||||
|
private val kryoPoolsForContexts = ConcurrentHashMap<Pair<ClassWhitelist, ClassLoader>, KryoPool>()
|
||||||
|
|
||||||
|
protected abstract fun rpcClientKryoPool(context: SerializationContext): KryoPool
|
||||||
|
protected abstract fun rpcServerKryoPool(context: SerializationContext): KryoPool
|
||||||
|
|
||||||
|
private fun getPool(context: SerializationContext): KryoPool {
|
||||||
|
return kryoPoolsForContexts.computeIfAbsent(Pair(context.whitelist, context.deserializationClassLoader)) {
|
||||||
|
when (context.useCase) {
|
||||||
|
SerializationContext.UseCase.Checkpoint ->
|
||||||
|
KryoPool.Builder {
|
||||||
|
val serializer = Fiber.getFiberSerializer(false) as KryoSerializer
|
||||||
|
val classResolver = CordaClassResolver(context).apply { setKryo(serializer.kryo) }
|
||||||
|
// TODO The ClassResolver can only be set in the Kryo constructor and Quasar doesn't provide us with a way of doing that
|
||||||
|
val field = Kryo::class.java.getDeclaredField("classResolver").apply { isAccessible = true }
|
||||||
|
serializer.kryo.apply {
|
||||||
|
field.set(this, classResolver)
|
||||||
|
DefaultKryoCustomizer.customize(this)
|
||||||
|
addDefaultSerializer(AutoCloseable::class.java, AutoCloseableSerialisationDetector)
|
||||||
|
register(ClosureSerializer.Closure::class.java, CordaClosureSerializer)
|
||||||
|
classLoader = it.second
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
SerializationContext.UseCase.RPCClient ->
|
||||||
|
rpcClientKryoPool(context)
|
||||||
|
SerializationContext.UseCase.RPCServer ->
|
||||||
|
rpcServerKryoPool(context)
|
||||||
|
else ->
|
||||||
|
KryoPool.Builder {
|
||||||
|
DefaultKryoCustomizer.customize(CordaKryo(CordaClassResolver(context))).apply { classLoader = it.second }
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T : Any> withContext(kryo: Kryo, context: SerializationContext, block: (Kryo) -> T): T {
|
||||||
|
kryo.context.ensureCapacity(context.properties.size)
|
||||||
|
context.properties.forEach { kryo.context.put(it.key, it.value) }
|
||||||
|
try {
|
||||||
|
return block(kryo)
|
||||||
|
} finally {
|
||||||
|
kryo.context.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
|
||||||
|
val pool = getPool(context)
|
||||||
|
val headerSize = KryoHeaderV0_1.size
|
||||||
|
val header = byteSequence.take(headerSize)
|
||||||
|
if (header != KryoHeaderV0_1) {
|
||||||
|
throw KryoException("Serialized bytes header does not match expected format.")
|
||||||
|
}
|
||||||
|
Input(byteSequence.bytes, byteSequence.offset + headerSize, byteSequence.size - headerSize).use { input ->
|
||||||
|
return pool.run { kryo ->
|
||||||
|
withContext(kryo, context) {
|
||||||
|
if (context.objectReferencesEnabled) {
|
||||||
|
uncheckedCast(kryo.readClassAndObject(input))
|
||||||
|
} else {
|
||||||
|
kryo.withoutReferences { uncheckedCast<Any?, T>(kryo.readClassAndObject(input)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
|
||||||
|
val pool = getPool(context)
|
||||||
|
return pool.run { kryo ->
|
||||||
|
withContext(kryo, context) {
|
||||||
|
serializeOutputStreamPool.run { stream ->
|
||||||
|
serializeBufferPool.run { buffer ->
|
||||||
|
Output(buffer).use {
|
||||||
|
it.outputStream = stream
|
||||||
|
it.writeBytes(KryoHeaderV0_1.bytes)
|
||||||
|
if (context.objectReferencesEnabled) {
|
||||||
|
kryo.writeClassAndObject(it, obj)
|
||||||
|
} else {
|
||||||
|
kryo.withoutReferences { kryo.writeClassAndObject(it, obj) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SerializedBytes(stream.toByteArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val serializeBufferPool = LazyPool(
|
||||||
|
newInstance = { ByteArray(64 * 1024) }
|
||||||
|
)
|
||||||
|
|
||||||
|
private val serializeOutputStreamPool = LazyPool(
|
||||||
|
clear = ByteArrayOutputStream::reset,
|
||||||
|
shouldReturnToPool = { it.size() < 256 * 1024 }, // Discard if it grew too large
|
||||||
|
newInstance = { ByteArrayOutputStream(64 * 1024) }
|
||||||
|
)
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.nodeapi.internal.serialization
|
package net.corda.nodeapi.internal.serialization.kryo
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.Kryo
|
import com.esotericsoftware.kryo.Kryo
|
||||||
import com.esotericsoftware.kryo.KryoException
|
import com.esotericsoftware.kryo.KryoException
|
@ -5,6 +5,8 @@ import net.corda.core.serialization.SerializationContext;
|
|||||||
import net.corda.core.serialization.SerializationFactory;
|
import net.corda.core.serialization.SerializationFactory;
|
||||||
import net.corda.core.serialization.SerializedBytes;
|
import net.corda.core.serialization.SerializedBytes;
|
||||||
import net.corda.testing.SerializationEnvironmentRule;
|
import net.corda.testing.SerializationEnvironmentRule;
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.CordaClosureBlacklistSerializer;
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoSerializationSchemeKt;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -31,7 +33,7 @@ public final class ForbiddenLambdaSerializationTests {
|
|||||||
EnumSet<SerializationContext.UseCase> contexts = EnumSet.complementOf(EnumSet.of(SerializationContext.UseCase.Checkpoint));
|
EnumSet<SerializationContext.UseCase> contexts = EnumSet.complementOf(EnumSet.of(SerializationContext.UseCase.Checkpoint));
|
||||||
|
|
||||||
contexts.forEach(ctx -> {
|
contexts.forEach(ctx -> {
|
||||||
SerializationContext context = new SerializationContextImpl(SerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx);
|
SerializationContext context = new SerializationContextImpl(KryoSerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx);
|
||||||
|
|
||||||
String value = "Hey";
|
String value = "Hey";
|
||||||
Callable<String> target = (Callable<String> & Serializable) () -> value;
|
Callable<String> target = (Callable<String> & Serializable) () -> value;
|
||||||
@ -54,7 +56,7 @@ public final class ForbiddenLambdaSerializationTests {
|
|||||||
EnumSet<SerializationContext.UseCase> contexts = EnumSet.complementOf(EnumSet.of(SerializationContext.UseCase.Checkpoint));
|
EnumSet<SerializationContext.UseCase> contexts = EnumSet.complementOf(EnumSet.of(SerializationContext.UseCase.Checkpoint));
|
||||||
|
|
||||||
contexts.forEach(ctx -> {
|
contexts.forEach(ctx -> {
|
||||||
SerializationContext context = new SerializationContextImpl(SerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx);
|
SerializationContext context = new SerializationContextImpl(KryoSerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx);
|
||||||
|
|
||||||
String value = "Hey";
|
String value = "Hey";
|
||||||
Callable<String> target = () -> value;
|
Callable<String> target = () -> value;
|
||||||
|
@ -5,6 +5,8 @@ import net.corda.core.serialization.SerializationContext;
|
|||||||
import net.corda.core.serialization.SerializationFactory;
|
import net.corda.core.serialization.SerializationFactory;
|
||||||
import net.corda.core.serialization.SerializedBytes;
|
import net.corda.core.serialization.SerializedBytes;
|
||||||
import net.corda.testing.SerializationEnvironmentRule;
|
import net.corda.testing.SerializationEnvironmentRule;
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.CordaClosureSerializer;
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoSerializationSchemeKt;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -24,7 +26,7 @@ public final class LambdaCheckpointSerializationTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
factory = testSerialization.env.getSERIALIZATION_FACTORY();
|
factory = testSerialization.env.getSERIALIZATION_FACTORY();
|
||||||
context = new SerializationContextImpl(SerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, SerializationContext.UseCase.Checkpoint);
|
context = new SerializationContextImpl(KryoSerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, SerializationContext.UseCase.Checkpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -10,6 +10,8 @@ import net.corda.core.node.services.AttachmentStorage
|
|||||||
import net.corda.core.serialization.*
|
import net.corda.core.serialization.*
|
||||||
import net.corda.nodeapi.internal.AttachmentsClassLoader
|
import net.corda.nodeapi.internal.AttachmentsClassLoader
|
||||||
import net.corda.nodeapi.internal.AttachmentsClassLoaderTests
|
import net.corda.nodeapi.internal.AttachmentsClassLoaderTests
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.CordaKryo
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||||
import net.corda.testing.node.MockAttachmentStorage
|
import net.corda.testing.node.MockAttachmentStorage
|
||||||
import net.corda.testing.rigorousMock
|
import net.corda.testing.rigorousMock
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
@ -13,6 +13,7 @@ import net.corda.core.utilities.ProgressTracker
|
|||||||
import net.corda.core.utilities.sequence
|
import net.corda.core.utilities.sequence
|
||||||
import net.corda.node.serialization.KryoServerSerializationScheme
|
import net.corda.node.serialization.KryoServerSerializationScheme
|
||||||
import net.corda.node.services.persistence.NodeAttachmentService
|
import net.corda.node.services.persistence.NodeAttachmentService
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||||
import net.corda.testing.ALICE_PUBKEY
|
import net.corda.testing.ALICE_PUBKEY
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.node.services.statemachine.SessionData
|
|||||||
import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput
|
import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.Envelope
|
import net.corda.nodeapi.internal.serialization.amqp.Envelope
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
|
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||||
import net.corda.testing.amqpSpecific
|
import net.corda.testing.amqpSpecific
|
||||||
import net.corda.testing.kryoSpecific
|
import net.corda.testing.kryoSpecific
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.serialization.CordaSerializable
|
|||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.node.services.statemachine.SessionData
|
import net.corda.node.services.statemachine.SessionData
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||||
import net.corda.testing.amqpSpecific
|
import net.corda.testing.amqpSpecific
|
||||||
import net.corda.testing.kryoSpecific
|
import net.corda.testing.kryoSpecific
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
|
@ -5,6 +5,10 @@ import com.esotericsoftware.kryo.KryoException
|
|||||||
import com.esotericsoftware.kryo.io.Output
|
import com.esotericsoftware.kryo.io.Output
|
||||||
import net.corda.core.serialization.*
|
import net.corda.core.serialization.*
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.CordaKryo
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||||
|
import net.corda.testing.TestDependencyInjectionBase
|
||||||
import net.corda.testing.rigorousMock
|
import net.corda.testing.rigorousMock
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
@ -5,6 +5,7 @@ import com.esotericsoftware.kryo.util.DefaultClassResolver
|
|||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.node.services.statemachine.SessionData
|
import net.corda.node.services.statemachine.SessionData
|
||||||
|
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||||
import net.corda.testing.kryoSpecific
|
import net.corda.testing.kryoSpecific
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import org.junit.Assert.assertArrayEquals
|
import org.junit.Assert.assertArrayEquals
|
||||||
|
@ -13,7 +13,6 @@ import net.corda.core.serialization.CordaSerializable
|
|||||||
import net.corda.core.serialization.SerializationFactory
|
import net.corda.core.serialization.SerializationFactory
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.client.rpc.RPCException
|
import net.corda.client.rpc.RPCException
|
||||||
import net.corda.nodeapi.internal.serialization.AbstractAMQPSerializationScheme
|
|
||||||
import net.corda.nodeapi.internal.serialization.AllWhitelist
|
import net.corda.nodeapi.internal.serialization.AllWhitelist
|
||||||
import net.corda.nodeapi.internal.serialization.EmptyWhitelist
|
import net.corda.nodeapi.internal.serialization.EmptyWhitelist
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive
|
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive
|
||||||
|
@ -19,9 +19,6 @@ configurations {
|
|||||||
|
|
||||||
integrationTestCompile.extendsFrom testCompile
|
integrationTestCompile.extendsFrom testCompile
|
||||||
integrationTestRuntime.extendsFrom testRuntime
|
integrationTestRuntime.extendsFrom testRuntime
|
||||||
|
|
||||||
smokeTestCompile.extendsFrom compile
|
|
||||||
smokeTestRuntime.extendsFrom runtime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@ -40,20 +37,6 @@ sourceSets {
|
|||||||
srcDir file('src/integration-test/resources')
|
srcDir file('src/integration-test/resources')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
smokeTest {
|
|
||||||
kotlin {
|
|
||||||
// We must NOT have any Node code on the classpath, so do NOT
|
|
||||||
// include the test or integrationTest dependencies here.
|
|
||||||
compileClasspath += main.output
|
|
||||||
runtimeClasspath += main.output
|
|
||||||
srcDir file('src/smoke-test/kotlin')
|
|
||||||
}
|
|
||||||
java {
|
|
||||||
compileClasspath += main.output
|
|
||||||
runtimeClasspath += main.output
|
|
||||||
srcDir file('src/smoke-test/java')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use manual resource copying of log4j2.xml rather than source sets.
|
// Use manual resource copying of log4j2.xml rather than source sets.
|
||||||
@ -62,13 +45,6 @@ processResources {
|
|||||||
from file("$rootDir/config/dev/log4j2.xml")
|
from file("$rootDir/config/dev/log4j2.xml")
|
||||||
}
|
}
|
||||||
|
|
||||||
processSmokeTestResources {
|
|
||||||
// Bring in the fully built corda.jar for use by NodeFactory in the smoke tests
|
|
||||||
from(project(':node:capsule').tasks.buildCordaJAR) {
|
|
||||||
rename 'corda-(.*)', 'corda.jar'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// To find potential version conflicts, run "gradle htmlDependencyReport" and then look in
|
// To find potential version conflicts, run "gradle htmlDependencyReport" and then look in
|
||||||
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
|
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
|
||||||
|
|
||||||
@ -184,11 +160,6 @@ dependencies {
|
|||||||
integrationTestCompile "junit:junit:$junit_version"
|
integrationTestCompile "junit:junit:$junit_version"
|
||||||
integrationTestCompile "org.assertj:assertj-core:${assertj_version}"
|
integrationTestCompile "org.assertj:assertj-core:${assertj_version}"
|
||||||
|
|
||||||
// Smoke tests do NOT have any Node code on the classpath!
|
|
||||||
smokeTestCompile project(':smoke-test-utils')
|
|
||||||
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
|
|
||||||
smokeTestCompile "junit:junit:$junit_version"
|
|
||||||
|
|
||||||
// Jetty dependencies for NetworkMapClient test.
|
// Jetty dependencies for NetworkMapClient test.
|
||||||
// Web stuff: for HTTP[S] servlets
|
// Web stuff: for HTTP[S] servlets
|
||||||
testCompile "org.eclipse.jetty:jetty-servlet:${jetty_version}"
|
testCompile "org.eclipse.jetty:jetty-servlet:${jetty_version}"
|
||||||
@ -206,17 +177,6 @@ task integrationTest(type: Test) {
|
|||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
}
|
}
|
||||||
|
|
||||||
task smokeTestJar(type: Jar) {
|
|
||||||
classifier 'smokeTests'
|
|
||||||
from sourceSets.smokeTest.output
|
|
||||||
}
|
|
||||||
|
|
||||||
task smokeTest(type: Test) {
|
|
||||||
dependsOn smokeTestJar
|
|
||||||
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
|
||||||
classpath = sourceSets.smokeTest.runtimeClasspath
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
baseName 'corda-node'
|
baseName 'corda-node'
|
||||||
}
|
}
|
||||||
|
@ -7,29 +7,24 @@ import net.corda.core.internal.div
|
|||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.internal.NodeStartup
|
import net.corda.node.internal.NodeStartup
|
||||||
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
import net.corda.nodeapi.internal.ServiceType
|
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.ProjectStructure.projectRootDir
|
import net.corda.testing.ProjectStructure.projectRootDir
|
||||||
import net.corda.testing.driver.ListenProcessDeathException
|
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Ignore
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
|
||||||
|
|
||||||
class BootTests {
|
class BootTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `java deserialization is disabled`() {
|
fun `java deserialization is disabled`() {
|
||||||
driver {
|
driver {
|
||||||
val user = User("u", "p", setOf(startFlowPermission<ObjectInputStreamFlow>()))
|
val user = User("u", "p", setOf(startFlow<ObjectInputStreamFlow>()))
|
||||||
val future = startNode(rpcUsers = listOf(user)).getOrThrow().rpcClientToNode().
|
val future = startNode(rpcUsers = listOf(user)).getOrThrow().rpcClientToNode().
|
||||||
start(user.username, user.password).proxy.startFlow(::ObjectInputStreamFlow).returnValue
|
start(user.username, user.password).proxy.startFlow(::ObjectInputStreamFlow).returnValue
|
||||||
assertThatThrownBy { future.getOrThrow() }.isInstanceOf(InvalidClassException::class.java).hasMessage("filter status: REJECTED")
|
assertThatThrownBy { future.getOrThrow() }.isInstanceOf(InvalidClassException::class.java).hasMessage("filter status: REJECTED")
|
||||||
@ -53,16 +48,6 @@ class BootTests {
|
|||||||
assertEquals(1, numberOfNodesThatLogged)
|
assertEquals(1, numberOfNodesThatLogged)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("Need rewriting to produce too big network map registration (adverticed services trick doesn't work after services removal).")
|
|
||||||
@Test
|
|
||||||
fun `node quits on failure to register with network map`() {
|
|
||||||
val tooManyAdvertisedServices = (1..100).map { ServiceInfo(ServiceType.notary.getSubType("$it")) }.toSet()
|
|
||||||
driver {
|
|
||||||
val future = startNode(providedName = ALICE.name)
|
|
||||||
assertFailsWith(ListenProcessDeathException::class) { future.getOrThrow() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
|
@ -7,7 +7,7 @@ import net.corda.core.internal.concurrent.transpose
|
|||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.unwrap
|
import net.corda.core.utilities.unwrap
|
||||||
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
@ -19,7 +19,7 @@ import org.junit.Test
|
|||||||
class CordappScanningDriverTest {
|
class CordappScanningDriverTest {
|
||||||
@Test
|
@Test
|
||||||
fun `sub-classed initiated flow pointing to the same initiating flow as its super-class`() {
|
fun `sub-classed initiated flow pointing to the same initiating flow as its super-class`() {
|
||||||
val user = User("u", "p", setOf(startFlowPermission<ReceiveFlow>()))
|
val user = User("u", "p", setOf(startFlow<ReceiveFlow>()))
|
||||||
// The driver will automatically pick up the annotated flows below
|
// The driver will automatically pick up the annotated flows below
|
||||||
driver {
|
driver {
|
||||||
val (alice, bob) = listOf(
|
val (alice, bob) = listOf(
|
||||||
|
@ -12,12 +12,12 @@ import net.corda.core.utilities.minutes
|
|||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
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.FlowPermissions.Companion.startFlowPermission
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import net.corda.testing.chooseIdentity
|
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.node.NotarySpec
|
||||||
import net.corda.testing.performance.div
|
import net.corda.testing.performance.div
|
||||||
import net.corda.testing.performance.startPublishingFixedRateInjector
|
import net.corda.testing.performance.startPublishingFixedRateInjector
|
||||||
import net.corda.testing.performance.startReporter
|
import net.corda.testing.performance.startReporter
|
||||||
@ -59,7 +59,7 @@ class NodePerformanceTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `empty flow per second`() {
|
fun `empty flow per second`() {
|
||||||
driver(startNodesInProcess = true) {
|
driver(startNodesInProcess = true) {
|
||||||
val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlowPermission<EmptyFlow>())))).get()
|
val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow<EmptyFlow>())))).get()
|
||||||
|
|
||||||
a.rpcClientToNode().use("A", "A") { connection ->
|
a.rpcClientToNode().use("A", "A") { connection ->
|
||||||
val timings = Collections.synchronizedList(ArrayList<Long>())
|
val timings = Collections.synchronizedList(ArrayList<Long>())
|
||||||
@ -89,7 +89,7 @@ class NodePerformanceTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `empty flow rate`() {
|
fun `empty flow rate`() {
|
||||||
driver(startNodesInProcess = true) {
|
driver(startNodesInProcess = true) {
|
||||||
val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlowPermission<EmptyFlow>())))).get()
|
val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow<EmptyFlow>())))).get()
|
||||||
a as NodeHandle.InProcess
|
a as NodeHandle.InProcess
|
||||||
val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics)
|
val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics)
|
||||||
a.rpcClientToNode().use("A", "A") { connection ->
|
a.rpcClientToNode().use("A", "A") { connection ->
|
||||||
@ -102,26 +102,25 @@ class NodePerformanceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `self pay rate`() {
|
fun `self pay rate`() {
|
||||||
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
val user = User("A", "A", setOf(startFlow<CashIssueFlow>(), startFlow<CashPaymentFlow>()))
|
||||||
val a = startNotaryNode(
|
driver(
|
||||||
DUMMY_NOTARY.name,
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, rpcUsers = listOf(user))),
|
||||||
rpcUsers = listOf(User("A", "A", setOf(startFlowPermission<CashIssueFlow>(), startFlowPermission<CashPaymentFlow>())))
|
startNodesInProcess = true,
|
||||||
).getOrThrow()
|
extraCordappPackagesToScan = listOf("net.corda.finance")
|
||||||
a as NodeHandle.InProcess
|
) {
|
||||||
val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics)
|
val notary = defaultNotaryNode.getOrThrow() as NodeHandle.InProcess
|
||||||
a.rpcClientToNode().use("A", "A") { connection ->
|
val metricRegistry = startReporter(shutdownManager, notary.node.services.monitoringService.metrics)
|
||||||
val notary = connection.proxy.notaryIdentities().first()
|
notary.rpcClientToNode().use("A", "A") { connection ->
|
||||||
println("ISSUING")
|
println("ISSUING")
|
||||||
val doneFutures = (1..100).toList().parallelStream().map {
|
val doneFutures = (1..100).toList().parallelStream().map {
|
||||||
connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), notary).returnValue
|
connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue
|
||||||
}.toList()
|
}.toList()
|
||||||
doneFutures.transpose().get()
|
doneFutures.transpose().get()
|
||||||
println("STARTING PAYMENT")
|
println("STARTING PAYMENT")
|
||||||
startPublishingFixedRateInjector(metricRegistry, 8, 5.minutes, 100L / TimeUnit.SECONDS) {
|
startPublishingFixedRateInjector(metricRegistry, 8, 5.minutes, 100L / TimeUnit.SECONDS) {
|
||||||
connection.proxy.startFlow(::CashPaymentFlow, 1.DOLLARS, a.nodeInfo.chooseIdentity()).returnValue.get()
|
connection.proxy.startFlow(::CashPaymentFlow, 1.DOLLARS, defaultNotaryIdentity).returnValue.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,14 +13,13 @@ import net.corda.core.internal.div
|
|||||||
import net.corda.core.internal.toLedgerTransaction
|
import net.corda.core.internal.toLedgerTransaction
|
||||||
import net.corda.core.serialization.SerializationFactory
|
import net.corda.core.serialization.SerializationFactory
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.core.utilities.seconds
|
|
||||||
import net.corda.node.internal.cordapp.CordappLoader
|
import net.corda.node.internal.cordapp.CordappLoader
|
||||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||||
import net.corda.nodeapi.User
|
import net.corda.testing.DUMMY_BANK_A
|
||||||
import net.corda.testing.*
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.driver.DriverDSLExposedInterface
|
import net.corda.testing.driver.DriverDSLExposedInterface
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
@ -53,20 +52,15 @@ class AttachmentLoadingTests {
|
|||||||
|
|
||||||
val bankAName = CordaX500Name("BankA", "Zurich", "CH")
|
val bankAName = CordaX500Name("BankA", "Zurich", "CH")
|
||||||
val bankBName = CordaX500Name("BankB", "Zurich", "CH")
|
val bankBName = CordaX500Name("BankB", "Zurich", "CH")
|
||||||
val notaryName = CordaX500Name("Notary", "Zurich", "CH")
|
val flowInitiatorClass: Class<out FlowLogic<*>> =
|
||||||
val flowInitiatorClass =
|
|
||||||
Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR)))
|
Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR)))
|
||||||
.asSubclass(FlowLogic::class.java)
|
.asSubclass(FlowLogic::class.java)
|
||||||
|
|
||||||
private fun DriverDSLExposedInterface.createTwoNodesAndNotary(): List<NodeHandle> {
|
private fun DriverDSLExposedInterface.createTwoNodes(): List<NodeHandle> {
|
||||||
val adminUser = User("admin", "admin", permissions = setOf("ALL"))
|
return listOf(
|
||||||
val nodes = listOf(
|
startNode(providedName = bankAName),
|
||||||
startNode(providedName = bankAName, rpcUsers = listOf(adminUser)),
|
startNode(providedName = bankBName)
|
||||||
startNode(providedName = bankBName, rpcUsers = listOf(adminUser)),
|
).transpose().getOrThrow()
|
||||||
startNotaryNode(providedName = notaryName, rpcUsers = listOf(adminUser), validating = false)
|
|
||||||
).transpose().getOrThrow() // Wait for all nodes to start up.
|
|
||||||
nodes.forEach { it.rpc.waitUntilNetworkReady().getOrThrow() }
|
|
||||||
return nodes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DriverDSLExposedInterface.installIsolatedCordappTo(nodeName: CordaX500Name) {
|
private fun DriverDSLExposedInterface.installIsolatedCordappTo(nodeName: CordaX500Name) {
|
||||||
@ -79,15 +73,6 @@ class AttachmentLoadingTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Due to cluster instability after nodes been started it may take some time to all the nodes to become available
|
|
||||||
// *and* discover each other to reliably communicate. Hence, eventual nature of the test.
|
|
||||||
// TODO: Remove this method and usages of it once NetworkMap service been re-worked
|
|
||||||
private fun eventuallyPassingTest(block: () -> Unit) {
|
|
||||||
eventually<Throwable, Unit>(30.seconds) {
|
|
||||||
block()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var services: Services
|
private lateinit var services: Services
|
||||||
@ -103,9 +88,8 @@ class AttachmentLoadingTests {
|
|||||||
val contractClass = appClassLoader.loadClass(ISOLATED_CONTRACT_ID).asSubclass(Contract::class.java)
|
val contractClass = appClassLoader.loadClass(ISOLATED_CONTRACT_ID).asSubclass(Contract::class.java)
|
||||||
val generateInitialMethod = contractClass.getDeclaredMethod("generateInitial", PartyAndReference::class.java, Integer.TYPE, Party::class.java)
|
val generateInitialMethod = contractClass.getDeclaredMethod("generateInitial", PartyAndReference::class.java, Integer.TYPE, Party::class.java)
|
||||||
val contract = contractClass.newInstance()
|
val contract = contractClass.newInstance()
|
||||||
val txBuilder = generateInitialMethod.invoke(contract, PartyAndReference(DUMMY_BANK_A, OpaqueBytes(kotlin.ByteArray(1))), 1, DUMMY_NOTARY) as TransactionBuilder
|
val txBuilder = generateInitialMethod.invoke(contract, DUMMY_BANK_A.ref(1), 1, DUMMY_NOTARY) as TransactionBuilder
|
||||||
val context = SerializationFactory.defaultFactory.defaultContext
|
val context = SerializationFactory.defaultFactory.defaultContext.withClassLoader(appClassLoader)
|
||||||
.withClassLoader(appClassLoader)
|
|
||||||
val ledgerTx = txBuilder.toLedgerTransaction(services, context)
|
val ledgerTx = txBuilder.toLedgerTransaction(services, context)
|
||||||
contract.verify(ledgerTx)
|
contract.verify(ledgerTx)
|
||||||
|
|
||||||
@ -119,24 +103,20 @@ class AttachmentLoadingTests {
|
|||||||
fun `test that attachments retrieved over the network are not used for code`() {
|
fun `test that attachments retrieved over the network are not used for code`() {
|
||||||
driver(initialiseSerialization = false) {
|
driver(initialiseSerialization = false) {
|
||||||
installIsolatedCordappTo(bankAName)
|
installIsolatedCordappTo(bankAName)
|
||||||
val (bankA, bankB, _) = createTwoNodesAndNotary()
|
val (bankA, bankB) = createTwoNodes()
|
||||||
eventuallyPassingTest {
|
|
||||||
assertFailsWith<UnexpectedFlowEndException>("Party C=CH,L=Zurich,O=BankB rejected session request: Don't know net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator") {
|
assertFailsWith<UnexpectedFlowEndException>("Party C=CH,L=Zurich,O=BankB rejected session request: Don't know net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator") {
|
||||||
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `tests that if the attachment is loaded on both sides already that a flow can run`() {
|
fun `tests that if the attachment is loaded on both sides already that a flow can run`() {
|
||||||
driver(initialiseSerialization = false) {
|
driver(initialiseSerialization = false) {
|
||||||
installIsolatedCordappTo(bankAName)
|
installIsolatedCordappTo(bankAName)
|
||||||
installIsolatedCordappTo(bankBName)
|
installIsolatedCordappTo(bankBName)
|
||||||
val (bankA, bankB, _) = createTwoNodesAndNotary()
|
val (bankA, bankB) = createTwoNodes()
|
||||||
eventuallyPassingTest {
|
|
||||||
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -13,6 +13,8 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.deleteIfExists
|
import net.corda.core.internal.deleteIfExists
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.node.NotaryInfo
|
||||||
|
import net.corda.core.node.services.NotaryService
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
@ -21,15 +23,16 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.config.BFTSMaRtConfiguration
|
import net.corda.node.services.config.BFTSMaRtConfiguration
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
|
|
||||||
import net.corda.node.services.transactions.minClusterSize
|
import net.corda.node.services.transactions.minClusterSize
|
||||||
import net.corda.node.services.transactions.minCorrectReplicas
|
import net.corda.node.services.transactions.minCorrectReplicas
|
||||||
import net.corda.node.utilities.ServiceIdentityGenerator
|
import net.corda.node.utilities.ServiceIdentityGenerator
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
|
import net.corda.testing.common.internal.NetworkParametersCopier
|
||||||
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.dummyCommand
|
import net.corda.testing.dummyCommand
|
||||||
import net.corda.testing.getDefaultNotary
|
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
|
import net.corda.testing.node.MockNetwork.MockNode
|
||||||
import net.corda.testing.node.MockNodeParameters
|
import net.corda.testing.node.MockNodeParameters
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -38,39 +41,47 @@ import kotlin.test.assertEquals
|
|||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class BFTNotaryServiceTests {
|
class BFTNotaryServiceTests {
|
||||||
companion object {
|
|
||||||
private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH")
|
|
||||||
}
|
|
||||||
|
|
||||||
private val mockNet = MockNetwork()
|
private val mockNet = MockNetwork()
|
||||||
private val node = mockNet.createNode()
|
private lateinit var notary: Party
|
||||||
|
private lateinit var node: StartedNode<MockNode>
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun stopNodes() {
|
fun stopNodes() {
|
||||||
mockNet.stopNodes()
|
mockNet.stopNodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bftNotaryCluster(clusterSize: Int, exposeRaces: Boolean = false) {
|
private fun startBftClusterAndNode(clusterSize: Int, exposeRaces: Boolean = false) {
|
||||||
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
|
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
|
||||||
val replicaIds = (0 until clusterSize)
|
val replicaIds = (0 until clusterSize)
|
||||||
ServiceIdentityGenerator.generateToDisk(
|
|
||||||
|
notary = ServiceIdentityGenerator.generateToDisk(
|
||||||
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
||||||
clusterName)
|
CordaX500Name("BFT", "Zurich", "CH"),
|
||||||
|
NotaryService.constructId(validating = false, bft = true))
|
||||||
|
|
||||||
|
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notary, false))))
|
||||||
|
|
||||||
val clusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) }
|
val clusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) }
|
||||||
replicaIds.forEach { replicaId ->
|
|
||||||
mockNet.createNode(MockNodeParameters(configOverrides = {
|
val nodes = replicaIds.map { replicaId ->
|
||||||
|
mockNet.createUnstartedNode(MockNodeParameters(configOverrides = {
|
||||||
val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces))
|
val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces))
|
||||||
doReturn(notary).whenever(it).notary
|
doReturn(notary).whenever(it).notary
|
||||||
}))
|
}))
|
||||||
}
|
} + mockNet.createUnstartedNode()
|
||||||
mockNet.runNetwork() // Exchange initial network map registration messages.
|
|
||||||
|
// MockNetwork doesn't support BFT clusters, so we create all the nodes we need unstarted, and then install the
|
||||||
|
// network-parameters in their directories before they're started.
|
||||||
|
node = nodes.map { node ->
|
||||||
|
networkParameters.install(mockNet.baseDirectory(node.id))
|
||||||
|
node.start()
|
||||||
|
}.last()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Failure mode is the redundant replica gets stuck in startup, so we can't dispose it cleanly at the end. */
|
/** Failure mode is the redundant replica gets stuck in startup, so we can't dispose it cleanly at the end. */
|
||||||
@Test
|
@Test
|
||||||
fun `all replicas start even if there is a new consensus during startup`() {
|
fun `all replicas start even if there is a new consensus during startup`() {
|
||||||
bftNotaryCluster(minClusterSize(1), true) // This true adds a sleep to expose the race.
|
startBftClusterAndNode(minClusterSize(1), exposeRaces = true) // This true adds a sleep to expose the race.
|
||||||
val notary = node.services.getDefaultNotary()
|
|
||||||
val f = node.run {
|
val f = node.run {
|
||||||
val trivialTx = signInitialTransaction(notary) {
|
val trivialTx = signInitialTransaction(notary) {
|
||||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
||||||
@ -94,8 +105,7 @@ class BFTNotaryServiceTests {
|
|||||||
|
|
||||||
private fun detectDoubleSpend(faultyReplicas: Int) {
|
private fun detectDoubleSpend(faultyReplicas: Int) {
|
||||||
val clusterSize = minClusterSize(faultyReplicas)
|
val clusterSize = minClusterSize(faultyReplicas)
|
||||||
bftNotaryCluster(clusterSize)
|
startBftClusterAndNode(clusterSize)
|
||||||
val notary = node.services.getDefaultNotary()
|
|
||||||
node.run {
|
node.run {
|
||||||
val issueTx = signInitialTransaction(notary) {
|
val issueTx = signInitialTransaction(notary) {
|
||||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
||||||
@ -138,15 +148,13 @@ class BFTNotaryServiceTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun StartedNode<*>.signInitialTransaction(
|
private fun StartedNode<*>.signInitialTransaction(notary: Party, block: TransactionBuilder.() -> Any?): SignedTransaction {
|
||||||
notary: Party,
|
|
||||||
block: TransactionBuilder.() -> Any?
|
|
||||||
): SignedTransaction {
|
|
||||||
return services.signInitialTransaction(
|
return services.signInitialTransaction(
|
||||||
TransactionBuilder(notary).apply {
|
TransactionBuilder(notary).apply {
|
||||||
addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey))
|
addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey))
|
||||||
block()
|
block()
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,65 +11,70 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.finance.POUNDS
|
import net.corda.finance.POUNDS
|
||||||
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.FlowPermissions.Companion.startFlowPermission
|
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.node.DriverBasedTest
|
import net.corda.testing.node.ClusterSpec
|
||||||
|
import net.corda.testing.node.NotarySpec
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
class DistributedServiceTests : DriverBasedTest() {
|
class DistributedServiceTests {
|
||||||
lateinit var alice: NodeHandle
|
private lateinit var alice: NodeHandle
|
||||||
lateinit var notaries: List<NodeHandle.OutOfProcess>
|
private lateinit var notaryNodes: List<NodeHandle.OutOfProcess>
|
||||||
lateinit var aliceProxy: CordaRPCOps
|
private lateinit var aliceProxy: CordaRPCOps
|
||||||
lateinit var raftNotaryIdentity: Party
|
private lateinit var raftNotaryIdentity: Party
|
||||||
lateinit var notaryStateMachines: Observable<Pair<Party, StateMachineUpdate>>
|
private lateinit var notaryStateMachines: Observable<Pair<Party, StateMachineUpdate>>
|
||||||
|
|
||||||
override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) {
|
private fun setup(testBlock: () -> Unit) {
|
||||||
// Start Alice and 3 notaries in a RAFT cluster
|
|
||||||
val clusterSize = 3
|
|
||||||
val testUser = User("test", "test", permissions = setOf(
|
val testUser = User("test", "test", permissions = setOf(
|
||||||
startFlowPermission<CashIssueFlow>(),
|
startFlow<CashIssueFlow>(),
|
||||||
startFlowPermission<CashPaymentFlow>())
|
startFlow<CashPaymentFlow>(),
|
||||||
)
|
invokeRpc(CordaRPCOps::nodeInfo),
|
||||||
val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser))
|
invokeRpc(CordaRPCOps::stateMachinesFeed))
|
||||||
val notariesFuture = startNotaryCluster(
|
|
||||||
DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.id),
|
|
||||||
rpcUsers = listOf(testUser),
|
|
||||||
clusterSize = clusterSize
|
|
||||||
)
|
)
|
||||||
|
|
||||||
alice = aliceFuture.get()
|
driver(
|
||||||
val (notaryIdentity, notaryNodes) = notariesFuture.get()
|
extraCordappPackagesToScan = listOf("net.corda.finance.contracts"),
|
||||||
raftNotaryIdentity = notaryIdentity
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, rpcUsers = listOf(testUser), cluster = ClusterSpec.Raft(clusterSize = 3))))
|
||||||
notaries = notaryNodes.map { it as NodeHandle.OutOfProcess }
|
{
|
||||||
|
alice = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)).getOrThrow()
|
||||||
|
raftNotaryIdentity = defaultNotaryIdentity
|
||||||
|
notaryNodes = defaultNotaryHandle.nodeHandles.getOrThrow().map { it as NodeHandle.OutOfProcess }
|
||||||
|
|
||||||
|
assertThat(notaryNodes).hasSize(3)
|
||||||
|
|
||||||
|
for (notaryNode in notaryNodes) {
|
||||||
|
assertThat(notaryNode.nodeInfo.legalIdentities).contains(raftNotaryIdentity)
|
||||||
|
}
|
||||||
|
|
||||||
assertEquals(notaries.size, clusterSize)
|
|
||||||
// Check that each notary has different identity as a node.
|
// Check that each notary has different identity as a node.
|
||||||
assertEquals(notaries.size, notaries.map { it.nodeInfo.chooseIdentity() }.toSet().size)
|
assertThat(notaryNodes.flatMap { it.nodeInfo.legalIdentities - raftNotaryIdentity }.toSet()).hasSameSizeAs(notaryNodes)
|
||||||
|
|
||||||
// Connect to Alice and the notaries
|
// Connect to Alice and the notaries
|
||||||
fun connectRpc(node: NodeHandle): CordaRPCOps {
|
fun connectRpc(node: NodeHandle): CordaRPCOps {
|
||||||
val client = node.rpcClientToNode()
|
val client = node.rpcClientToNode()
|
||||||
return client.start("test", "test").proxy
|
return client.start("test", "test").proxy
|
||||||
}
|
}
|
||||||
aliceProxy = connectRpc(alice)
|
aliceProxy = connectRpc(alice)
|
||||||
val rpcClientsToNotaries = notaries.map(::connectRpc)
|
val rpcClientsToNotaries = notaryNodes.map(::connectRpc)
|
||||||
notaryStateMachines = Observable.from(rpcClientsToNotaries.map { proxy ->
|
notaryStateMachines = Observable.from(rpcClientsToNotaries.map { proxy ->
|
||||||
proxy.stateMachinesFeed().updates.map { Pair(proxy.nodeInfo().chooseIdentity(), it) }
|
proxy.stateMachinesFeed().updates.map { Pair(proxy.nodeInfo().chooseIdentity(), it) }
|
||||||
}).flatMap { it.onErrorResumeNext(Observable.empty()) }.bufferUntilSubscribed()
|
}).flatMap { it.onErrorResumeNext(Observable.empty()) }.bufferUntilSubscribed()
|
||||||
|
|
||||||
runTest()
|
testBlock()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Use a dummy distributed service rather than a Raft Notary Service as this test is only about Artemis' ability
|
// TODO Use a dummy distributed service rather than a Raft Notary Service as this test is only about Artemis' ability
|
||||||
// to handle distributed services
|
// to handle distributed services
|
||||||
@Test
|
@Test
|
||||||
fun `requests are distributed evenly amongst the nodes`() {
|
fun `requests are distributed evenly amongst the nodes`() = setup {
|
||||||
// Issue 100 pounds, then pay ourselves 50x2 pounds
|
// Issue 100 pounds, then pay ourselves 50x2 pounds
|
||||||
issueCash(100.POUNDS)
|
issueCash(100.POUNDS)
|
||||||
|
|
||||||
@ -97,7 +102,7 @@ class DistributedServiceTests : DriverBasedTest() {
|
|||||||
|
|
||||||
// TODO This should be in RaftNotaryServiceTests
|
// TODO This should be in RaftNotaryServiceTests
|
||||||
@Test
|
@Test
|
||||||
fun `cluster survives if a notary is killed`() {
|
fun `cluster survives if a notary is killed`() = setup {
|
||||||
// Issue 100 pounds, then pay ourselves 10x5 pounds
|
// Issue 100 pounds, then pay ourselves 10x5 pounds
|
||||||
issueCash(100.POUNDS)
|
issueCash(100.POUNDS)
|
||||||
|
|
||||||
@ -105,8 +110,8 @@ class DistributedServiceTests : DriverBasedTest() {
|
|||||||
paySelf(5.POUNDS)
|
paySelf(5.POUNDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now kill a notary
|
// Now kill a notary node
|
||||||
with(notaries[0].process) {
|
with(notaryNodes[0].process) {
|
||||||
destroy()
|
destroy()
|
||||||
waitFor()
|
waitFor()
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ import net.corda.testing.contracts.DummyContract
|
|||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.dummyCommand
|
import net.corda.testing.dummyCommand
|
||||||
|
import net.corda.testing.node.ClusterSpec
|
||||||
|
import net.corda.testing.node.NotarySpec
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -28,13 +30,16 @@ class RaftNotaryServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `detect double spend`() {
|
fun `detect double spend`() {
|
||||||
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
|
driver(
|
||||||
val (notaryParty) = startNotaryCluster(notaryName, 3).getOrThrow()
|
startNodesInProcess = true,
|
||||||
|
extraCordappPackagesToScan = listOf("net.corda.testing.contracts"),
|
||||||
|
notarySpecs = listOf(NotarySpec(notaryName, cluster = ClusterSpec.Raft(clusterSize = 3))))
|
||||||
|
{
|
||||||
val bankA = startNode(providedName = DUMMY_BANK_A.name).map { (it as NodeHandle.InProcess).node }.getOrThrow()
|
val bankA = startNode(providedName = DUMMY_BANK_A.name).map { (it as NodeHandle.InProcess).node }.getOrThrow()
|
||||||
|
|
||||||
val inputState = issueState(bankA, notaryParty)
|
val inputState = issueState(bankA, defaultNotaryIdentity)
|
||||||
|
|
||||||
val firstTxBuilder = TransactionBuilder(notaryParty)
|
val firstTxBuilder = TransactionBuilder(defaultNotaryIdentity)
|
||||||
.addInputState(inputState)
|
.addInputState(inputState)
|
||||||
.addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey))
|
.addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey))
|
||||||
val firstSpendTx = bankA.services.signInitialTransaction(firstTxBuilder)
|
val firstSpendTx = bankA.services.signInitialTransaction(firstTxBuilder)
|
||||||
@ -42,7 +47,7 @@ class RaftNotaryServiceTests {
|
|||||||
val firstSpend = bankA.services.startFlow(NotaryFlow.Client(firstSpendTx))
|
val firstSpend = bankA.services.startFlow(NotaryFlow.Client(firstSpendTx))
|
||||||
firstSpend.resultFuture.getOrThrow()
|
firstSpend.resultFuture.getOrThrow()
|
||||||
|
|
||||||
val secondSpendBuilder = TransactionBuilder(notaryParty).withItems(inputState).run {
|
val secondSpendBuilder = TransactionBuilder(defaultNotaryIdentity).withItems(inputState).run {
|
||||||
val dummyState = DummyContract.SingleOwnerState(0, bankA.info.chooseIdentity())
|
val dummyState = DummyContract.SingleOwnerState(0, bankA.info.chooseIdentity())
|
||||||
addOutputState(dummyState, DummyContract.PROGRAM_ID)
|
addOutputState(dummyState, DummyContract.PROGRAM_ID)
|
||||||
addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey))
|
addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey))
|
||||||
|
@ -6,15 +6,11 @@ import net.corda.cordform.CordformNode
|
|||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.KeyManagementService
|
|
||||||
import net.corda.node.services.identity.InMemoryIdentityService
|
|
||||||
import net.corda.nodeapi.NodeInfoFilesCopier
|
import net.corda.nodeapi.NodeInfoFilesCopier
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.ALICE_KEY
|
import net.corda.testing.ALICE_KEY
|
||||||
import net.corda.testing.DEV_TRUST_ROOT
|
|
||||||
import net.corda.testing.getTestPartyAndCertificate
|
import net.corda.testing.getTestPartyAndCertificate
|
||||||
import net.corda.testing.internal.NodeBasedTest
|
import net.corda.testing.internal.NodeBasedTest
|
||||||
import net.corda.testing.node.MockKeyManagementService
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.contentOf
|
import org.assertj.core.api.Assertions.contentOf
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -31,7 +27,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
|||||||
val nodeInfo = NodeInfo(listOf(), listOf(getTestPartyAndCertificate(ALICE)), 0, 0)
|
val nodeInfo = NodeInfo(listOf(), listOf(getTestPartyAndCertificate(ALICE)), 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var keyManagementService: KeyManagementService
|
|
||||||
private lateinit var nodeInfoPath: Path
|
private lateinit var nodeInfoPath: Path
|
||||||
private val scheduler = TestScheduler()
|
private val scheduler = TestScheduler()
|
||||||
private val testSubscriber = TestSubscriber<NodeInfo>()
|
private val testSubscriber = TestSubscriber<NodeInfo>()
|
||||||
@ -41,16 +36,15 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun start() {
|
fun start() {
|
||||||
val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
|
|
||||||
keyManagementService = MockKeyManagementService(identityService, ALICE_KEY)
|
|
||||||
nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler = scheduler)
|
nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler = scheduler)
|
||||||
nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY
|
nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `save a NodeInfo`() {
|
fun `save a NodeInfo`() {
|
||||||
assertEquals(0, tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.size)
|
assertEquals(0,
|
||||||
NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, keyManagementService)
|
tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.size)
|
||||||
|
NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, ALICE_KEY)
|
||||||
|
|
||||||
val nodeInfoFiles = tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }
|
val nodeInfoFiles = tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }
|
||||||
assertEquals(1, nodeInfoFiles.size)
|
assertEquals(1, nodeInfoFiles.size)
|
||||||
@ -65,7 +59,7 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
|||||||
fun `save a NodeInfo to JimFs`() {
|
fun `save a NodeInfo to JimFs`() {
|
||||||
val jimFs = Jimfs.newFileSystem(Configuration.unix())
|
val jimFs = Jimfs.newFileSystem(Configuration.unix())
|
||||||
val jimFolder = jimFs.getPath("/nodeInfo")
|
val jimFolder = jimFs.getPath("/nodeInfo")
|
||||||
NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, keyManagementService)
|
NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, ALICE_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -134,6 +128,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
|||||||
|
|
||||||
// Write a nodeInfo under the right path.
|
// Write a nodeInfo under the right path.
|
||||||
private fun createNodeInfoFileInPath(nodeInfo: NodeInfo) {
|
private fun createNodeInfoFileInPath(nodeInfo: NodeInfo) {
|
||||||
NodeInfoWatcher.saveToFile(nodeInfoPath, nodeInfo, keyManagementService)
|
NodeInfoWatcher.saveToFile(nodeInfoPath, nodeInfo, ALICE_KEY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import net.corda.node.internal.Node
|
|||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_REGULATOR
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.internal.NodeBasedTest
|
import net.corda.testing.internal.NodeBasedTest
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -16,9 +16,9 @@ import org.junit.Test
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class PersistentNetworkMapCacheTest : NodeBasedTest() {
|
class PersistentNetworkMapCacheTest : NodeBasedTest() {
|
||||||
private val partiesList = listOf(DUMMY_NOTARY, ALICE, BOB)
|
private val partiesList = listOf(DUMMY_REGULATOR, ALICE, BOB)
|
||||||
private val addressesMap = HashMap<CordaX500Name, NetworkHostAndPort>()
|
private val addressesMap = HashMap<CordaX500Name, NetworkHostAndPort>()
|
||||||
private val infos: MutableSet<NodeInfo> = HashSet()
|
private val infos = HashSet<NodeInfo>()
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun start() {
|
fun start() {
|
||||||
@ -37,8 +37,8 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() {
|
|||||||
alice.database.transaction {
|
alice.database.transaction {
|
||||||
val res = netCache.getNodeByLegalIdentity(alice.info.chooseIdentity())
|
val res = netCache.getNodeByLegalIdentity(alice.info.chooseIdentity())
|
||||||
assertEquals(alice.info, res)
|
assertEquals(alice.info, res)
|
||||||
val res2 = netCache.getNodeByLegalName(DUMMY_NOTARY.name)
|
val res2 = netCache.getNodeByLegalName(DUMMY_REGULATOR.name)
|
||||||
assertEquals(infos.singleOrNull { DUMMY_NOTARY.name in it.legalIdentitiesAndCerts.map { it.name } }, res2)
|
assertEquals(infos.singleOrNull { DUMMY_REGULATOR.name in it.legalIdentities.map { it.name } }, res2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import net.corda.core.messaging.startFlow
|
|||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import net.corda.testing.aliceBobAndNotary
|
import net.corda.testing.aliceAndBob
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.contracts.DummyState
|
import net.corda.testing.contracts.DummyState
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
@ -65,7 +65,7 @@ class LargeTransactionsTest {
|
|||||||
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2)
|
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2)
|
||||||
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3)
|
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3)
|
||||||
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
|
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
|
||||||
val (alice, _, _) = aliceBobAndNotary()
|
val (alice, _) = aliceAndBob()
|
||||||
alice.useRPC {
|
alice.useRPC {
|
||||||
val hash1 = it.uploadAttachment(bigFile1.inputStream)
|
val hash1 = it.uploadAttachment(bigFile1.inputStream)
|
||||||
val hash2 = it.uploadAttachment(bigFile2.inputStream)
|
val hash2 = it.uploadAttachment(bigFile2.inputStream)
|
||||||
|
@ -18,7 +18,6 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
|||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
import org.bouncycastle.asn1.x509.NameConstraints
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@ import net.corda.testing.chooseIdentity
|
|||||||
import net.corda.testing.driver.DriverDSLExposedInterface
|
import net.corda.testing.driver.DriverDSLExposedInterface
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.node.ClusterSpec
|
||||||
|
import net.corda.testing.node.NotarySpec
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -35,16 +37,14 @@ class P2PMessagingTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `communicating with a distributed service which we're part of`() {
|
fun `communicating with a distributed service which we're part of`() {
|
||||||
driver(startNodesInProcess = true) {
|
startDriverWithDistributedService { distributedService ->
|
||||||
val distributedService = startDistributedService()
|
|
||||||
assertAllNodesAreUsed(distributedService, DISTRIBUTED_SERVICE_NAME, distributedService[0])
|
assertAllNodesAreUsed(distributedService, DISTRIBUTED_SERVICE_NAME, distributedService[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `distributed service requests are retried if one of the nodes in the cluster goes down without sending a response`() {
|
fun `distributed service requests are retried if one of the nodes in the cluster goes down without sending a response`() {
|
||||||
driver(startNodesInProcess = true) {
|
startDriverWithDistributedService { distributedServiceNodes ->
|
||||||
val distributedServiceNodes = startDistributedService()
|
|
||||||
val alice = startAlice()
|
val alice = startAlice()
|
||||||
val serviceAddress = alice.services.networkMapCache.run {
|
val serviceAddress = alice.services.networkMapCache.run {
|
||||||
val notaryParty = notaryIdentities.randomOrNull()!!
|
val notaryParty = notaryIdentities.randomOrNull()!!
|
||||||
@ -77,8 +77,7 @@ class P2PMessagingTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `distributed service request retries are persisted across client node restarts`() {
|
fun `distributed service request retries are persisted across client node restarts`() {
|
||||||
driver(startNodesInProcess = true) {
|
startDriverWithDistributedService { distributedServiceNodes ->
|
||||||
val distributedServiceNodes = startDistributedService()
|
|
||||||
val alice = startAlice()
|
val alice = startAlice()
|
||||||
val serviceAddress = alice.services.networkMapCache.run {
|
val serviceAddress = alice.services.networkMapCache.run {
|
||||||
val notaryParty = notaryIdentities.randomOrNull()!!
|
val notaryParty = notaryIdentities.randomOrNull()!!
|
||||||
@ -117,11 +116,10 @@ class P2PMessagingTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DriverDSLExposedInterface.startDistributedService(): List<StartedNode<Node>> {
|
private fun startDriverWithDistributedService(dsl: DriverDSLExposedInterface.(List<StartedNode<Node>>) -> Unit) {
|
||||||
return startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2)
|
driver(startNodesInProcess = true, notarySpecs = listOf(NotarySpec(DISTRIBUTED_SERVICE_NAME, cluster = ClusterSpec.Raft(clusterSize = 2)))) {
|
||||||
.getOrThrow()
|
dsl(defaultNotaryHandle.nodeHandles.getOrThrow().map { (it as NodeHandle.InProcess).node })
|
||||||
.second
|
}
|
||||||
.map { (it as NodeHandle.InProcess).node }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DriverDSLExposedInterface.startAlice(): StartedNode<Node> {
|
private fun DriverDSLExposedInterface.startAlice(): StartedNode<Node> {
|
||||||
|
@ -7,7 +7,6 @@ import net.corda.core.flows.FlowLogic
|
|||||||
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.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.concurrent.transpose
|
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.schemas.MappedSchema
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.core.schemas.PersistentState
|
import net.corda.core.schemas.PersistentState
|
||||||
@ -18,14 +17,12 @@ import net.corda.core.transactions.SignedTransaction
|
|||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.services.FlowPermissions
|
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||||
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.driver.DriverDSLExposedInterface
|
|
||||||
import net.corda.testing.driver.NodeHandle
|
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import org.junit.Assume
|
import org.junit.Assume.assumeFalse
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.lang.management.ManagementFactory
|
import java.lang.management.ManagementFactory
|
||||||
import javax.persistence.Column
|
import javax.persistence.Column
|
||||||
@ -35,30 +32,26 @@ import kotlin.test.assertEquals
|
|||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
class NodeStatePersistenceTests {
|
class NodeStatePersistenceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `persistent state survives node restart`() {
|
fun `persistent state survives node restart`() {
|
||||||
// Temporary disable this test when executed on Windows. It is known to be sporadically failing.
|
// Temporary disable this test when executed on Windows. It is known to be sporadically failing.
|
||||||
// More investigation is needed to establish why.
|
// More investigation is needed to establish why.
|
||||||
Assume.assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win"))
|
assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win"))
|
||||||
|
|
||||||
val user = User("mark", "dadada", setOf(FlowPermissions.startFlowPermission<SendMessageFlow>()))
|
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery")))
|
||||||
val message = Message("Hello world!")
|
val message = Message("Hello world!")
|
||||||
driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
|
driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
|
||||||
val (nodeName, notaryNodeHandle) = {
|
val nodeName = {
|
||||||
val notaryNodeHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow()
|
|
||||||
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
|
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
|
||||||
ensureAcquainted(notaryNodeHandle, nodeHandle)
|
|
||||||
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name
|
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name
|
||||||
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
||||||
it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow()
|
it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow()
|
||||||
}
|
}
|
||||||
nodeHandle.stop().getOrThrow()
|
nodeHandle.stop()
|
||||||
nodeName to notaryNodeHandle
|
nodeName
|
||||||
}()
|
}()
|
||||||
|
|
||||||
val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow()
|
val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow()
|
||||||
ensureAcquainted(notaryNodeHandle, nodeHandle)
|
|
||||||
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
||||||
val page = it.proxy.vaultQuery(MessageState::class.java)
|
val page = it.proxy.vaultQuery(MessageState::class.java)
|
||||||
val stateAndRef = page.states.singleOrNull()
|
val stateAndRef = page.states.singleOrNull()
|
||||||
@ -68,10 +61,6 @@ class NodeStatePersistenceTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DriverDSLExposedInterface.ensureAcquainted(one: NodeHandle, another: NodeHandle) {
|
|
||||||
listOf(one.pollUntilKnowsAbout(another), another.pollUntilKnowsAbout(one)).transpose().getOrThrow()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isQuasarAgentSpecified(): Boolean {
|
fun isQuasarAgentSpecified(): Boolean {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user