mirror of
https://github.com/corda/corda.git
synced 2024-12-28 00:38:55 +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()
|
||||
@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
|
||||
public <init>()
|
||||
public <init>(net.corda.core.schemas.PersistentStateRef)
|
||||
|
@ -52,6 +52,10 @@ if [ $abstractCount -gt 0 ]; then
|
||||
fi
|
||||
|
||||
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
|
||||
exit $badChanges
|
||||
|
9
.idea/compiler.xml
generated
9
.idea/compiler.xml
generated
@ -1,7 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<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_main" target="1.8" />
|
||||
<module name="attachment-demo_test" target="1.8" />
|
||||
@ -21,7 +23,12 @@
|
||||
<module name="cordapp_test" target="1.8" />
|
||||
<module name="cordform-common_main" 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_smokeTest" target="1.8" />
|
||||
<module name="core_test" target="1.8" />
|
||||
<module name="demobench_main" target="1.8" />
|
||||
<module name="demobench_test" target="1.8" />
|
||||
|
@ -267,9 +267,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
||||
}
|
||||
node {
|
||||
name "O=Bank B,OU=corda,L=London,C=GB"
|
||||
p2pPort 10007
|
||||
rpcPort 10008
|
||||
webPort 10009
|
||||
p2pAddress "localhost:10007"
|
||||
rpcAddress "localhost:10008"
|
||||
webAddress "localhost:10009"
|
||||
cordapps = []
|
||||
}
|
||||
}
|
||||
|
@ -26,64 +26,71 @@ import net.corda.finance.USD
|
||||
import net.corda.finance.flows.CashExitFlow
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.node.services.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.testing.*
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.node.DriverBasedTest
|
||||
import org.junit.Test
|
||||
import rx.Observable
|
||||
|
||||
class NodeMonitorModelTest : DriverBasedTest() {
|
||||
lateinit var aliceNode: NodeInfo
|
||||
lateinit var bobNode: NodeInfo
|
||||
lateinit var notaryParty: Party
|
||||
class NodeMonitorModelTest {
|
||||
private lateinit var aliceNode: NodeInfo
|
||||
private lateinit var bobNode: NodeInfo
|
||||
private lateinit var notaryParty: Party
|
||||
|
||||
lateinit var rpc: CordaRPCOps
|
||||
lateinit var rpcBob: CordaRPCOps
|
||||
lateinit var stateMachineTransactionMapping: Observable<StateMachineTransactionMapping>
|
||||
lateinit var stateMachineUpdates: Observable<StateMachineUpdate>
|
||||
lateinit var stateMachineUpdatesBob: Observable<StateMachineUpdate>
|
||||
lateinit var progressTracking: Observable<ProgressTrackingEvent>
|
||||
lateinit var transactions: Observable<SignedTransaction>
|
||||
lateinit var vaultUpdates: Observable<Vault.Update<ContractState>>
|
||||
lateinit var networkMapUpdates: Observable<NetworkMapCache.MapChange>
|
||||
lateinit var newNode: (CordaX500Name) -> NodeInfo
|
||||
private lateinit var rpc: CordaRPCOps
|
||||
private lateinit var rpcBob: CordaRPCOps
|
||||
private lateinit var stateMachineTransactionMapping: Observable<StateMachineTransactionMapping>
|
||||
private lateinit var stateMachineUpdates: Observable<StateMachineUpdate>
|
||||
private lateinit var stateMachineUpdatesBob: Observable<StateMachineUpdate>
|
||||
private lateinit var progressTracking: Observable<ProgressTrackingEvent>
|
||||
private lateinit var transactions: Observable<SignedTransaction>
|
||||
private lateinit var vaultUpdates: Observable<Vault.Update<ContractState>>
|
||||
private lateinit var networkMapUpdates: Observable<NetworkMapCache.MapChange>
|
||||
private lateinit var newNode: (CordaX500Name) -> NodeInfo
|
||||
|
||||
override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
||||
val cashUser = User("user1", "test", permissions = setOf(
|
||||
startFlowPermission<CashIssueFlow>(),
|
||||
startFlowPermission<CashPaymentFlow>(),
|
||||
startFlowPermission<CashExitFlow>())
|
||||
)
|
||||
val aliceNodeFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser))
|
||||
val notaryHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow()
|
||||
val aliceNodeHandle = aliceNodeFuture.getOrThrow()
|
||||
aliceNode = aliceNodeHandle.nodeInfo
|
||||
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
|
||||
val monitor = NodeMonitorModel()
|
||||
stateMachineTransactionMapping = monitor.stateMachineTransactionMapping.bufferUntilSubscribed()
|
||||
stateMachineUpdates = monitor.stateMachineUpdates.bufferUntilSubscribed()
|
||||
progressTracking = monitor.progressTracking.bufferUntilSubscribed()
|
||||
transactions = monitor.transactions.bufferUntilSubscribed()
|
||||
vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed()
|
||||
networkMapUpdates = monitor.networkMap.bufferUntilSubscribed()
|
||||
private fun setup(runTest: () -> Unit) {
|
||||
driver(extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
||||
val cashUser = User("user1", "test", permissions = setOf(
|
||||
startFlow<CashIssueFlow>(),
|
||||
startFlow<CashPaymentFlow>(),
|
||||
startFlow<CashExitFlow>(),
|
||||
invokeRpc(CordaRPCOps::notaryIdentities),
|
||||
invokeRpc("vaultTrackBy"),
|
||||
invokeRpc("vaultQueryBy"),
|
||||
invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed),
|
||||
invokeRpc(CordaRPCOps::stateMachineRecordedTransactionMappingFeed),
|
||||
invokeRpc(CordaRPCOps::stateMachinesFeed),
|
||||
invokeRpc(CordaRPCOps::networkMapFeed))
|
||||
)
|
||||
val aliceNodeHandle = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)).getOrThrow()
|
||||
aliceNode = aliceNodeHandle.nodeInfo
|
||||
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
|
||||
val monitor = NodeMonitorModel()
|
||||
stateMachineTransactionMapping = monitor.stateMachineTransactionMapping.bufferUntilSubscribed()
|
||||
stateMachineUpdates = monitor.stateMachineUpdates.bufferUntilSubscribed()
|
||||
progressTracking = monitor.progressTracking.bufferUntilSubscribed()
|
||||
transactions = monitor.transactions.bufferUntilSubscribed()
|
||||
vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed()
|
||||
networkMapUpdates = monitor.networkMap.bufferUntilSubscribed()
|
||||
|
||||
monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
|
||||
rpc = monitor.proxyObservable.value!!
|
||||
notaryParty = notaryHandle.nodeInfo.legalIdentities[1]
|
||||
monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
|
||||
rpc = monitor.proxyObservable.value!!
|
||||
notaryParty = defaultNotaryIdentity
|
||||
|
||||
val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow()
|
||||
bobNode = bobNodeHandle.nodeInfo
|
||||
val monitorBob = NodeMonitorModel()
|
||||
stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed()
|
||||
monitorBob.register(bobNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
|
||||
rpcBob = monitorBob.proxyObservable.value!!
|
||||
runTest()
|
||||
val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow()
|
||||
bobNode = bobNodeHandle.nodeInfo
|
||||
val monitorBob = NodeMonitorModel()
|
||||
stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed()
|
||||
monitorBob.register(bobNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
|
||||
rpcBob = monitorBob.proxyObservable.value!!
|
||||
runTest()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network map update`() {
|
||||
fun `network map update`() = setup {
|
||||
val charlieNode = newNode(CHARLIE.name)
|
||||
val nonServiceIdentities = aliceNode.legalIdentitiesAndCerts + bobNode.legalIdentitiesAndCerts + charlieNode.legalIdentitiesAndCerts
|
||||
networkMapUpdates.filter { it.node.legalIdentitiesAndCerts.any { it in nonServiceIdentities } }
|
||||
@ -104,7 +111,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cash issue works end to end`() {
|
||||
fun `cash issue works end to end`() = setup {
|
||||
rpc.startFlow(::CashIssueFlow,
|
||||
Amount(100, USD),
|
||||
OpaqueBytes(ByteArray(1, { 1 })),
|
||||
@ -128,7 +135,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
|
||||
}
|
||||
|
||||
@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()
|
||||
rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.chooseIdentity()).returnValue.getOrThrow()
|
||||
|
||||
|
@ -40,7 +40,7 @@ class ContractStateModel {
|
||||
return this.map { stateAndRef ->
|
||||
if (stateAndRef.state.data is Cash.State) {
|
||||
// Kotlin doesn't unify here for some reason
|
||||
uncheckedCast(stateAndRef)
|
||||
uncheckedCast<StateAndRef<ContractState>, StateAndRef<Cash.State>>(stateAndRef)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.google.common.cache.CacheLoader
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.collections.ObservableList
|
||||
import net.corda.client.jfx.utils.ChosenList
|
||||
import net.corda.client.jfx.utils.filterNotNull
|
||||
import net.corda.client.jfx.utils.fold
|
||||
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.node.NodeInfo
|
||||
import net.corda.core.node.services.NetworkMapCache.MapChange
|
||||
import net.corda.nodeapi.internal.ServiceType
|
||||
import java.security.PublicKey
|
||||
|
||||
class NetworkIdentityModel {
|
||||
@ -36,16 +36,10 @@ class NetworkIdentityModel {
|
||||
.build<PublicKey, ObservableValue<NodeInfo?>>(CacheLoader.from { publicKey ->
|
||||
publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } }
|
||||
})
|
||||
|
||||
val notaries: ObservableList<Party> = networkIdentities.map {
|
||||
it.legalIdentitiesAndCerts.find { it.name.commonName?.let { ServiceType.parse(it).isNotary() } == true }
|
||||
}.map { it?.party }.filterNotNull()
|
||||
|
||||
val notaries = ChosenList(rpcProxy.map { FXCollections.observableList(it?.notaryIdentities() ?: emptyList()) })
|
||||
val notaryNodes: ObservableList<NodeInfo> = notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }.filterNotNull()
|
||||
val parties: ObservableList<NodeInfo> = networkIdentities
|
||||
.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 }
|
||||
|
||||
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 net.corda.finance.Currencies.DOLLARS;
|
||||
import static net.corda.finance.contracts.GetBalances.getCashBalance;
|
||||
import static net.corda.node.services.FlowPermissions.startFlowPermission;
|
||||
import static net.corda.node.services.Permissions.invokeRpc;
|
||||
import static net.corda.node.services.Permissions.startFlow;
|
||||
import static net.corda.testing.TestConstants.getALICE;
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
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 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.internal.concurrent.flatMap
|
||||
import net.corda.core.internal.packageName
|
||||
import net.corda.core.messaging.FlowProgressHandle
|
||||
import net.corda.core.messaging.StateMachineUpdate
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.messaging.startTrackedFlow
|
||||
import net.corda.core.messaging.*
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.DOLLARS
|
||||
@ -20,7 +17,8 @@ import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.finance.schemas.CashSchemaV1
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.chooseIdentity
|
||||
@ -36,9 +34,12 @@ import kotlin.test.assertTrue
|
||||
|
||||
class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", CashSchemaV1::class.packageName)) {
|
||||
private val rpcUser = User("user1", "test", permissions = setOf(
|
||||
startFlowPermission<CashIssueFlow>(),
|
||||
startFlowPermission<CashPaymentFlow>()
|
||||
))
|
||||
startFlow<CashIssueFlow>(),
|
||||
startFlow<CashPaymentFlow>(),
|
||||
invokeRpc("vaultQueryBy"),
|
||||
invokeRpc(CordaRPCOps::stateMachinesFeed),
|
||||
invokeRpc("vaultQueryByCriteria"))
|
||||
)
|
||||
private lateinit var node: StartedNode<Node>
|
||||
private lateinit var client: CordaRPCClient
|
||||
private var connection: CordaRPCConnection? = null
|
||||
|
@ -7,6 +7,7 @@ import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
|
||||
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 java.time.Duration
|
||||
|
||||
|
@ -5,6 +5,11 @@ import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationDefaults
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
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
|
||||
|
||||
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.messaging.RPCOps
|
||||
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.rpcDriver
|
||||
import net.corda.testing.rpcTestUser
|
||||
@ -65,7 +65,7 @@ class ClientRPCInfrastructureTests : AbstractRPCTest() {
|
||||
override fun makeComplicatedObservable() = complicatedObservable
|
||||
override fun makeComplicatedListenableFuture() = complicatedListenableFuturee
|
||||
override fun addedLater(): Unit = throw IllegalStateException()
|
||||
override fun captureUser(): String = getRpcContext().currentUser.username
|
||||
override fun captureUser(): String = rpcContext().currentUser.username
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1,7 +1,9 @@
|
||||
package net.corda.client.rpc
|
||||
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
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.nodeapi.User
|
||||
import net.corda.testing.RPCDriverExposedDSLInterface
|
||||
@ -9,6 +11,8 @@ import net.corda.testing.rpcDriver
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import kotlin.reflect.KVisibility
|
||||
import kotlin.reflect.full.declaredMemberFunctions
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
@ -28,7 +32,7 @@ class RPCPermissionsTests : AbstractRPCTest() {
|
||||
|
||||
class TestOpsImpl : TestOps {
|
||||
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.flows.CashIssueAndPaymentFlow
|
||||
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.singleIdentity
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@ -24,12 +27,16 @@ import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class IdentitySyncFlowTests {
|
||||
lateinit var mockNet: MockNetwork
|
||||
private lateinit var mockNet: MockNetwork
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
// 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
|
||||
@ -40,12 +47,11 @@ class IdentitySyncFlowTests {
|
||||
@Test
|
||||
fun `sync confidential identities`() {
|
||||
// Set up values we'll need
|
||||
val notaryNode = mockNet.createNotaryNode()
|
||||
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
||||
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
||||
val alice: Party = aliceNode.info.singleIdentity()
|
||||
val bob: Party = bobNode.info.singleIdentity()
|
||||
val notary = notaryNode.services.getDefaultNotary()
|
||||
val notary = mockNet.defaultNotaryIdentity
|
||||
bobNode.internals.registerInitiatedFlow(Receive::class.java)
|
||||
|
||||
// Alice issues then pays some cash to a new confidential identity that Bob doesn't know about
|
||||
@ -70,14 +76,13 @@ class IdentitySyncFlowTests {
|
||||
@Test
|
||||
fun `don't offer other's identities confidential identities`() {
|
||||
// Set up values we'll need
|
||||
val notaryNode = mockNet.createNotaryNode()
|
||||
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
||||
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
||||
val charlieNode = mockNet.createPartyNode(CHARLIE_NAME)
|
||||
val alice: Party = aliceNode.info.singleIdentity()
|
||||
val bob: Party = bobNode.info.singleIdentity()
|
||||
val charlie: Party = charlieNode.info.singleIdentity()
|
||||
val notary = notaryNode.services.getDefaultNotary()
|
||||
val notary = mockNet.defaultNotaryIdentity
|
||||
bobNode.internals.registerInitiatedFlow(Receive::class.java)
|
||||
|
||||
// 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.
|
||||
*/
|
||||
@InitiatingFlow
|
||||
class Initiator(val otherSide: Party, val tx: WireTransaction): FlowLogic<Boolean>() {
|
||||
class Initiator(private val otherSide: Party, private val tx: WireTransaction): FlowLogic<Boolean>() {
|
||||
@Suspendable
|
||||
override fun call(): Boolean {
|
||||
val session = initiateFlow(otherSide)
|
||||
@ -116,7 +121,7 @@ class IdentitySyncFlowTests {
|
||||
}
|
||||
|
||||
@InitiatedBy(IdentitySyncFlowTests.Initiator::class)
|
||||
class Receive(val otherSideSession: FlowSession): FlowLogic<Unit>() {
|
||||
class Receive(private val otherSideSession: FlowSession): FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
subFlow(IdentitySyncFlow.Receive(otherSideSession))
|
||||
|
@ -6,17 +6,22 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.*
|
||||
|
||||
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
|
||||
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
|
||||
mockNet.createNotaryNode()
|
||||
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||
val bobNode = mockNet.createPartyNode(BOB.name)
|
||||
val alice = aliceNode.info.singleIdentity()
|
||||
@ -52,19 +57,16 @@ class SwapIdentitiesFlowTests {
|
||||
*/
|
||||
@Test
|
||||
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
|
||||
val notaryNode = mockNet.createNotaryNode()
|
||||
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||
val bobNode = mockNet.createPartyNode(BOB.name)
|
||||
val charlieNode = mockNet.createPartyNode(CHARLIE.name)
|
||||
val bob: Party = bobNode.services.myInfo.singleIdentity()
|
||||
val notBob = notaryNode.database.transaction {
|
||||
notaryNode.services.keyManagementService.freshKeyAndCert(notaryNode.services.myInfo.chooseIdentityAndCert(), false)
|
||||
val notBob = charlieNode.database.transaction {
|
||||
charlieNode.services.keyManagementService.freshKeyAndCert(charlieNode.services.myInfo.chooseIdentityAndCert(), false)
|
||||
}
|
||||
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.") {
|
||||
SwapIdentitiesFlow.validateAndRegisterIdentity(aliceNode.services.identityService, bob, notBob, signature.withoutKey())
|
||||
}
|
||||
@ -77,11 +79,8 @@ class SwapIdentitiesFlowTests {
|
||||
*/
|
||||
@Test
|
||||
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
|
||||
val notaryNode = mockNet.createNotaryNode()
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||
val bobNode = mockNet.createPartyNode(BOB.name)
|
||||
val bob: Party = bobNode.services.myInfo.singleIdentity()
|
||||
|
@ -4,8 +4,4 @@ trustStorePassword : "trustpass"
|
||||
p2pAddress : "localhost:10005"
|
||||
rpcAddress : "localhost:10006"
|
||||
webAddress : "localhost:10007"
|
||||
networkMapService : {
|
||||
address : "localhost:10000"
|
||||
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
|
||||
}
|
||||
useHTTPS : false
|
||||
|
@ -1,5 +1,5 @@
|
||||
gradlePluginsVersion=2.0.6
|
||||
gradlePluginsVersion=2.0.8
|
||||
kotlinVersion=1.1.50
|
||||
guavaVersion=21.0
|
||||
bouncycastleVersion=1.57
|
||||
typesafeConfigVersion=1.3.1
|
||||
typesafeConfigVersion=1.3.1
|
@ -7,6 +7,52 @@ apply plugin: 'com.jfrog.artifactory'
|
||||
|
||||
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 {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@ -14,7 +60,6 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
testCompile "junit:junit:$junit_version"
|
||||
testCompile "commons-fileupload:commons-fileupload:$fileupload_version"
|
||||
|
||||
@ -48,6 +93,11 @@ dependencies {
|
||||
// Guava: Google utilities library.
|
||||
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.
|
||||
compile "io.reactivex:rxjava:$rxjava_version"
|
||||
|
||||
@ -87,6 +137,22 @@ task testJar(type: Jar) {
|
||||
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 {
|
||||
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 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.
|
||||
|
@ -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(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.
|
||||
@ -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(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. */
|
||||
@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.
|
||||
|
@ -49,6 +49,7 @@ class NotaryFlow {
|
||||
progressTracker.currentStep = REQUESTING
|
||||
|
||||
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 }) {
|
||||
"Input states must have the same Notary"
|
||||
}
|
||||
@ -115,6 +116,9 @@ class NotaryFlow {
|
||||
|
||||
@Suspendable
|
||||
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()
|
||||
checkNotary(notary)
|
||||
service.validateTimeWindow(timeWindow)
|
||||
@ -135,7 +139,7 @@ class NotaryFlow {
|
||||
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
|
||||
// notary identities?
|
||||
if (notary !in serviceHub.myInfo.legalIdentities) {
|
||||
if (notary == null || !serviceHub.myInfo.isLegalIdentity(notary)) {
|
||||
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.identity.PartyAndCertificate
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.SerializationDefaults
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
@ -100,21 +100,13 @@ interface CordaRPCOps : RPCOps {
|
||||
// Java Helpers
|
||||
|
||||
// DOCSTART VaultQueryAPIHelpers
|
||||
fun <T : ContractState> vaultQuery(contractStateType: Class<out T>): Vault.Page<T> {
|
||||
return vaultQueryBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType)
|
||||
}
|
||||
fun <T : ContractState> vaultQuery(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> vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class<out T>): 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> vaultQueryByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): Vault.Page<T>
|
||||
|
||||
fun <T : ContractState> vaultQueryByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T> {
|
||||
return vaultQueryBy(criteria, PageSpecification(), sorting, contractStateType)
|
||||
}
|
||||
fun <T : ContractState> vaultQueryByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T>
|
||||
// DOCEND VaultQueryAPIHelpers
|
||||
|
||||
/**
|
||||
@ -141,21 +133,13 @@ interface CordaRPCOps : RPCOps {
|
||||
// Java Helpers
|
||||
|
||||
// DOCSTART VaultTrackAPIHelpers
|
||||
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> vaultTrack(contractStateType: Class<out T>): 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> vaultTrackByCriteria(contractStateType: Class<out T>, criteria: QueryCriteria): 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> vaultTrackByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): 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)
|
||||
}
|
||||
fun <T : ContractState> vaultTrackByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): DataFeed<Vault.Page<T>, Vault.Update<T>>
|
||||
// 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.
|
||||
*/
|
||||
// TODO this list will be taken from NetworkParameters distributed by NetworkMap.
|
||||
val notaryIdentities: List<Party>
|
||||
// DOCEND 1
|
||||
|
||||
@ -116,12 +115,15 @@ interface NetworkMapCacheBase {
|
||||
fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name }
|
||||
// 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
|
||||
|
||||
/** Checks whether a given party is an validating notary identity. */
|
||||
// TODO This implementation will change after introducing of NetworkParameters.
|
||||
fun isValidatingNotary(party: Party): Boolean = isNotary(party) && "validating" in party.name.commonName!!
|
||||
/**
|
||||
* Returns true if and only if the given [Party] is validating notary. For every party that is a validating notary,
|
||||
* [isNotary] is also true.
|
||||
* @see isNotary
|
||||
*/
|
||||
fun isValidatingNotary(party: Party): Boolean
|
||||
|
||||
/** Clear all network map data from local node cache. */
|
||||
fun clearNetworkMapCache()
|
||||
|
@ -108,7 +108,7 @@ abstract class TraversableTransaction(open val componentGroups: List<ComponentGr
|
||||
* @param groupHashes the roots of the transaction component groups.
|
||||
*/
|
||||
@CordaSerializable
|
||||
class FilteredTransaction private constructor(
|
||||
class FilteredTransaction internal constructor(
|
||||
override val id: SecureHash,
|
||||
val filteredComponentGroups: List<FilteredComponentGroup>,
|
||||
val groupHashes: List<SecureHash>
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.corda.node
|
||||
package net.corda.core.cordapp
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
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.utilities.getOrThrow
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.smoketesting.NodeConfig
|
||||
import net.corda.smoketesting.NodeProcess
|
||||
@ -23,6 +22,7 @@ import kotlin.streams.toList
|
||||
|
||||
class CordappSmokeTest {
|
||||
private companion object {
|
||||
private const val CORDAPPS_DIR_NAME = "cordapps"
|
||||
val user = User("user1", "test", permissions = setOf("ALL"))
|
||||
val port = AtomicInteger(15100)
|
||||
}
|
||||
@ -38,9 +38,10 @@ class CordappSmokeTest {
|
||||
users = listOf(user)
|
||||
)
|
||||
|
||||
|
||||
@Test
|
||||
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
|
||||
val selfCordapp = Paths.get("build", "libs").list {
|
||||
it.filter { "-smokeTests" in it.toString() }.toList().single()
|
||||
@ -61,7 +62,7 @@ class CordappSmokeTest {
|
||||
|
||||
@Test
|
||||
fun `empty cordapps directory`() {
|
||||
(factory.baseDirectory(aliceConfig) / CordappLoader.CORDAPPS_DIR_NAME).createDirectories()
|
||||
(factory.baseDirectory(aliceConfig) / CORDAPPS_DIR_NAME).createDirectories()
|
||||
factory.create(aliceConfig).close()
|
||||
}
|
||||
|
@ -18,14 +18,12 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class FlowsInJavaTest {
|
||||
|
||||
private final MockNetwork mockNet = new MockNetwork();
|
||||
private StartedNode<MockNetwork.MockNode> aliceNode;
|
||||
private StartedNode<MockNetwork.MockNode> bobNode;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mockNet.createNotaryNode();
|
||||
aliceNode = mockNet.createPartyNode(TestConstants.getALICE().getName());
|
||||
bobNode = mockNet.createPartyNode(TestConstants.getBOB().getName());
|
||||
mockNet.runNetwork();
|
||||
|
@ -14,7 +14,6 @@ import net.corda.testing.ALICE
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.BOB
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeArgs
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.singleIdentity
|
||||
import org.junit.After
|
||||
@ -109,15 +108,13 @@ class AttachmentTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `malicious response`() {
|
||||
fun maliciousResponse() {
|
||||
// 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> {
|
||||
override fun create(args: MockNodeArgs): MockNetwork.MockNode {
|
||||
return object : MockNetwork.MockNode(args) {
|
||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
|
||||
}
|
||||
val aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE.name), nodeFactory = { args ->
|
||||
object : MockNetwork.MockNode(args) {
|
||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
|
||||
}
|
||||
}, validating = false)
|
||||
})
|
||||
val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name))
|
||||
mockNet.runNetwork()
|
||||
val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME)
|
||||
|
@ -27,19 +27,18 @@ class CollectSignaturesFlowTests {
|
||||
private val cordappPackages = listOf("net.corda.testing.contracts")
|
||||
}
|
||||
|
||||
lateinit var mockNet: MockNetwork
|
||||
lateinit var aliceNode: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var bobNode: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var charlieNode: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var alice: Party
|
||||
lateinit var bob: Party
|
||||
lateinit var charlie: Party
|
||||
lateinit var notary: Party
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var aliceNode: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var bobNode: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var charlieNode: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var alice: Party
|
||||
private lateinit var bob: Party
|
||||
private lateinit var charlie: Party
|
||||
private lateinit var notary: Party
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(cordappPackages = cordappPackages)
|
||||
val notaryNode = mockNet.createNotaryNode()
|
||||
aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||
bobNode = mockNet.createPartyNode(BOB.name)
|
||||
charlieNode = mockNet.createPartyNode(CHARLIE.name)
|
||||
@ -47,7 +46,7 @@ class CollectSignaturesFlowTests {
|
||||
alice = aliceNode.info.singleIdentity()
|
||||
bob = bobNode.info.singleIdentity()
|
||||
charlie = charlieNode.info.singleIdentity()
|
||||
notary = notaryNode.services.getDefaultNotary()
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -16,9 +16,9 @@ import net.corda.finance.USD
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
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.services.FlowPermissions.Companion.startFlowPermission
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
@ -41,14 +41,13 @@ class ContractUpgradeFlowTest {
|
||||
@Before
|
||||
fun setup() {
|
||||
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)
|
||||
bobNode = mockNet.createPartyNode(BOB.name)
|
||||
|
||||
// Process registration
|
||||
mockNet.runNetwork()
|
||||
|
||||
notary = notaryNode.services.getDefaultNotary()
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
}
|
||||
|
||||
@After
|
||||
@ -118,7 +117,7 @@ class ContractUpgradeFlowTest {
|
||||
return startRpcClient<CordaRPCOps>(
|
||||
rpcAddress = startRpcServer(
|
||||
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!!,
|
||||
username = user.username,
|
||||
password = user.password
|
||||
@ -134,10 +133,10 @@ class ContractUpgradeFlowTest {
|
||||
val stx = bobNode.services.addSignature(signedByA)
|
||||
|
||||
val user = rpcTestUser.copy(permissions = setOf(
|
||||
startFlowPermission<FinalityInvoker>(),
|
||||
startFlowPermission<ContractUpgradeFlow.Initiate<*, *>>(),
|
||||
startFlowPermission<ContractUpgradeFlow.Authorise>(),
|
||||
startFlowPermission<ContractUpgradeFlow.Deauthorise>()
|
||||
startFlow<FinalityInvoker>(),
|
||||
startFlow<ContractUpgradeFlow.Initiate<*, *>>(),
|
||||
startFlow<ContractUpgradeFlow.Authorise>(),
|
||||
startFlow<ContractUpgradeFlow.Deauthorise>()
|
||||
))
|
||||
val rpcA = startProxy(aliceNode, user)
|
||||
val rpcB = startProxy(bobNode, user)
|
||||
|
@ -26,7 +26,6 @@ class FinalityFlowTests {
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||
val notaryNode = mockNet.createNotaryNode()
|
||||
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
||||
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
||||
mockNet.runNetwork()
|
||||
@ -34,7 +33,7 @@ class FinalityFlowTests {
|
||||
bobServices = bobNode.services
|
||||
alice = aliceNode.info.singleIdentity()
|
||||
bob = bobNode.info.singleIdentity()
|
||||
notary = notaryNode.services.getDefaultNotary()
|
||||
notary = aliceServices.getDefaultNotary()
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -13,7 +13,7 @@ import org.junit.Test
|
||||
class ReceiveMultipleFlowTests {
|
||||
@Test
|
||||
fun `receive all messages in parallel using map style`() {
|
||||
network(3) { nodes, _ ->
|
||||
network(3) { nodes ->
|
||||
val doubleValue = 5.0
|
||||
nodes[1].registerAnswer(AlgorithmDefinition::class, doubleValue)
|
||||
val stringValue = "Thriller"
|
||||
@ -30,7 +30,7 @@ class ReceiveMultipleFlowTests {
|
||||
|
||||
@Test
|
||||
fun `receive all messages in parallel using list style`() {
|
||||
network(3) { nodes, _ ->
|
||||
network(3) { nodes ->
|
||||
val value1 = 5.0
|
||||
nodes[1].registerAnswer(ParallelAlgorithmList::class, value1)
|
||||
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.sequence
|
||||
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.getDefaultNotary
|
||||
import net.corda.testing.node.MockNetwork
|
||||
@ -26,18 +28,18 @@ import kotlin.test.assertNull
|
||||
|
||||
// DOCSTART 3
|
||||
class ResolveTransactionsFlowTest {
|
||||
lateinit var mockNet: MockNetwork
|
||||
lateinit var notaryNode: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var megaCorpNode: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var miniCorpNode: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var megaCorp: Party
|
||||
lateinit var miniCorp: Party
|
||||
lateinit var notary: Party
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var notaryNode: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var megaCorpNode: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var miniCorpNode: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var megaCorp: Party
|
||||
private lateinit var miniCorp: Party
|
||||
private lateinit var notary: Party
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
|
||||
notaryNode = mockNet.createNotaryNode()
|
||||
notaryNode = mockNet.defaultNotaryNode
|
||||
megaCorpNode = mockNet.createPartyNode(MEGA_CORP.name)
|
||||
miniCorpNode = mockNet.createPartyNode(MINI_CORP.name)
|
||||
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.testing.chooseIdentity
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeArgs
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@ -156,11 +155,9 @@ class AttachmentSerializationTest {
|
||||
|
||||
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
||||
client.dispose()
|
||||
client = mockNet.createNode(MockNodeParameters(client.internals.id), object : MockNetwork.Factory<MockNetwork.MockNode> {
|
||||
override fun create(args: MockNodeArgs): MockNetwork.MockNode {
|
||||
return object : MockNetwork.MockNode(args) {
|
||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
||||
}
|
||||
client = mockNet.createNode(MockNodeParameters(client.internals.id), { args ->
|
||||
object : MockNetwork.MockNode(args) {
|
||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
||||
}
|
||||
})
|
||||
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.crypto.*
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.*
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
@ -399,8 +399,7 @@ class CompatibleTransactionTests {
|
||||
@Test
|
||||
fun `FilteredTransaction signer manipulation tests`() {
|
||||
// Required to call the private constructor.
|
||||
val ftxConstructor = FilteredTransaction::class.java.declaredConstructors[1]
|
||||
ftxConstructor.isAccessible = true
|
||||
val ftxConstructor = ::FilteredTransaction
|
||||
|
||||
// 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))
|
||||
@ -479,12 +478,12 @@ class CompatibleTransactionTests {
|
||||
// A command with no corresponding signer detected
|
||||
// because the pointer of CommandData (3rd leaf) cannot find a corresponding (3rd) signer.
|
||||
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.
|
||||
// Update partial Merkle tree for signers.
|
||||
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.
|
||||
ftxNoLastCommandAndSigners.verify()
|
||||
// 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.
|
||||
// Do not change partial Merkle tree for signers.
|
||||
// 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.
|
||||
assertFailsWith<FilteredTransactionVerificationException> { ftxNoLastSigner.verify() }
|
||||
// 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.
|
||||
// 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.
|
||||
ftxNoLastSignerB.verify()
|
||||
// But, checkAllComponentsVisible() will not pass.
|
||||
@ -526,20 +525,18 @@ class CompatibleTransactionTests {
|
||||
val alterFilteredComponents = listOf(key1CommandsFtx.filteredComponentGroups[0], alterSignerGroup)
|
||||
|
||||
// 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.
|
||||
assertFailsWith<FilteredTransactionVerificationException> { ftxAlterSigner.verify() }
|
||||
// Also, checkAllComponentsVisible() will not pass (groupHash matching will fail).
|
||||
assertFailsWith<ComponentVisibilityException> { ftxAlterSigner.checkCommandVisibility(DUMMY_KEY_1.public) }
|
||||
|
||||
// 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.
|
||||
assertFailsWith<FilteredTransactionVerificationException> { ftxAlterSignerB.verify() }
|
||||
// Also, checkAllComponentsVisible() will not pass (top level Merkle tree cannot be verified against transaction's id).
|
||||
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.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
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.transactions.LedgerTransaction
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
@ -12,7 +14,7 @@ import org.junit.Test
|
||||
import java.time.Instant
|
||||
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 {
|
||||
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.CompositeKey
|
||||
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.testing.*
|
||||
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>`_
|
||||
|
||||
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
|
||||
--------------
|
||||
|
@ -8,6 +8,8 @@ UNRELEASED
|
||||
----------
|
||||
* ``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``.
|
||||
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
|
||||
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:
|
||||
|
||||
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
|
||||
|
||||
@ -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
|
||||
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
|
||||
-----------
|
||||
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.
|
||||
|
||||
: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
|
||||
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.
|
||||
|
@ -16,7 +16,7 @@ Running DemoBench
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -41,11 +41,12 @@ notary node:
|
||||
cordapps = []
|
||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
||||
}
|
||||
// Example of explicit addresses being used.
|
||||
node {
|
||||
name "CN=NodeC,O=NodeC,L=Paris,C=FR"
|
||||
p2pPort 10011
|
||||
rpcPort 10012
|
||||
webPort 10013
|
||||
p2pAddress "localhost:10011"
|
||||
rpcAddress "localhost:10012"
|
||||
webAddress "localhost:10013"
|
||||
cordapps = []
|
||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.corda.docs
|
||||
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.messaging.vaultTrackBy
|
||||
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.flows.CashIssueFlow
|
||||
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.testing.*
|
||||
import net.corda.testing.driver.driver
|
||||
@ -24,17 +26,22 @@ class IntegrationTestingTutorial {
|
||||
driver(startNodesInProcess = true,
|
||||
extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset")) {
|
||||
val aliceUser = User("aliceUser", "testPassword1", permissions = setOf(
|
||||
startFlowPermission<CashIssueFlow>(),
|
||||
startFlowPermission<CashPaymentFlow>()
|
||||
startFlow<CashIssueFlow>(),
|
||||
startFlow<CashPaymentFlow>(),
|
||||
invokeRpc("vaultTrackBy"),
|
||||
invokeRpc(CordaRPCOps::notaryIdentities),
|
||||
invokeRpc(CordaRPCOps::networkMapFeed)
|
||||
))
|
||||
val bobUser = User("bobUser", "testPassword2", permissions = setOf(
|
||||
startFlowPermission<CashPaymentFlow>()
|
||||
startFlow<CashPaymentFlow>(),
|
||||
invokeRpc("vaultTrackBy"),
|
||||
invokeRpc(CordaRPCOps::networkMapFeed)
|
||||
))
|
||||
val (alice, bob) = listOf(
|
||||
startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)),
|
||||
startNode(providedName = BOB.name, rpcUsers = listOf(bobUser)),
|
||||
startNotaryNode(DUMMY_NOTARY.name)
|
||||
startNode(providedName = BOB.name, rpcUsers = listOf(bobUser))
|
||||
).transpose().getOrThrow()
|
||||
|
||||
// END 1
|
||||
|
||||
// START 2
|
||||
@ -43,9 +50,6 @@ class IntegrationTestingTutorial {
|
||||
|
||||
val bobClient = bob.rpcClientToNode()
|
||||
val bobProxy = bobClient.start("bobUser", "testPassword2").proxy
|
||||
|
||||
aliceProxy.waitUntilNetworkReady().getOrThrow()
|
||||
bobProxy.waitUntilNetworkReady().getOrThrow()
|
||||
// END 2
|
||||
|
||||
// START 3
|
||||
|
@ -1,3 +1,5 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package net.corda.docs
|
||||
|
||||
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.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.USD
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.flows.CashExitFlow
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
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.testing.ALICE
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.driver.driver
|
||||
import org.graphstream.graph.Edge
|
||||
import org.graphstream.graph.Node
|
||||
@ -42,19 +43,18 @@ fun main(args: Array<String>) {
|
||||
val printOrVisualise = PrintOrVisualise.valueOf(args[0])
|
||||
|
||||
val baseDirectory = Paths.get("build/rpc-api-tutorial")
|
||||
val user = User("user", "password", permissions = setOf(startFlowPermission<CashIssueFlow>(),
|
||||
startFlowPermission<CashPaymentFlow>(),
|
||||
startFlowPermission<CashExitFlow>()))
|
||||
|
||||
val user = User("user", "password", permissions = setOf(startFlow<CashIssueFlow>(),
|
||||
startFlow<CashPaymentFlow>(),
|
||||
startFlow<CashExitFlow>(),
|
||||
invokeRpc(CordaRPCOps::nodeInfo)
|
||||
))
|
||||
driver(driverDirectory = baseDirectory, extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
||||
startNotaryNode(DUMMY_NOTARY.name)
|
||||
val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)).get()
|
||||
// END 1
|
||||
|
||||
// START 2
|
||||
val client = node.rpcClientToNode()
|
||||
val proxy = client.start("user", "password").proxy
|
||||
proxy.waitUntilNetworkReady().getOrThrow()
|
||||
|
||||
thread {
|
||||
generateTransactions(proxy)
|
||||
@ -143,4 +143,4 @@ class ExampleRPCSerializationWhitelist : SerializationWhitelist {
|
||||
// Add classes like this.
|
||||
override val whitelist = listOf(ExampleRPCValue::class.java)
|
||||
}
|
||||
// END 7
|
||||
// END 7
|
@ -55,7 +55,6 @@ class TutorialMockNetwork {
|
||||
}
|
||||
|
||||
lateinit private var mockNet: MockNetwork
|
||||
lateinit private var notary: StartedNode<MockNetwork.MockNode>
|
||||
lateinit private var nodeA: StartedNode<MockNetwork.MockNode>
|
||||
lateinit private var nodeB: StartedNode<MockNetwork.MockNode>
|
||||
|
||||
@ -66,7 +65,6 @@ class TutorialMockNetwork {
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockNet = MockNetwork()
|
||||
notary = mockNet.createNotaryNode()
|
||||
nodeA = mockNet.createPartyNode()
|
||||
nodeB = mockNet.createPartyNode()
|
||||
|
||||
|
@ -1,8 +1,4 @@
|
||||
myLegalName : "O=Bank A,L=London,C=GB"
|
||||
p2pAddress : "my-corda-node:10002"
|
||||
webAddress : "localhost:10003"
|
||||
networkMapService : {
|
||||
address : "my-network-map:10000"
|
||||
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
|
||||
}
|
||||
verifierType: "OutOfProcess"
|
||||
|
@ -10,7 +10,8 @@ import net.corda.finance.contracts.getCashBalances
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.schemas.CashSchemaV1
|
||||
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 org.junit.After
|
||||
import org.junit.Assert
|
||||
@ -19,23 +20,20 @@ import org.junit.Test
|
||||
import java.util.*
|
||||
|
||||
class CustomVaultQueryTest {
|
||||
|
||||
lateinit var mockNet: MockNetwork
|
||||
lateinit var nodeA: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var nodeB: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var notary: Party
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var nodeA: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var nodeB: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var notary: Party
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(
|
||||
threadPerNode = true,
|
||||
mockNet = MockNetwork(threadPerNode = true,
|
||||
cordappPackages = listOf(
|
||||
"net.corda.finance.contracts.asset",
|
||||
CashSchemaV1::class.packageName,
|
||||
"net.corda.docs"
|
||||
)
|
||||
)
|
||||
mockNet.createNotaryNode()
|
||||
nodeA = mockNet.createPartyNode()
|
||||
nodeB = mockNet.createPartyNode()
|
||||
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.schemas.CashSchemaV1
|
||||
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 org.junit.After
|
||||
import org.junit.Before
|
||||
@ -18,15 +19,14 @@ import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class FxTransactionBuildTutorialTest {
|
||||
lateinit var mockNet: MockNetwork
|
||||
lateinit var nodeA: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var nodeB: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var notary: Party
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var nodeA: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var nodeB: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var notary: Party
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName))
|
||||
mockNet.createNotaryNode()
|
||||
nodeA = mockNet.createPartyNode()
|
||||
nodeB = mockNet.createPartyNode()
|
||||
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.utilities.getOrThrow
|
||||
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 org.junit.After
|
||||
import org.junit.Before
|
||||
@ -18,11 +19,11 @@ import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class WorkflowTransactionBuildTutorialTest {
|
||||
lateinit var mockNet: MockNetwork
|
||||
lateinit var aliceServices: StartedNodeServices
|
||||
lateinit var bobServices: StartedNodeServices
|
||||
lateinit var alice: Party
|
||||
lateinit var bob: Party
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var aliceServices: StartedNodeServices
|
||||
private lateinit var bobServices: StartedNodeServices
|
||||
private lateinit var alice: Party
|
||||
private lateinit var bob: Party
|
||||
|
||||
// Helper method to locate the latest Vault version of a LinearState
|
||||
private inline fun <reified T : LinearState> ServiceHub.latest(ref: UniqueIdentifier): StateAndRef<T> {
|
||||
@ -33,8 +34,6 @@ class WorkflowTransactionBuildTutorialTest {
|
||||
@Before
|
||||
fun setup() {
|
||||
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 bobNode = mockNet.createPartyNode(BOB_NAME)
|
||||
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
|
||||
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 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.
|
||||
|
||||
|
||||
PersistentNetworkMapService and NetworkMapService
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
PersistentNetworkMapService
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``NetworkMapService`` is a node internal component responsible for
|
||||
managing and communicating the directory of authenticated registered
|
||||
nodes and advertised services in the Corda network. Only a single node
|
||||
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
|
||||
The ``PersistentNetworkMapService`` keeps track of ``NodeInfo`` and
|
||||
persists it to the database. It 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
|
||||
else that the source flow will be terminated by admin intervention.
|
||||
An ``InMemoryNetworkMapService`` is also available for unit tests
|
||||
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
|
||||
----------------------------------------
|
||||
|
@ -3,8 +3,7 @@
|
||||
Creating a Corda network
|
||||
========================
|
||||
|
||||
A Corda network consists of a number of machines running nodes, including a single node operating as the network map
|
||||
service. These nodes communicate using persistent protocols in order to create and validate transactions.
|
||||
A Corda network consists of a number of machines running nodes. 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
|
||||
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
|
||||
|
||||
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()
|
||||
|
||||
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
|
||||
|
||||
import net.corda.core.internal.packageName
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.flows.CashException
|
||||
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.MockNodeParameters
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.After
|
||||
import org.junit.Test
|
||||
|
||||
|
||||
class CashSelectionH2Test {
|
||||
private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance"))
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
mockNet.stopNodes()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check does not hold connection over retries`() {
|
||||
val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName))
|
||||
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).
|
||||
existingConfig.dataSourceProperties.setProperty("maximumPoolSize", "2")
|
||||
}))
|
||||
mockNet.startNodes()
|
||||
val bankA = mockNet.createNode(MockNodeParameters(configOverrides = {
|
||||
// Tweak connections to be minimal to make this easier (1 results in a hung node during start up, so use 2 connections).
|
||||
it.dataSourceProperties.setProperty("maximumPoolSize", "2")
|
||||
}))
|
||||
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.
|
||||
val flow1 = 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 = notaryNode.info.chooseIdentity()))
|
||||
val flow3 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity()))
|
||||
// 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 = notary))
|
||||
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 = notary))
|
||||
|
||||
assertThatThrownBy { flow1.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
||||
assertThatThrownBy { flow2.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
||||
assertThatThrownBy { flow3.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
||||
} finally {
|
||||
mockNet.stopNodes()
|
||||
}
|
||||
assertThatThrownBy { flow1.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
||||
assertThatThrownBy { flow2.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
||||
assertThatThrownBy { flow3.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
|
||||
}
|
||||
}
|
@ -7,7 +7,9 @@ import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
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.MockNetwork
|
||||
import net.corda.testing.node.MockNetwork.MockNode
|
||||
@ -23,15 +25,13 @@ class CashExitFlowTests {
|
||||
private val ref = OpaqueBytes.of(0x01)
|
||||
private lateinit var bankOfCordaNode: StartedNode<MockNode>
|
||||
private lateinit var bankOfCorda: Party
|
||||
private lateinit var notaryNode: StartedNode<MockNode>
|
||||
private lateinit var notary: Party
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||
notaryNode = mockNet.createNotaryNode()
|
||||
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(),
|
||||
cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||
bankOfCordaNode = mockNet.createPartyNode(BOC.name)
|
||||
notary = notaryNode.services.getDefaultNotary()
|
||||
bankOfCorda = bankOfCordaNode.info.chooseIdentity()
|
||||
|
||||
mockNet.runNetwork()
|
||||
|
@ -7,9 +7,8 @@ import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
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.chooseIdentity
|
||||
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNetwork.MockNode
|
||||
@ -23,16 +22,14 @@ class CashIssueFlowTests {
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var bankOfCordaNode: StartedNode<MockNode>
|
||||
private lateinit var bankOfCorda: Party
|
||||
private lateinit var notaryNode: StartedNode<MockNode>
|
||||
private lateinit var notary: Party
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||
notaryNode = mockNet.createNotaryNode()
|
||||
bankOfCordaNode = mockNet.createPartyNode(BOC.name)
|
||||
bankOfCorda = bankOfCordaNode.info.chooseIdentity()
|
||||
notary = notaryNode.services.getDefaultNotary()
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
mockNet.runNetwork()
|
||||
}
|
||||
|
||||
|
@ -26,18 +26,16 @@ class CashPaymentFlowTests {
|
||||
private val ref = OpaqueBytes.of(0x01)
|
||||
private lateinit var bankOfCordaNode: StartedNode<MockNode>
|
||||
private lateinit var bankOfCorda: Party
|
||||
private lateinit var notaryNode: StartedNode<MockNode>
|
||||
private lateinit var notary: Party
|
||||
private lateinit var aliceNode: StartedNode<MockNode>
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||
notaryNode = mockNet.createNotaryNode()
|
||||
bankOfCordaNode = mockNet.createPartyNode(BOC.name)
|
||||
aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||
bankOfCorda = bankOfCordaNode.info.chooseIdentity()
|
||||
notary = notaryNode.services.getDefaultNotary()
|
||||
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture
|
||||
mockNet.runNetwork()
|
||||
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, mockNet.defaultNotaryIdentity)).resultFuture
|
||||
future.getOrThrow()
|
||||
}
|
||||
|
||||
@ -48,7 +46,7 @@ class CashPaymentFlowTests {
|
||||
|
||||
@Test
|
||||
fun `pay some cash`() {
|
||||
val payTo = notaryNode.info.chooseIdentity()
|
||||
val payTo = aliceNode.info.chooseIdentity()
|
||||
val expectedPayment = 500.DOLLARS
|
||||
val expectedChange = 1500.DOLLARS
|
||||
|
||||
@ -56,7 +54,7 @@ class CashPaymentFlowTests {
|
||||
// Register for vault updates
|
||||
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||
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,
|
||||
payTo)).resultFuture
|
||||
@ -88,7 +86,7 @@ class CashPaymentFlowTests {
|
||||
|
||||
@Test
|
||||
fun `pay more than we have`() {
|
||||
val payTo = notaryNode.info.chooseIdentity()
|
||||
val payTo = aliceNode.info.chooseIdentity()
|
||||
val expected = 4000.DOLLARS
|
||||
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected,
|
||||
payTo)).resultFuture
|
||||
@ -100,7 +98,7 @@ class CashPaymentFlowTests {
|
||||
|
||||
@Test
|
||||
fun `pay zero cash`() {
|
||||
val payTo = notaryNode.info.chooseIdentity()
|
||||
val payTo = aliceNode.info.chooseIdentity()
|
||||
val expected = 0.DOLLARS
|
||||
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected,
|
||||
payTo)).resultFuture
|
||||
|
@ -61,7 +61,8 @@ class CordappPlugin : Plugin<Project> {
|
||||
}
|
||||
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
|
||||
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." +
|
||||
"This can cause node stability problems. Please use 'corda' instead." +
|
||||
"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.
|
||||
*/
|
||||
@ -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.
|
||||
*/
|
||||
@ -75,6 +93,15 @@ public class CordformNode implements NodeDefinition {
|
||||
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.
|
||||
*
|
||||
|
@ -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.
|
||||
*/
|
||||
@ -70,6 +70,16 @@ class Node(private val project: Project) : CordformNode() {
|
||||
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.
|
||||
*
|
||||
@ -107,15 +117,6 @@ class Node(private val project: Project) : CordformNode() {
|
||||
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) {
|
||||
if(name == null) {
|
||||
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
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
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.SerializationDefaults
|
||||
import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
|
||||
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||
|
||||
/*
|
||||
* Serialisation contexts for the client.
|
||||
* These have been refactored into a separate file to prevent
|
||||
* servers from trying to instantiate any of them.
|
||||
*/
|
||||
|
||||
val KRYO_RPC_CLIENT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
||||
SerializationDefaults.javaClass.classLoader,
|
||||
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
||||
emptyMap(),
|
||||
true,
|
||||
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.utilities.loggerFor
|
||||
import net.corda.nodeapi.internal.serialization.amqp.hasAnnotationInHierarchy
|
||||
import net.corda.nodeapi.internal.serialization.kryo.ThrowableSerializer
|
||||
import java.io.PrintWriter
|
||||
import java.lang.reflect.Modifier.isAbstract
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
@ -1,26 +1,13 @@
|
||||
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.CacheBuilder
|
||||
import net.corda.core.contracts.Attachment
|
||||
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.utilities.ByteSequence
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.nodeapi.internal.AttachmentsClassLoader
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.NotSerializableException
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@ -158,124 +145,8 @@ open class SerializationFactoryImpl : SerializationFactory() {
|
||||
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 {
|
||||
// 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.SerializationDefaults
|
||||
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.
|
||||
@ -16,18 +21,28 @@ import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
|
||||
* CANNOT always be instantiated outside of the server and so
|
||||
* MUST be kept separate!
|
||||
*/
|
||||
|
||||
val KRYO_RPC_SERVER_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
||||
SerializationDefaults.javaClass.classLoader,
|
||||
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
|
||||
emptyMap(),
|
||||
true,
|
||||
SerializationContext.UseCase.RPCServer)
|
||||
|
||||
val KRYO_STORAGE_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
||||
SerializationDefaults.javaClass.classLoader,
|
||||
AllButBlacklisted,
|
||||
emptyMap(),
|
||||
true,
|
||||
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,
|
||||
SerializationDefaults.javaClass.classLoader,
|
||||
QuasarWhitelist,
|
||||
@ -35,9 +50,6 @@ val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
|
||||
true,
|
||||
SerializationContext.UseCase.Checkpoint)
|
||||
|
||||
object QuasarWhitelist : ClassWhitelist {
|
||||
override fun hasListed(type: Class<*>): Boolean = true
|
||||
}
|
||||
|
||||
val AMQP_STORAGE_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
|
||||
SerializationDefaults.javaClass.classLoader,
|
||||
@ -45,3 +57,18 @@ val AMQP_STORAGE_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
|
||||
emptyMap(),
|
||||
true,
|
||||
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")
|
||||
|
||||
package net.corda.nodeapi.internal.serialization
|
||||
package net.corda.nodeapi.internal.serialization.amqp
|
||||
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
|
||||
import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput
|
||||
import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput
|
||||
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
|
||||
import net.corda.nodeapi.internal.serialization.DefaultWhitelist
|
||||
import net.corda.nodeapi.internal.serialization.MutableClassWhitelist
|
||||
import net.corda.nodeapi.internal.serialization.SerializationScheme
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
@ -25,7 +24,7 @@ fun SerializerFactory.addToWhitelist(vararg types: Class<*>) {
|
||||
}
|
||||
|
||||
abstract class AbstractAMQPSerializationScheme : SerializationScheme {
|
||||
internal companion object {
|
||||
companion object {
|
||||
private val serializationWhitelists: List<SerializationWhitelist> by lazy {
|
||||
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.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.Serializer
|
||||
@ -23,6 +23,10 @@ import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.utilities.NonEmptySet
|
||||
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.EdDSAPublicKey
|
||||
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.factories.ReflectionSerializerFactory
|
||||
@ -21,11 +21,9 @@ import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.toFuture
|
||||
import net.corda.core.toObservable
|
||||
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.nodeapi.internal.serialization.CordaClassResolver
|
||||
import net.corda.nodeapi.internal.serialization.serializationContextKey
|
||||
import org.bouncycastle.asn1.ASN1InputStream
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
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>() {
|
||||
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.KryoException
|
@ -5,6 +5,8 @@ import net.corda.core.serialization.SerializationContext;
|
||||
import net.corda.core.serialization.SerializationFactory;
|
||||
import net.corda.core.serialization.SerializedBytes;
|
||||
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.Rule;
|
||||
import org.junit.Test;
|
||||
@ -31,7 +33,7 @@ public final class ForbiddenLambdaSerializationTests {
|
||||
EnumSet<SerializationContext.UseCase> contexts = EnumSet.complementOf(EnumSet.of(SerializationContext.UseCase.Checkpoint));
|
||||
|
||||
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";
|
||||
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));
|
||||
|
||||
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";
|
||||
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.SerializedBytes;
|
||||
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.Rule;
|
||||
import org.junit.Test;
|
||||
@ -24,7 +26,7 @@ public final class LambdaCheckpointSerializationTest {
|
||||
@Before
|
||||
public void setup() {
|
||||
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
|
||||
|
@ -10,6 +10,8 @@ import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.nodeapi.internal.AttachmentsClassLoader
|
||||
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.rigorousMock
|
||||
import org.junit.Rule
|
||||
|
@ -13,6 +13,7 @@ import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.sequence
|
||||
import net.corda.node.serialization.KryoServerSerializationScheme
|
||||
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.SerializationEnvironmentRule
|
||||
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.Envelope
|
||||
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.kryoSpecific
|
||||
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.serialize
|
||||
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.kryoSpecific
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
@ -84,4 +85,4 @@ class MapsSerializationTest {
|
||||
}
|
||||
assertArrayEquals(output.toByteArray(), serializedForm.bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,10 @@ import com.esotericsoftware.kryo.KryoException
|
||||
import com.esotericsoftware.kryo.io.Output
|
||||
import net.corda.core.serialization.*
|
||||
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.SerializationEnvironmentRule
|
||||
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.serialize
|
||||
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.SerializationEnvironmentRule
|
||||
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.transactions.LedgerTransaction
|
||||
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.EmptyWhitelist
|
||||
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive
|
||||
|
@ -19,9 +19,6 @@ configurations {
|
||||
|
||||
integrationTestCompile.extendsFrom testCompile
|
||||
integrationTestRuntime.extendsFrom testRuntime
|
||||
|
||||
smokeTestCompile.extendsFrom compile
|
||||
smokeTestRuntime.extendsFrom runtime
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -40,20 +37,6 @@ sourceSets {
|
||||
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.
|
||||
@ -62,13 +45,6 @@ processResources {
|
||||
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
|
||||
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
|
||||
|
||||
@ -184,11 +160,6 @@ dependencies {
|
||||
integrationTestCompile "junit:junit:$junit_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.
|
||||
// Web stuff: for HTTP[S] servlets
|
||||
testCompile "org.eclipse.jetty:jetty-servlet:${jetty_version}"
|
||||
@ -206,17 +177,6 @@ task integrationTest(type: Test) {
|
||||
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 {
|
||||
baseName 'corda-node'
|
||||
}
|
||||
|
@ -7,29 +7,24 @@ import net.corda.core.internal.div
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
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.internal.ServiceInfo
|
||||
import net.corda.nodeapi.internal.ServiceType
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.ProjectStructure.projectRootDir
|
||||
import net.corda.testing.driver.ListenProcessDeathException
|
||||
import net.corda.testing.driver.driver
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.io.*
|
||||
import java.nio.file.Files
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class BootTests {
|
||||
|
||||
@Test
|
||||
fun `java deserialization is disabled`() {
|
||||
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().
|
||||
start(user.username, user.password).proxy.startFlow(::ObjectInputStreamFlow).returnValue
|
||||
assertThatThrownBy { future.getOrThrow() }.isInstanceOf(InvalidClassException::class.java).hasMessage("filter status: REJECTED")
|
||||
@ -53,16 +48,6 @@ class BootTests {
|
||||
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
|
||||
|
@ -7,7 +7,7 @@ import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
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.testing.ALICE
|
||||
import net.corda.testing.BOB
|
||||
@ -19,7 +19,7 @@ import org.junit.Test
|
||||
class CordappScanningDriverTest {
|
||||
@Test
|
||||
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
|
||||
driver {
|
||||
val (alice, bob) = listOf(
|
||||
|
@ -12,12 +12,12 @@ import net.corda.core.utilities.minutes
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
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.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.performance.div
|
||||
import net.corda.testing.performance.startPublishingFixedRateInjector
|
||||
import net.corda.testing.performance.startReporter
|
||||
@ -59,7 +59,7 @@ class NodePerformanceTests {
|
||||
@Test
|
||||
fun `empty flow per second`() {
|
||||
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 ->
|
||||
val timings = Collections.synchronizedList(ArrayList<Long>())
|
||||
@ -89,7 +89,7 @@ class NodePerformanceTests {
|
||||
@Test
|
||||
fun `empty flow rate`() {
|
||||
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
|
||||
val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics)
|
||||
a.rpcClientToNode().use("A", "A") { connection ->
|
||||
@ -102,26 +102,25 @@ class NodePerformanceTests {
|
||||
|
||||
@Test
|
||||
fun `self pay rate`() {
|
||||
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
||||
val a = startNotaryNode(
|
||||
DUMMY_NOTARY.name,
|
||||
rpcUsers = listOf(User("A", "A", setOf(startFlowPermission<CashIssueFlow>(), startFlowPermission<CashPaymentFlow>())))
|
||||
).getOrThrow()
|
||||
a as NodeHandle.InProcess
|
||||
val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics)
|
||||
a.rpcClientToNode().use("A", "A") { connection ->
|
||||
val notary = connection.proxy.notaryIdentities().first()
|
||||
val user = User("A", "A", setOf(startFlow<CashIssueFlow>(), startFlow<CashPaymentFlow>()))
|
||||
driver(
|
||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, rpcUsers = listOf(user))),
|
||||
startNodesInProcess = true,
|
||||
extraCordappPackagesToScan = listOf("net.corda.finance")
|
||||
) {
|
||||
val notary = defaultNotaryNode.getOrThrow() as NodeHandle.InProcess
|
||||
val metricRegistry = startReporter(shutdownManager, notary.node.services.monitoringService.metrics)
|
||||
notary.rpcClientToNode().use("A", "A") { connection ->
|
||||
println("ISSUING")
|
||||
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()
|
||||
doneFutures.transpose().get()
|
||||
println("STARTING PAYMENT")
|
||||
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.serialization.SerializationFactory
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.DUMMY_BANK_A
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.driver.DriverDSLExposedInterface
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
@ -53,20 +52,15 @@ class AttachmentLoadingTests {
|
||||
|
||||
val bankAName = CordaX500Name("BankA", "Zurich", "CH")
|
||||
val bankBName = CordaX500Name("BankB", "Zurich", "CH")
|
||||
val notaryName = CordaX500Name("Notary", "Zurich", "CH")
|
||||
val flowInitiatorClass =
|
||||
val flowInitiatorClass: Class<out FlowLogic<*>> =
|
||||
Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR)))
|
||||
.asSubclass(FlowLogic::class.java)
|
||||
|
||||
private fun DriverDSLExposedInterface.createTwoNodesAndNotary(): List<NodeHandle> {
|
||||
val adminUser = User("admin", "admin", permissions = setOf("ALL"))
|
||||
val nodes = listOf(
|
||||
startNode(providedName = bankAName, rpcUsers = listOf(adminUser)),
|
||||
startNode(providedName = bankBName, rpcUsers = listOf(adminUser)),
|
||||
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.createTwoNodes(): List<NodeHandle> {
|
||||
return listOf(
|
||||
startNode(providedName = bankAName),
|
||||
startNode(providedName = bankBName)
|
||||
).transpose().getOrThrow()
|
||||
}
|
||||
|
||||
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
|
||||
@ -103,9 +88,8 @@ class AttachmentLoadingTests {
|
||||
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 contract = contractClass.newInstance()
|
||||
val txBuilder = generateInitialMethod.invoke(contract, PartyAndReference(DUMMY_BANK_A, OpaqueBytes(kotlin.ByteArray(1))), 1, DUMMY_NOTARY) as TransactionBuilder
|
||||
val context = SerializationFactory.defaultFactory.defaultContext
|
||||
.withClassLoader(appClassLoader)
|
||||
val txBuilder = generateInitialMethod.invoke(contract, DUMMY_BANK_A.ref(1), 1, DUMMY_NOTARY) as TransactionBuilder
|
||||
val context = SerializationFactory.defaultFactory.defaultContext.withClassLoader(appClassLoader)
|
||||
val ledgerTx = txBuilder.toLedgerTransaction(services, context)
|
||||
contract.verify(ledgerTx)
|
||||
|
||||
@ -119,11 +103,9 @@ class AttachmentLoadingTests {
|
||||
fun `test that attachments retrieved over the network are not used for code`() {
|
||||
driver(initialiseSerialization = false) {
|
||||
installIsolatedCordappTo(bankAName)
|
||||
val (bankA, bankB, _) = createTwoNodesAndNotary()
|
||||
eventuallyPassingTest {
|
||||
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()
|
||||
}
|
||||
val (bankA, bankB) = createTwoNodes()
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -133,10 +115,8 @@ class AttachmentLoadingTests {
|
||||
driver(initialiseSerialization = false) {
|
||||
installIsolatedCordappTo(bankAName)
|
||||
installIsolatedCordappTo(bankBName)
|
||||
val (bankA, bankB, _) = createTwoNodesAndNotary()
|
||||
eventuallyPassingTest {
|
||||
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
||||
}
|
||||
val (bankA, bankB) = createTwoNodes()
|
||||
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.internal.deleteIfExists
|
||||
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.TransactionBuilder
|
||||
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.services.config.BFTSMaRtConfiguration
|
||||
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.minCorrectReplicas
|
||||
import net.corda.node.utilities.ServiceIdentityGenerator
|
||||
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.dummyCommand
|
||||
import net.corda.testing.getDefaultNotary
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNetwork.MockNode
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import org.junit.After
|
||||
import org.junit.Test
|
||||
@ -38,39 +41,47 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class BFTNotaryServiceTests {
|
||||
companion object {
|
||||
private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH")
|
||||
}
|
||||
|
||||
private val mockNet = MockNetwork()
|
||||
private val node = mockNet.createNode()
|
||||
private lateinit var notary: Party
|
||||
private lateinit var node: StartedNode<MockNode>
|
||||
|
||||
@After
|
||||
fun 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?
|
||||
val replicaIds = (0 until clusterSize)
|
||||
ServiceIdentityGenerator.generateToDisk(
|
||||
|
||||
notary = ServiceIdentityGenerator.generateToDisk(
|
||||
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) }
|
||||
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))
|
||||
doReturn(notary).whenever(it).notary
|
||||
}))
|
||||
}
|
||||
mockNet.runNetwork() // Exchange initial network map registration messages.
|
||||
} + mockNet.createUnstartedNode()
|
||||
|
||||
// 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. */
|
||||
@Test
|
||||
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.
|
||||
val notary = node.services.getDefaultNotary()
|
||||
startBftClusterAndNode(minClusterSize(1), exposeRaces = true) // This true adds a sleep to expose the race.
|
||||
val f = node.run {
|
||||
val trivialTx = signInitialTransaction(notary) {
|
||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
||||
@ -94,8 +105,7 @@ class BFTNotaryServiceTests {
|
||||
|
||||
private fun detectDoubleSpend(faultyReplicas: Int) {
|
||||
val clusterSize = minClusterSize(faultyReplicas)
|
||||
bftNotaryCluster(clusterSize)
|
||||
val notary = node.services.getDefaultNotary()
|
||||
startBftClusterAndNode(clusterSize)
|
||||
node.run {
|
||||
val issueTx = signInitialTransaction(notary) {
|
||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
||||
@ -138,15 +148,13 @@ class BFTNotaryServiceTests {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun StartedNode<*>.signInitialTransaction(
|
||||
notary: Party,
|
||||
block: TransactionBuilder.() -> Any?
|
||||
): SignedTransaction {
|
||||
return services.signInitialTransaction(
|
||||
TransactionBuilder(notary).apply {
|
||||
addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey))
|
||||
block()
|
||||
})
|
||||
private fun StartedNode<*>.signInitialTransaction(notary: Party, block: TransactionBuilder.() -> Any?): SignedTransaction {
|
||||
return services.signInitialTransaction(
|
||||
TransactionBuilder(notary).apply {
|
||||
addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey))
|
||||
block()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -11,65 +11,70 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.POUNDS
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
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 rx.Observable
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class DistributedServiceTests : DriverBasedTest() {
|
||||
lateinit var alice: NodeHandle
|
||||
lateinit var notaries: List<NodeHandle.OutOfProcess>
|
||||
lateinit var aliceProxy: CordaRPCOps
|
||||
lateinit var raftNotaryIdentity: Party
|
||||
lateinit var notaryStateMachines: Observable<Pair<Party, StateMachineUpdate>>
|
||||
class DistributedServiceTests {
|
||||
private lateinit var alice: NodeHandle
|
||||
private lateinit var notaryNodes: List<NodeHandle.OutOfProcess>
|
||||
private lateinit var aliceProxy: CordaRPCOps
|
||||
private lateinit var raftNotaryIdentity: Party
|
||||
private lateinit var notaryStateMachines: Observable<Pair<Party, StateMachineUpdate>>
|
||||
|
||||
override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) {
|
||||
// Start Alice and 3 notaries in a RAFT cluster
|
||||
val clusterSize = 3
|
||||
private fun setup(testBlock: () -> Unit) {
|
||||
val testUser = User("test", "test", permissions = setOf(
|
||||
startFlowPermission<CashIssueFlow>(),
|
||||
startFlowPermission<CashPaymentFlow>())
|
||||
)
|
||||
val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser))
|
||||
val notariesFuture = startNotaryCluster(
|
||||
DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.id),
|
||||
rpcUsers = listOf(testUser),
|
||||
clusterSize = clusterSize
|
||||
startFlow<CashIssueFlow>(),
|
||||
startFlow<CashPaymentFlow>(),
|
||||
invokeRpc(CordaRPCOps::nodeInfo),
|
||||
invokeRpc(CordaRPCOps::stateMachinesFeed))
|
||||
)
|
||||
|
||||
alice = aliceFuture.get()
|
||||
val (notaryIdentity, notaryNodes) = notariesFuture.get()
|
||||
raftNotaryIdentity = notaryIdentity
|
||||
notaries = notaryNodes.map { it as NodeHandle.OutOfProcess }
|
||||
driver(
|
||||
extraCordappPackagesToScan = listOf("net.corda.finance.contracts"),
|
||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, rpcUsers = listOf(testUser), cluster = ClusterSpec.Raft(clusterSize = 3))))
|
||||
{
|
||||
alice = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)).getOrThrow()
|
||||
raftNotaryIdentity = defaultNotaryIdentity
|
||||
notaryNodes = defaultNotaryHandle.nodeHandles.getOrThrow().map { it as NodeHandle.OutOfProcess }
|
||||
|
||||
assertEquals(notaries.size, clusterSize)
|
||||
// Check that each notary has different identity as a node.
|
||||
assertEquals(notaries.size, notaries.map { it.nodeInfo.chooseIdentity() }.toSet().size)
|
||||
// Connect to Alice and the notaries
|
||||
fun connectRpc(node: NodeHandle): CordaRPCOps {
|
||||
val client = node.rpcClientToNode()
|
||||
return client.start("test", "test").proxy
|
||||
assertThat(notaryNodes).hasSize(3)
|
||||
|
||||
for (notaryNode in notaryNodes) {
|
||||
assertThat(notaryNode.nodeInfo.legalIdentities).contains(raftNotaryIdentity)
|
||||
}
|
||||
|
||||
// Check that each notary has different identity as a node.
|
||||
assertThat(notaryNodes.flatMap { it.nodeInfo.legalIdentities - raftNotaryIdentity }.toSet()).hasSameSizeAs(notaryNodes)
|
||||
|
||||
// Connect to Alice and the notaries
|
||||
fun connectRpc(node: NodeHandle): CordaRPCOps {
|
||||
val client = node.rpcClientToNode()
|
||||
return client.start("test", "test").proxy
|
||||
}
|
||||
aliceProxy = connectRpc(alice)
|
||||
val rpcClientsToNotaries = notaryNodes.map(::connectRpc)
|
||||
notaryStateMachines = Observable.from(rpcClientsToNotaries.map { proxy ->
|
||||
proxy.stateMachinesFeed().updates.map { Pair(proxy.nodeInfo().chooseIdentity(), it) }
|
||||
}).flatMap { it.onErrorResumeNext(Observable.empty()) }.bufferUntilSubscribed()
|
||||
|
||||
testBlock()
|
||||
}
|
||||
aliceProxy = connectRpc(alice)
|
||||
val rpcClientsToNotaries = notaries.map(::connectRpc)
|
||||
notaryStateMachines = Observable.from(rpcClientsToNotaries.map { proxy ->
|
||||
proxy.stateMachinesFeed().updates.map { Pair(proxy.nodeInfo().chooseIdentity(), it) }
|
||||
}).flatMap { it.onErrorResumeNext(Observable.empty()) }.bufferUntilSubscribed()
|
||||
|
||||
runTest()
|
||||
}
|
||||
|
||||
// TODO Use a dummy distributed service rather than a Raft Notary Service as this test is only about Artemis' ability
|
||||
// to handle distributed services
|
||||
@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
|
||||
issueCash(100.POUNDS)
|
||||
|
||||
@ -97,7 +102,7 @@ class DistributedServiceTests : DriverBasedTest() {
|
||||
|
||||
// TODO This should be in RaftNotaryServiceTests
|
||||
@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
|
||||
issueCash(100.POUNDS)
|
||||
|
||||
@ -105,8 +110,8 @@ class DistributedServiceTests : DriverBasedTest() {
|
||||
paySelf(5.POUNDS)
|
||||
}
|
||||
|
||||
// Now kill a notary
|
||||
with(notaries[0].process) {
|
||||
// Now kill a notary node
|
||||
with(notaryNodes[0].process) {
|
||||
destroy()
|
||||
waitFor()
|
||||
}
|
||||
@ -137,4 +142,4 @@ class DistributedServiceTests : DriverBasedTest() {
|
||||
private fun paySelf(amount: Amount<Currency>) {
|
||||
aliceProxy.startFlow(::CashPaymentFlow, amount, alice.nodeInfo.chooseIdentity()).returnValue.getOrThrow()
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,8 @@ import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.node.ClusterSpec
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
@ -28,13 +30,16 @@ class RaftNotaryServiceTests {
|
||||
|
||||
@Test
|
||||
fun `detect double spend`() {
|
||||
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
|
||||
val (notaryParty) = startNotaryCluster(notaryName, 3).getOrThrow()
|
||||
driver(
|
||||
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 inputState = issueState(bankA, notaryParty)
|
||||
val inputState = issueState(bankA, defaultNotaryIdentity)
|
||||
|
||||
val firstTxBuilder = TransactionBuilder(notaryParty)
|
||||
val firstTxBuilder = TransactionBuilder(defaultNotaryIdentity)
|
||||
.addInputState(inputState)
|
||||
.addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey))
|
||||
val firstSpendTx = bankA.services.signInitialTransaction(firstTxBuilder)
|
||||
@ -42,7 +47,7 @@ class RaftNotaryServiceTests {
|
||||
val firstSpend = bankA.services.startFlow(NotaryFlow.Client(firstSpendTx))
|
||||
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())
|
||||
addOutputState(dummyState, DummyContract.PROGRAM_ID)
|
||||
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.div
|
||||
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.testing.ALICE
|
||||
import net.corda.testing.ALICE_KEY
|
||||
import net.corda.testing.DEV_TRUST_ROOT
|
||||
import net.corda.testing.getTestPartyAndCertificate
|
||||
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.contentOf
|
||||
import org.junit.Before
|
||||
@ -31,7 +27,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
||||
val nodeInfo = NodeInfo(listOf(), listOf(getTestPartyAndCertificate(ALICE)), 0, 0)
|
||||
}
|
||||
|
||||
private lateinit var keyManagementService: KeyManagementService
|
||||
private lateinit var nodeInfoPath: Path
|
||||
private val scheduler = TestScheduler()
|
||||
private val testSubscriber = TestSubscriber<NodeInfo>()
|
||||
@ -41,16 +36,15 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
|
||||
keyManagementService = MockKeyManagementService(identityService, ALICE_KEY)
|
||||
nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler = scheduler)
|
||||
nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `save a NodeInfo`() {
|
||||
assertEquals(0, tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.size)
|
||||
NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, keyManagementService)
|
||||
assertEquals(0,
|
||||
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) }
|
||||
assertEquals(1, nodeInfoFiles.size)
|
||||
@ -65,7 +59,7 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
||||
fun `save a NodeInfo to JimFs`() {
|
||||
val jimFs = Jimfs.newFileSystem(Configuration.unix())
|
||||
val jimFolder = jimFs.getPath("/nodeInfo")
|
||||
NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, keyManagementService)
|
||||
NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, ALICE_KEY)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -134,6 +128,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
||||
|
||||
// Write a nodeInfo under the right path.
|
||||
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.testing.ALICE
|
||||
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.internal.NodeBasedTest
|
||||
import org.junit.Before
|
||||
@ -16,9 +16,9 @@ import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
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 infos: MutableSet<NodeInfo> = HashSet()
|
||||
private val infos = HashSet<NodeInfo>()
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
@ -37,8 +37,8 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() {
|
||||
alice.database.transaction {
|
||||
val res = netCache.getNodeByLegalIdentity(alice.info.chooseIdentity())
|
||||
assertEquals(alice.info, res)
|
||||
val res2 = netCache.getNodeByLegalName(DUMMY_NOTARY.name)
|
||||
assertEquals(infos.singleOrNull { DUMMY_NOTARY.name in it.legalIdentitiesAndCerts.map { it.name } }, res2)
|
||||
val res2 = netCache.getNodeByLegalName(DUMMY_REGULATOR.name)
|
||||
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.testing.BOB
|
||||
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.DummyState
|
||||
import net.corda.testing.driver.driver
|
||||
@ -65,7 +65,7 @@ class LargeTransactionsTest {
|
||||
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2)
|
||||
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3)
|
||||
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
|
||||
val (alice, _, _) = aliceBobAndNotary()
|
||||
val (alice, _) = aliceAndBob()
|
||||
alice.useRPC {
|
||||
val hash1 = it.uploadAttachment(bigFile1.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.GeneralSubtree
|
||||
import org.bouncycastle.asn1.x509.NameConstraints
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.junit.Test
|
||||
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.NodeHandle
|
||||
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.junit.Test
|
||||
import java.util.*
|
||||
@ -35,16 +37,14 @@ class P2PMessagingTest {
|
||||
|
||||
@Test
|
||||
fun `communicating with a distributed service which we're part of`() {
|
||||
driver(startNodesInProcess = true) {
|
||||
val distributedService = startDistributedService()
|
||||
startDriverWithDistributedService { distributedService ->
|
||||
assertAllNodesAreUsed(distributedService, DISTRIBUTED_SERVICE_NAME, distributedService[0])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `distributed service requests are retried if one of the nodes in the cluster goes down without sending a response`() {
|
||||
driver(startNodesInProcess = true) {
|
||||
val distributedServiceNodes = startDistributedService()
|
||||
startDriverWithDistributedService { distributedServiceNodes ->
|
||||
val alice = startAlice()
|
||||
val serviceAddress = alice.services.networkMapCache.run {
|
||||
val notaryParty = notaryIdentities.randomOrNull()!!
|
||||
@ -77,8 +77,7 @@ class P2PMessagingTest {
|
||||
|
||||
@Test
|
||||
fun `distributed service request retries are persisted across client node restarts`() {
|
||||
driver(startNodesInProcess = true) {
|
||||
val distributedServiceNodes = startDistributedService()
|
||||
startDriverWithDistributedService { distributedServiceNodes ->
|
||||
val alice = startAlice()
|
||||
val serviceAddress = alice.services.networkMapCache.run {
|
||||
val notaryParty = notaryIdentities.randomOrNull()!!
|
||||
@ -117,11 +116,10 @@ class P2PMessagingTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun DriverDSLExposedInterface.startDistributedService(): List<StartedNode<Node>> {
|
||||
return startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2)
|
||||
.getOrThrow()
|
||||
.second
|
||||
.map { (it as NodeHandle.InProcess).node }
|
||||
private fun startDriverWithDistributedService(dsl: DriverDSLExposedInterface.(List<StartedNode<Node>>) -> Unit) {
|
||||
driver(startNodesInProcess = true, notarySpecs = listOf(NotarySpec(DISTRIBUTED_SERVICE_NAME, cluster = ClusterSpec.Raft(clusterSize = 2)))) {
|
||||
dsl(defaultNotaryHandle.nodeHandles.getOrThrow().map { (it as NodeHandle.InProcess).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.identity.AbstractParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
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.utilities.ProgressTracker
|
||||
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.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.driver.DriverDSLExposedInterface
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import org.junit.Assume
|
||||
import org.junit.Assume.assumeFalse
|
||||
import org.junit.Test
|
||||
import java.lang.management.ManagementFactory
|
||||
import javax.persistence.Column
|
||||
@ -35,30 +32,26 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class NodeStatePersistenceTests {
|
||||
|
||||
@Test
|
||||
fun `persistent state survives node restart`() {
|
||||
// Temporary disable this test when executed on Windows. It is known to be sporadically failing.
|
||||
// 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!")
|
||||
driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
|
||||
val (nodeName, notaryNodeHandle) = {
|
||||
val notaryNodeHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow()
|
||||
val nodeName = {
|
||||
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
|
||||
ensureAcquainted(notaryNodeHandle, nodeHandle)
|
||||
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name
|
||||
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
||||
it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow()
|
||||
}
|
||||
nodeHandle.stop().getOrThrow()
|
||||
nodeName to notaryNodeHandle
|
||||
nodeHandle.stop()
|
||||
nodeName
|
||||
}()
|
||||
|
||||
val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow()
|
||||
ensureAcquainted(notaryNodeHandle, nodeHandle)
|
||||
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
||||
val page = it.proxy.vaultQuery(MessageState::class.java)
|
||||
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 {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user