Merge pull request #93 from corda/christians_os_merge_20171106

Merge OS -> enterprise as of 20171106
This commit is contained in:
Christian Sailer 2017-11-07 09:42:27 +00:00 committed by GitHub
commit fd94732261
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
209 changed files with 2539 additions and 2990 deletions

View File

@ -2344,66 +2344,6 @@ public class net.corda.core.schemas.MappedSchema extends java.lang.Object
public final int getVersion() public final int getVersion()
@org.jetbrains.annotations.NotNull public String toString() @org.jetbrains.annotations.NotNull public String toString()
## ##
public final class net.corda.core.schemas.NodeInfoSchema extends java.lang.Object
public static final net.corda.core.schemas.NodeInfoSchema INSTANCE
##
public final class net.corda.core.schemas.NodeInfoSchemaV1 extends net.corda.core.schemas.MappedSchema
public static final net.corda.core.schemas.NodeInfoSchemaV1 INSTANCE
##
@javax.persistence.Entity public static final class net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort extends java.lang.Object
public <init>()
public <init>(net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort)
@org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort copy(net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort)
public boolean equals(Object)
public int hashCode()
@org.jetbrains.annotations.NotNull public final net.corda.core.utilities.NetworkHostAndPort toHostAndPort()
public String toString()
public static final net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort$Companion Companion
##
public static final class net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort$Companion extends java.lang.Object
@org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$DBHostAndPort fromHostAndPort(net.corda.core.utilities.NetworkHostAndPort)
##
@javax.persistence.Entity @javax.persistence.Table public static final class net.corda.core.schemas.NodeInfoSchemaV1$DBPartyAndCertificate extends java.lang.Object
public <init>()
public <init>(String, String, byte[], boolean, Set)
public <init>(net.corda.core.identity.PartyAndCertificate, boolean)
@org.jetbrains.annotations.NotNull public final String component1()
@org.jetbrains.annotations.NotNull public final String component2()
@org.jetbrains.annotations.NotNull public final byte[] component3()
public final boolean component4()
@org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$DBPartyAndCertificate copy(String, String, byte[], boolean, Set)
public boolean equals(Object)
@org.jetbrains.annotations.NotNull public final String getName()
@org.jetbrains.annotations.NotNull public final String getOwningKeyHash()
@org.jetbrains.annotations.NotNull public final byte[] getPartyCertBinary()
public int hashCode()
public final boolean isMain()
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.PartyAndCertificate toLegalIdentityAndCert()
public String toString()
##
@javax.persistence.Embeddable public static final class net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort extends java.lang.Object implements java.io.Serializable
public <init>()
public <init>(String, Integer)
@org.jetbrains.annotations.Nullable public final String component1()
@org.jetbrains.annotations.Nullable public final Integer component2()
@org.jetbrains.annotations.NotNull public final net.corda.core.schemas.NodeInfoSchemaV1$PKHostAndPort copy(String, Integer)
public boolean equals(Object)
@org.jetbrains.annotations.Nullable public final String getHost()
@org.jetbrains.annotations.Nullable public final Integer getPort()
public int hashCode()
public String toString()
##
@javax.persistence.Entity @javax.persistence.Table public static final class net.corda.core.schemas.NodeInfoSchemaV1$PersistentNodeInfo extends java.lang.Object
public <init>()
public <init>(int, List, List, int, long)
@org.jetbrains.annotations.NotNull public final List getAddresses()
public final int getId()
@org.jetbrains.annotations.NotNull public final List getLegalIdentitiesAndCerts()
public final int getPlatformVersion()
public final long getSerial()
public final void setId(int)
@org.jetbrains.annotations.NotNull public final net.corda.core.node.NodeInfo toNodeInfo()
##
@javax.persistence.MappedSuperclass @net.corda.core.serialization.CordaSerializable public class net.corda.core.schemas.PersistentState extends java.lang.Object implements net.corda.core.schemas.StatePersistable @javax.persistence.MappedSuperclass @net.corda.core.serialization.CordaSerializable public class net.corda.core.schemas.PersistentState extends java.lang.Object implements net.corda.core.schemas.StatePersistable
public <init>() public <init>()
public <init>(net.corda.core.schemas.PersistentStateRef) public <init>(net.corda.core.schemas.PersistentStateRef)

View File

@ -52,6 +52,10 @@ if [ $abstractCount -gt 0 ]; then
fi fi
badChanges=$(($removalCount + $abstractCount)) badChanges=$(($removalCount + $abstractCount))
if [ $badChanges -gt 255 ]; then
echo "OVERFLOW! Number of bad API changes: $badChanges"
badChanges=255
fi
echo "Exiting with exit code" $badChanges echo "Exiting with exit code" $badChanges
exit $badChanges exit $badChanges

9
.idea/compiler.xml generated
View File

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel> <bytecodeTargetLevel target="1.8">
<module name="api-scanner_main" target="1.8" />
<module name="api-scanner_test" target="1.8" />
<module name="attachment-demo_integrationTest" target="1.8" /> <module name="attachment-demo_integrationTest" target="1.8" />
<module name="attachment-demo_main" target="1.8" /> <module name="attachment-demo_main" target="1.8" />
<module name="attachment-demo_test" target="1.8" /> <module name="attachment-demo_test" target="1.8" />
@ -21,7 +23,12 @@
<module name="cordapp_test" target="1.8" /> <module name="cordapp_test" target="1.8" />
<module name="cordform-common_main" target="1.8" /> <module name="cordform-common_main" target="1.8" />
<module name="cordform-common_test" target="1.8" /> <module name="cordform-common_test" target="1.8" />
<module name="cordformation_main" target="1.8" />
<module name="cordformation_runnodes" target="1.8" />
<module name="cordformation_test" target="1.8" />
<module name="core_integrationTest" target="1.8" />
<module name="core_main" target="1.8" /> <module name="core_main" target="1.8" />
<module name="core_smokeTest" target="1.8" />
<module name="core_test" target="1.8" /> <module name="core_test" target="1.8" />
<module name="demobench_main" target="1.8" /> <module name="demobench_main" target="1.8" />
<module name="demobench_test" target="1.8" /> <module name="demobench_test" target="1.8" />

View File

@ -267,9 +267,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
} }
node { node {
name "O=Bank B,OU=corda,L=London,C=GB" name "O=Bank B,OU=corda,L=London,C=GB"
p2pPort 10007 p2pAddress "localhost:10007"
rpcPort 10008 rpcAddress "localhost:10008"
webPort 10009 webAddress "localhost:10009"
cordapps = [] cordapps = []
} }
} }

View File

@ -26,64 +26,71 @@ import net.corda.finance.USD
import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashExitFlow
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.node.DriverBasedTest
import org.junit.Test import org.junit.Test
import rx.Observable import rx.Observable
class NodeMonitorModelTest : DriverBasedTest() { class NodeMonitorModelTest {
lateinit var aliceNode: NodeInfo private lateinit var aliceNode: NodeInfo
lateinit var bobNode: NodeInfo private lateinit var bobNode: NodeInfo
lateinit var notaryParty: Party private lateinit var notaryParty: Party
lateinit var rpc: CordaRPCOps private lateinit var rpc: CordaRPCOps
lateinit var rpcBob: CordaRPCOps private lateinit var rpcBob: CordaRPCOps
lateinit var stateMachineTransactionMapping: Observable<StateMachineTransactionMapping> private lateinit var stateMachineTransactionMapping: Observable<StateMachineTransactionMapping>
lateinit var stateMachineUpdates: Observable<StateMachineUpdate> private lateinit var stateMachineUpdates: Observable<StateMachineUpdate>
lateinit var stateMachineUpdatesBob: Observable<StateMachineUpdate> private lateinit var stateMachineUpdatesBob: Observable<StateMachineUpdate>
lateinit var progressTracking: Observable<ProgressTrackingEvent> private lateinit var progressTracking: Observable<ProgressTrackingEvent>
lateinit var transactions: Observable<SignedTransaction> private lateinit var transactions: Observable<SignedTransaction>
lateinit var vaultUpdates: Observable<Vault.Update<ContractState>> private lateinit var vaultUpdates: Observable<Vault.Update<ContractState>>
lateinit var networkMapUpdates: Observable<NetworkMapCache.MapChange> private lateinit var networkMapUpdates: Observable<NetworkMapCache.MapChange>
lateinit var newNode: (CordaX500Name) -> NodeInfo private lateinit var newNode: (CordaX500Name) -> NodeInfo
override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) { private fun setup(runTest: () -> Unit) {
val cashUser = User("user1", "test", permissions = setOf( driver(extraCordappPackagesToScan = listOf("net.corda.finance")) {
startFlowPermission<CashIssueFlow>(), val cashUser = User("user1", "test", permissions = setOf(
startFlowPermission<CashPaymentFlow>(), startFlow<CashIssueFlow>(),
startFlowPermission<CashExitFlow>()) startFlow<CashPaymentFlow>(),
) startFlow<CashExitFlow>(),
val aliceNodeFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)) invokeRpc(CordaRPCOps::notaryIdentities),
val notaryHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow() invokeRpc("vaultTrackBy"),
val aliceNodeHandle = aliceNodeFuture.getOrThrow() invokeRpc("vaultQueryBy"),
aliceNode = aliceNodeHandle.nodeInfo invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed),
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo } invokeRpc(CordaRPCOps::stateMachineRecordedTransactionMappingFeed),
val monitor = NodeMonitorModel() invokeRpc(CordaRPCOps::stateMachinesFeed),
stateMachineTransactionMapping = monitor.stateMachineTransactionMapping.bufferUntilSubscribed() invokeRpc(CordaRPCOps::networkMapFeed))
stateMachineUpdates = monitor.stateMachineUpdates.bufferUntilSubscribed() )
progressTracking = monitor.progressTracking.bufferUntilSubscribed() val aliceNodeHandle = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)).getOrThrow()
transactions = monitor.transactions.bufferUntilSubscribed() aliceNode = aliceNodeHandle.nodeInfo
vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed() newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
networkMapUpdates = monitor.networkMap.bufferUntilSubscribed() 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) monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
rpc = monitor.proxyObservable.value!! rpc = monitor.proxyObservable.value!!
notaryParty = notaryHandle.nodeInfo.legalIdentities[1] notaryParty = defaultNotaryIdentity
val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow() val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow()
bobNode = bobNodeHandle.nodeInfo bobNode = bobNodeHandle.nodeInfo
val monitorBob = NodeMonitorModel() val monitorBob = NodeMonitorModel()
stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed() stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed()
monitorBob.register(bobNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password) monitorBob.register(bobNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
rpcBob = monitorBob.proxyObservable.value!! rpcBob = monitorBob.proxyObservable.value!!
runTest() runTest()
}
} }
@Test @Test
fun `network map update`() { fun `network map update`() = setup {
val charlieNode = newNode(CHARLIE.name) val charlieNode = newNode(CHARLIE.name)
val nonServiceIdentities = aliceNode.legalIdentitiesAndCerts + bobNode.legalIdentitiesAndCerts + charlieNode.legalIdentitiesAndCerts val nonServiceIdentities = aliceNode.legalIdentitiesAndCerts + bobNode.legalIdentitiesAndCerts + charlieNode.legalIdentitiesAndCerts
networkMapUpdates.filter { it.node.legalIdentitiesAndCerts.any { it in nonServiceIdentities } } networkMapUpdates.filter { it.node.legalIdentitiesAndCerts.any { it in nonServiceIdentities } }
@ -104,7 +111,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
} }
@Test @Test
fun `cash issue works end to end`() { fun `cash issue works end to end`() = setup {
rpc.startFlow(::CashIssueFlow, rpc.startFlow(::CashIssueFlow,
Amount(100, USD), Amount(100, USD),
OpaqueBytes(ByteArray(1, { 1 })), OpaqueBytes(ByteArray(1, { 1 })),
@ -128,7 +135,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
} }
@Test @Test
fun `cash issue and move`() { fun `cash issue and move`() = setup {
val (_, issueIdentity) = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryParty).returnValue.getOrThrow() val (_, issueIdentity) = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryParty).returnValue.getOrThrow()
rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.chooseIdentity()).returnValue.getOrThrow() rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.chooseIdentity()).returnValue.getOrThrow()

View File

@ -40,7 +40,7 @@ class ContractStateModel {
return this.map { stateAndRef -> return this.map { stateAndRef ->
if (stateAndRef.state.data is Cash.State) { if (stateAndRef.state.data is Cash.State) {
// Kotlin doesn't unify here for some reason // Kotlin doesn't unify here for some reason
uncheckedCast(stateAndRef) uncheckedCast<StateAndRef<ContractState>, StateAndRef<Cash.State>>(stateAndRef)
} else { } else {
null null
} }

View File

@ -5,6 +5,7 @@ import com.google.common.cache.CacheLoader
import javafx.beans.value.ObservableValue import javafx.beans.value.ObservableValue
import javafx.collections.FXCollections import javafx.collections.FXCollections
import javafx.collections.ObservableList import javafx.collections.ObservableList
import net.corda.client.jfx.utils.ChosenList
import net.corda.client.jfx.utils.filterNotNull import net.corda.client.jfx.utils.filterNotNull
import net.corda.client.jfx.utils.fold import net.corda.client.jfx.utils.fold
import net.corda.client.jfx.utils.map import net.corda.client.jfx.utils.map
@ -12,7 +13,6 @@ import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.nodeapi.internal.ServiceType
import java.security.PublicKey import java.security.PublicKey
class NetworkIdentityModel { class NetworkIdentityModel {
@ -36,16 +36,10 @@ class NetworkIdentityModel {
.build<PublicKey, ObservableValue<NodeInfo?>>(CacheLoader.from { publicKey -> .build<PublicKey, ObservableValue<NodeInfo?>>(CacheLoader.from { publicKey ->
publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } } publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } }
}) })
val notaries = ChosenList(rpcProxy.map { FXCollections.observableList(it?.notaryIdentities() ?: emptyList()) })
val notaries: ObservableList<Party> = networkIdentities.map {
it.legalIdentitiesAndCerts.find { it.name.commonName?.let { ServiceType.parse(it).isNotary() } == true }
}.map { it?.party }.filterNotNull()
val notaryNodes: ObservableList<NodeInfo> = notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }.filterNotNull() val notaryNodes: ObservableList<NodeInfo> = notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }.filterNotNull()
val parties: ObservableList<NodeInfo> = networkIdentities val parties: ObservableList<NodeInfo> = networkIdentities
.filtered { it.legalIdentities.all { it !in notaries } } .filtered { it.legalIdentities.all { it !in notaries } }
// TODO: REMOVE THIS HACK WHEN NETWORK MAP REDESIGN WORK IS COMPLETED.
.filtered { it.legalIdentities.all { it.name.organisation != "Network Map Service" } }
val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party } val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party }
fun partyFromPublicKey(publicKey: PublicKey): ObservableValue<NodeInfo?> = identityCache[publicKey] fun partyFromPublicKey(publicKey: PublicKey): ObservableValue<NodeInfo?> = identityCache[publicKey]

View File

@ -26,7 +26,8 @@ import static java.util.Objects.requireNonNull;
import static kotlin.test.AssertionsKt.assertEquals; import static kotlin.test.AssertionsKt.assertEquals;
import static net.corda.finance.Currencies.DOLLARS; import static net.corda.finance.Currencies.DOLLARS;
import static net.corda.finance.contracts.GetBalances.getCashBalance; import static net.corda.finance.contracts.GetBalances.getCashBalance;
import static net.corda.node.services.FlowPermissions.startFlowPermission; import static net.corda.node.services.Permissions.invokeRpc;
import static net.corda.node.services.Permissions.startFlow;
import static net.corda.testing.TestConstants.getALICE; import static net.corda.testing.TestConstants.getALICE;
public class CordaRPCJavaClientTest extends NodeBasedTest { public class CordaRPCJavaClientTest extends NodeBasedTest {
@ -34,7 +35,12 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
super(Arrays.asList("net.corda.finance.contracts", CashSchemaV1.class.getPackage().getName())); super(Arrays.asList("net.corda.finance.contracts", CashSchemaV1.class.getPackage().getName()));
} }
private List<String> perms = Arrays.asList(startFlowPermission(CashPaymentFlow.class), startFlowPermission(CashIssueFlow.class)); private List<String> perms = Arrays.asList(
startFlow(CashPaymentFlow.class),
startFlow(CashIssueFlow.class),
invokeRpc("nodeInfo"),
invokeRpc("vaultQueryBy"),
invokeRpc("vaultQueryByCriteria"));
private Set<String> permSet = new HashSet<>(perms); private Set<String> permSet = new HashSet<>(perms);
private User rpcUser = new User("user1", "test", permSet); private User rpcUser = new User("user1", "test", permSet);

View File

@ -4,10 +4,7 @@ import net.corda.core.crypto.random63BitValue
import net.corda.core.flows.FlowInitiator import net.corda.core.flows.FlowInitiator
import net.corda.core.internal.concurrent.flatMap import net.corda.core.internal.concurrent.flatMap
import net.corda.core.internal.packageName import net.corda.core.internal.packageName
import net.corda.core.messaging.FlowProgressHandle import net.corda.core.messaging.*
import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.messaging.startFlow
import net.corda.core.messaging.startTrackedFlow
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
@ -20,7 +17,8 @@ import net.corda.finance.flows.CashPaymentFlow
import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.CashSchemaV1
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
@ -36,9 +34,12 @@ import kotlin.test.assertTrue
class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", CashSchemaV1::class.packageName)) { class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", CashSchemaV1::class.packageName)) {
private val rpcUser = User("user1", "test", permissions = setOf( private val rpcUser = User("user1", "test", permissions = setOf(
startFlowPermission<CashIssueFlow>(), startFlow<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>() startFlow<CashPaymentFlow>(),
)) invokeRpc("vaultQueryBy"),
invokeRpc(CordaRPCOps::stateMachinesFeed),
invokeRpc("vaultQueryByCriteria"))
)
private lateinit var node: StartedNode<Node> private lateinit var node: StartedNode<Node>
private lateinit var client: CordaRPCClient private lateinit var client: CordaRPCClient
private var connection: CordaRPCConnection? = null private var connection: CordaRPCConnection? = null

View File

@ -7,6 +7,7 @@ import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.internal.serialization.AMQP_RPC_CLIENT_CONTEXT
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
import java.time.Duration import java.time.Duration

View File

@ -5,6 +5,11 @@ import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.SerializationDefaults
import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.ByteSequence
import net.corda.nodeapi.internal.serialization.* import net.corda.nodeapi.internal.serialization.*
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
import net.corda.nodeapi.internal.serialization.kryo.RPCKryo
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
class KryoClientSerializationScheme : AbstractKryoSerializationScheme() { class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {

View File

@ -6,7 +6,7 @@ import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.concurrent.thenMatch import net.corda.core.internal.concurrent.thenMatch
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.services.messaging.getRpcContext import net.corda.node.services.messaging.rpcContext
import net.corda.testing.RPCDriverExposedDSLInterface import net.corda.testing.RPCDriverExposedDSLInterface
import net.corda.testing.rpcDriver import net.corda.testing.rpcDriver
import net.corda.testing.rpcTestUser import net.corda.testing.rpcTestUser
@ -65,7 +65,7 @@ class ClientRPCInfrastructureTests : AbstractRPCTest() {
override fun makeComplicatedObservable() = complicatedObservable override fun makeComplicatedObservable() = complicatedObservable
override fun makeComplicatedListenableFuture() = complicatedListenableFuturee override fun makeComplicatedListenableFuture() = complicatedListenableFuturee
override fun addedLater(): Unit = throw IllegalStateException() override fun addedLater(): Unit = throw IllegalStateException()
override fun captureUser(): String = getRpcContext().currentUser.username override fun captureUser(): String = rpcContext().currentUser.username
} }
@Test @Test

View File

@ -1,7 +1,9 @@
package net.corda.client.rpc package net.corda.client.rpc
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.node.services.messaging.getRpcContext import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.messaging.rpcContext
import net.corda.node.services.messaging.requirePermission import net.corda.node.services.messaging.requirePermission
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.RPCDriverExposedDSLInterface import net.corda.testing.RPCDriverExposedDSLInterface
@ -9,6 +11,8 @@ import net.corda.testing.rpcDriver
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized
import kotlin.reflect.KVisibility
import kotlin.reflect.full.declaredMemberFunctions
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
@RunWith(Parameterized::class) @RunWith(Parameterized::class)
@ -28,7 +32,7 @@ class RPCPermissionsTests : AbstractRPCTest() {
class TestOpsImpl : TestOps { class TestOpsImpl : TestOps {
override val protocolVersion = 1 override val protocolVersion = 1
override fun validatePermission(str: String) = getRpcContext().requirePermission(str) override fun validatePermission(str: String) { rpcContext().requirePermission(str) }
} }
/** /**
@ -89,4 +93,19 @@ class RPCPermissionsTests : AbstractRPCTest() {
} }
} }
@Test
fun `fine grained permissions are enforced`() {
val allPermissions = CordaRPCOps::class.declaredMemberFunctions.filter { it.visibility == KVisibility.PUBLIC }.map { invokeRpc(it) }
allPermissions.forEach { permission ->
rpcDriver {
val user = userOf("Mark", setOf(permission))
val proxy = testProxyFor(user)
proxy.validatePermission(permission)
(allPermissions - permission).forEach { notOwnedPermission ->
assertFailsWith(PermissionException::class, { proxy.validatePermission(notOwnedPermission) })
}
}
}
}
} }

View File

@ -14,8 +14,11 @@ import net.corda.finance.DOLLARS
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.testing.* import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB_NAME
import net.corda.testing.CHARLIE_NAME
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.singleIdentity
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -24,12 +27,16 @@ import kotlin.test.assertNotNull
import kotlin.test.assertNull import kotlin.test.assertNull
class IdentitySyncFlowTests { class IdentitySyncFlowTests {
lateinit var mockNet: MockNetwork private lateinit var mockNet: MockNetwork
@Before @Before
fun before() { fun before() {
// We run this in parallel threads to help catch any race conditions that may exist. // We run this in parallel threads to help catch any race conditions that may exist.
mockNet = MockNetwork(networkSendManuallyPumped = false, threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset")) mockNet = MockNetwork(
networkSendManuallyPumped = false,
threadPerNode = true,
cordappPackages = listOf("net.corda.finance.contracts.asset")
)
} }
@After @After
@ -40,12 +47,11 @@ class IdentitySyncFlowTests {
@Test @Test
fun `sync confidential identities`() { fun `sync confidential identities`() {
// Set up values we'll need // Set up values we'll need
val notaryNode = mockNet.createNotaryNode()
val aliceNode = mockNet.createPartyNode(ALICE_NAME) val aliceNode = mockNet.createPartyNode(ALICE_NAME)
val bobNode = mockNet.createPartyNode(BOB_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME)
val alice: Party = aliceNode.info.singleIdentity() val alice: Party = aliceNode.info.singleIdentity()
val bob: Party = bobNode.info.singleIdentity() val bob: Party = bobNode.info.singleIdentity()
val notary = notaryNode.services.getDefaultNotary() val notary = mockNet.defaultNotaryIdentity
bobNode.internals.registerInitiatedFlow(Receive::class.java) bobNode.internals.registerInitiatedFlow(Receive::class.java)
// Alice issues then pays some cash to a new confidential identity that Bob doesn't know about // Alice issues then pays some cash to a new confidential identity that Bob doesn't know about
@ -70,14 +76,13 @@ class IdentitySyncFlowTests {
@Test @Test
fun `don't offer other's identities confidential identities`() { fun `don't offer other's identities confidential identities`() {
// Set up values we'll need // Set up values we'll need
val notaryNode = mockNet.createNotaryNode()
val aliceNode = mockNet.createPartyNode(ALICE_NAME) val aliceNode = mockNet.createPartyNode(ALICE_NAME)
val bobNode = mockNet.createPartyNode(BOB_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME)
val charlieNode = mockNet.createPartyNode(CHARLIE_NAME) val charlieNode = mockNet.createPartyNode(CHARLIE_NAME)
val alice: Party = aliceNode.info.singleIdentity() val alice: Party = aliceNode.info.singleIdentity()
val bob: Party = bobNode.info.singleIdentity() val bob: Party = bobNode.info.singleIdentity()
val charlie: Party = charlieNode.info.singleIdentity() val charlie: Party = charlieNode.info.singleIdentity()
val notary = notaryNode.services.getDefaultNotary() val notary = mockNet.defaultNotaryIdentity
bobNode.internals.registerInitiatedFlow(Receive::class.java) bobNode.internals.registerInitiatedFlow(Receive::class.java)
// Charlie issues then pays some cash to a new confidential identity // Charlie issues then pays some cash to a new confidential identity
@ -105,7 +110,7 @@ class IdentitySyncFlowTests {
* Very lightweight wrapping flow to trigger the counterparty flow that receives the identities. * Very lightweight wrapping flow to trigger the counterparty flow that receives the identities.
*/ */
@InitiatingFlow @InitiatingFlow
class Initiator(val otherSide: Party, val tx: WireTransaction): FlowLogic<Boolean>() { class Initiator(private val otherSide: Party, private val tx: WireTransaction): FlowLogic<Boolean>() {
@Suspendable @Suspendable
override fun call(): Boolean { override fun call(): Boolean {
val session = initiateFlow(otherSide) val session = initiateFlow(otherSide)
@ -116,7 +121,7 @@ class IdentitySyncFlowTests {
} }
@InitiatedBy(IdentitySyncFlowTests.Initiator::class) @InitiatedBy(IdentitySyncFlowTests.Initiator::class)
class Receive(val otherSideSession: FlowSession): FlowLogic<Unit>() { class Receive(private val otherSideSession: FlowSession): FlowLogic<Unit>() {
@Suspendable @Suspendable
override fun call() { override fun call() {
subFlow(IdentitySyncFlow.Receive(otherSideSession)) subFlow(IdentitySyncFlow.Receive(otherSideSession))

View File

@ -6,17 +6,22 @@ import net.corda.core.identity.Party
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import org.junit.Before
import org.junit.Test import org.junit.Test
import kotlin.test.* import kotlin.test.*
class SwapIdentitiesFlowTests { class SwapIdentitiesFlowTests {
private lateinit var mockNet: MockNetwork
@Before
fun setup() {
// We run this in parallel threads to help catch any race conditions that may exist.
mockNet = MockNetwork(networkSendManuallyPumped = false, threadPerNode = true)
}
@Test @Test
fun `issue key`() { fun `issue key`() {
// We run this in parallel threads to help catch any race conditions that may exist.
val mockNet = MockNetwork(threadPerNode = true)
// Set up values we'll need // Set up values we'll need
mockNet.createNotaryNode()
val aliceNode = mockNet.createPartyNode(ALICE.name) val aliceNode = mockNet.createPartyNode(ALICE.name)
val bobNode = mockNet.createPartyNode(BOB.name) val bobNode = mockNet.createPartyNode(BOB.name)
val alice = aliceNode.info.singleIdentity() val alice = aliceNode.info.singleIdentity()
@ -52,19 +57,16 @@ class SwapIdentitiesFlowTests {
*/ */
@Test @Test
fun `verifies identity name`() { fun `verifies identity name`() {
// We run this in parallel threads to help catch any race conditions that may exist.
val mockNet = MockNetwork(threadPerNode = true)
// Set up values we'll need // Set up values we'll need
val notaryNode = mockNet.createNotaryNode()
val aliceNode = mockNet.createPartyNode(ALICE.name) val aliceNode = mockNet.createPartyNode(ALICE.name)
val bobNode = mockNet.createPartyNode(BOB.name) val bobNode = mockNet.createPartyNode(BOB.name)
val charlieNode = mockNet.createPartyNode(CHARLIE.name)
val bob: Party = bobNode.services.myInfo.singleIdentity() val bob: Party = bobNode.services.myInfo.singleIdentity()
val notBob = notaryNode.database.transaction { val notBob = charlieNode.database.transaction {
notaryNode.services.keyManagementService.freshKeyAndCert(notaryNode.services.myInfo.chooseIdentityAndCert(), false) charlieNode.services.keyManagementService.freshKeyAndCert(charlieNode.services.myInfo.chooseIdentityAndCert(), false)
} }
val sigData = SwapIdentitiesFlow.buildDataToSign(notBob) val sigData = SwapIdentitiesFlow.buildDataToSign(notBob)
val signature = notaryNode.services.keyManagementService.sign(sigData, notBob.owningKey) val signature = charlieNode.services.keyManagementService.sign(sigData, notBob.owningKey)
assertFailsWith<SwapIdentitiesException>("Certificate subject must match counterparty's well known identity.") { assertFailsWith<SwapIdentitiesException>("Certificate subject must match counterparty's well known identity.") {
SwapIdentitiesFlow.validateAndRegisterIdentity(aliceNode.services.identityService, bob, notBob, signature.withoutKey()) SwapIdentitiesFlow.validateAndRegisterIdentity(aliceNode.services.identityService, bob, notBob, signature.withoutKey())
} }
@ -77,11 +79,8 @@ class SwapIdentitiesFlowTests {
*/ */
@Test @Test
fun `verifies signature`() { fun `verifies signature`() {
// We run this in parallel threads to help catch any race conditions that may exist.
val mockNet = MockNetwork(threadPerNode = true)
// Set up values we'll need // Set up values we'll need
val notaryNode = mockNet.createNotaryNode() val notaryNode = mockNet.defaultNotaryNode
val aliceNode = mockNet.createPartyNode(ALICE.name) val aliceNode = mockNet.createPartyNode(ALICE.name)
val bobNode = mockNet.createPartyNode(BOB.name) val bobNode = mockNet.createPartyNode(BOB.name)
val bob: Party = bobNode.services.myInfo.singleIdentity() val bob: Party = bobNode.services.myInfo.singleIdentity()

View File

@ -4,8 +4,4 @@ trustStorePassword : "trustpass"
p2pAddress : "localhost:10005" p2pAddress : "localhost:10005"
rpcAddress : "localhost:10006" rpcAddress : "localhost:10006"
webAddress : "localhost:10007" webAddress : "localhost:10007"
networkMapService : {
address : "localhost:10000"
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
}
useHTTPS : false useHTTPS : false

View File

@ -1,5 +1,5 @@
gradlePluginsVersion=2.0.6 gradlePluginsVersion=2.0.8
kotlinVersion=1.1.50 kotlinVersion=1.1.50
guavaVersion=21.0 guavaVersion=21.0
bouncycastleVersion=1.57 bouncycastleVersion=1.57
typesafeConfigVersion=1.3.1 typesafeConfigVersion=1.3.1

View File

@ -7,6 +7,52 @@ apply plugin: 'com.jfrog.artifactory'
description 'Corda core' description 'Corda core'
evaluationDependsOn(':node:capsule')
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
smokeTestCompile.extendsFrom compile
smokeTestRuntime.extendsFrom runtime
}
sourceSets {
integrationTest {
kotlin {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/kotlin')
}
java {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/java')
}
}
smokeTest {
kotlin {
// We must NOT have any Node code on the classpath, so do NOT
// include the test or integrationTest dependencies here.
compileClasspath += main.output
runtimeClasspath += main.output
srcDir file('src/smoke-test/kotlin')
}
java {
compileClasspath += main.output
runtimeClasspath += main.output
srcDir file('src/smoke-test/java')
}
}
}
processSmokeTestResources {
// Bring in the fully built corda.jar for use by NodeFactory in the smoke tests
from(project(':node:capsule').tasks['buildCordaJAR']) {
rename 'corda-(.*)', 'corda.jar'
}
}
buildscript { buildscript {
repositories { repositories {
mavenCentral() mavenCentral()
@ -14,7 +60,6 @@ buildscript {
} }
dependencies { dependencies {
testCompile "junit:junit:$junit_version" testCompile "junit:junit:$junit_version"
testCompile "commons-fileupload:commons-fileupload:$fileupload_version" testCompile "commons-fileupload:commons-fileupload:$fileupload_version"
@ -48,6 +93,11 @@ dependencies {
// Guava: Google utilities library. // Guava: Google utilities library.
testCompile "com.google.guava:guava:$guava_version" testCompile "com.google.guava:guava:$guava_version"
// Smoke tests do NOT have any Node code on the classpath!
smokeTestCompile project(':smoke-test-utils')
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
smokeTestCompile "junit:junit:$junit_version"
// RxJava: observable streams of events. // RxJava: observable streams of events.
compile "io.reactivex:rxjava:$rxjava_version" compile "io.reactivex:rxjava:$rxjava_version"
@ -87,6 +137,22 @@ task testJar(type: Jar) {
from sourceSets.test.output from sourceSets.test.output
} }
task integrationTest(type: Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
task smokeTestJar(type: Jar) {
classifier 'smokeTests'
from sourceSets.smokeTest.output
}
task smokeTest(type: Test) {
dependsOn smokeTestJar
testClassesDirs = sourceSets.smokeTest.output.classesDirs
classpath = sourceSets.smokeTest.runtimeClasspath
}
artifacts { artifacts {
testArtifacts testJar testArtifacts testJar
} }

View File

@ -13,7 +13,7 @@ interface CordaFuture<V> : Future<V> {
* If the completion thread is problematic for you e.g. deadlock, you can submit to an executor manually. * If the completion thread is problematic for you e.g. deadlock, you can submit to an executor manually.
* If callback fails, its throwable is logged. * If callback fails, its throwable is logged.
*/ */
fun <W> then(callback: (CordaFuture<V>) -> W): Unit fun <W> then(callback: (CordaFuture<V>) -> W)
/** /**
* @return a new [CompletableFuture] with the same outcome as this Future. * @return a new [CompletableFuture] with the same outcome as this Future.

View File

@ -34,7 +34,9 @@ fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey) = DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes) fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
return DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes)
}
/** /**
* Helper function to sign with a key pair. * Helper function to sign with a key pair.
@ -45,11 +47,11 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey) = DigitalSigna
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun KeyPair.sign(bytesToSign: ByteArray) = private.sign(bytesToSign, public) fun KeyPair.sign(bytesToSign: ByteArray): DigitalSignature.WithKey = private.sign(bytesToSign, public)
/** Helper function to sign the bytes of [bytesToSign] with a key pair. */ /** Helper function to sign the bytes of [bytesToSign] with a key pair. */
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun KeyPair.sign(bytesToSign: OpaqueBytes) = sign(bytesToSign.bytes) fun KeyPair.sign(bytesToSign: OpaqueBytes): DigitalSignature.WithKey = sign(bytesToSign.bytes)
/** /**
* Helper function for signing a [SignableData] object. * Helper function for signing a [SignableData] object.

View File

@ -49,6 +49,7 @@ class NotaryFlow {
progressTracker.currentStep = REQUESTING progressTracker.currentStep = REQUESTING
val notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary") val notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary")
check(serviceHub.networkMapCache.isNotary(notaryParty)) { "$notaryParty is not a notary on the network" }
check(stx.inputs.all { stateRef -> serviceHub.loadState(stateRef).notary == notaryParty }) { check(stx.inputs.all { stateRef -> serviceHub.loadState(stateRef).notary == notaryParty }) {
"Input states must have the same Notary" "Input states must have the same Notary"
} }
@ -115,6 +116,9 @@ class NotaryFlow {
@Suspendable @Suspendable
override fun call(): Void? { override fun call(): Void? {
check(serviceHub.myInfo.legalIdentities.any { serviceHub.networkMapCache.isNotary(it) }) {
"We are not a notary on the network"
}
val (id, inputs, timeWindow, notary) = receiveAndVerifyTx() val (id, inputs, timeWindow, notary) = receiveAndVerifyTx()
checkNotary(notary) checkNotary(notary)
service.validateTimeWindow(timeWindow) service.validateTimeWindow(timeWindow)
@ -135,7 +139,7 @@ class NotaryFlow {
protected fun checkNotary(notary: Party?) { protected fun checkNotary(notary: Party?) {
// TODO This check implies that it's OK to use the node's main identity. Shouldn't it be just limited to the // TODO This check implies that it's OK to use the node's main identity. Shouldn't it be just limited to the
// notary identities? // notary identities?
if (notary !in serviceHub.myInfo.legalIdentities) { if (notary == null || !serviceHub.myInfo.isLegalIdentity(notary)) {
throw NotaryException(NotaryError.WrongNotary) throw NotaryException(NotaryError.WrongNotary)
} }
} }

View File

@ -1,8 +1,9 @@
package net.corda.core.schemas package net.corda.core.internal.schemas
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize

View File

@ -100,21 +100,13 @@ interface CordaRPCOps : RPCOps {
// Java Helpers // Java Helpers
// DOCSTART VaultQueryAPIHelpers // DOCSTART VaultQueryAPIHelpers
fun <T : ContractState> vaultQuery(contractStateType: Class<out T>): Vault.Page<T> { fun <T : ContractState> vaultQuery(contractStateType: Class<out T>): Vault.Page<T>
return vaultQueryBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType)
}
fun <T : ContractState> vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class<out T>): Vault.Page<T> { fun <T : ContractState> vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class<out T>): Vault.Page<T>
return vaultQueryBy(criteria, PageSpecification(), Sort(emptySet()), contractStateType)
}
fun <T : ContractState> vaultQueryByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): Vault.Page<T> { fun <T : ContractState> vaultQueryByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): Vault.Page<T>
return vaultQueryBy(criteria, paging, Sort(emptySet()), contractStateType)
}
fun <T : ContractState> vaultQueryByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T> { fun <T : ContractState> vaultQueryByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T>
return vaultQueryBy(criteria, PageSpecification(), sorting, contractStateType)
}
// DOCEND VaultQueryAPIHelpers // DOCEND VaultQueryAPIHelpers
/** /**
@ -141,21 +133,13 @@ interface CordaRPCOps : RPCOps {
// Java Helpers // Java Helpers
// DOCSTART VaultTrackAPIHelpers // DOCSTART VaultTrackAPIHelpers
fun <T : ContractState> vaultTrack(contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>> { fun <T : ContractState> vaultTrack(contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>>
return vaultTrackBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType)
}
fun <T : ContractState> vaultTrackByCriteria(contractStateType: Class<out T>, criteria: QueryCriteria): DataFeed<Vault.Page<T>, Vault.Update<T>> { fun <T : ContractState> vaultTrackByCriteria(contractStateType: Class<out T>, criteria: QueryCriteria): DataFeed<Vault.Page<T>, Vault.Update<T>>
return vaultTrackBy(criteria, PageSpecification(), Sort(emptySet()), contractStateType)
}
fun <T : ContractState> vaultTrackByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): DataFeed<Vault.Page<T>, Vault.Update<T>> { fun <T : ContractState> vaultTrackByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): DataFeed<Vault.Page<T>, Vault.Update<T>>
return vaultTrackBy(criteria, paging, Sort(emptySet()), contractStateType)
}
fun <T : ContractState> vaultTrackByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): DataFeed<Vault.Page<T>, Vault.Update<T>> { fun <T : ContractState> vaultTrackByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): DataFeed<Vault.Page<T>, Vault.Update<T>>
return vaultTrackBy(criteria, PageSpecification(), sorting, contractStateType)
}
// DOCEND VaultTrackAPIHelpers // DOCEND VaultTrackAPIHelpers
/** /**

View File

@ -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)

View File

@ -52,7 +52,6 @@ interface NetworkMapCacheBase {
* *
* Note that the identities are sorted based on legal name, and the ordering might change once new notaries are introduced. * Note that the identities are sorted based on legal name, and the ordering might change once new notaries are introduced.
*/ */
// TODO this list will be taken from NetworkParameters distributed by NetworkMap.
val notaryIdentities: List<Party> val notaryIdentities: List<Party>
// DOCEND 1 // DOCEND 1
@ -116,12 +115,15 @@ interface NetworkMapCacheBase {
fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name } fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name }
// DOCEND 2 // DOCEND 2
/** Checks whether a given party is an advertised notary identity. */ /** Returns true if and only if the given [Party] is a notary, which is defined by the network parameters. */
fun isNotary(party: Party): Boolean = party in notaryIdentities fun isNotary(party: Party): Boolean = party in notaryIdentities
/** Checks whether a given party is an validating notary identity. */ /**
// TODO This implementation will change after introducing of NetworkParameters. * Returns true if and only if the given [Party] is validating notary. For every party that is a validating notary,
fun isValidatingNotary(party: Party): Boolean = isNotary(party) && "validating" in party.name.commonName!! * [isNotary] is also true.
* @see isNotary
*/
fun isValidatingNotary(party: Party): Boolean
/** Clear all network map data from local node cache. */ /** Clear all network map data from local node cache. */
fun clearNetworkMapCache() fun clearNetworkMapCache()

View File

@ -108,7 +108,7 @@ abstract class TraversableTransaction(open val componentGroups: List<ComponentGr
* @param groupHashes the roots of the transaction component groups. * @param groupHashes the roots of the transaction component groups.
*/ */
@CordaSerializable @CordaSerializable
class FilteredTransaction private constructor( class FilteredTransaction internal constructor(
override val id: SecureHash, override val id: SecureHash,
val filteredComponentGroups: List<FilteredComponentGroup>, val filteredComponentGroups: List<FilteredComponentGroup>,
val groupHashes: List<SecureHash> val groupHashes: List<SecureHash>

View File

@ -1,4 +1,4 @@
package net.corda.node package net.corda.core.cordapp
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.* import net.corda.core.flows.*
@ -11,7 +11,6 @@ import net.corda.core.internal.list
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.node.internal.cordapp.CordappLoader
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.smoketesting.NodeConfig import net.corda.smoketesting.NodeConfig
import net.corda.smoketesting.NodeProcess import net.corda.smoketesting.NodeProcess
@ -23,6 +22,7 @@ import kotlin.streams.toList
class CordappSmokeTest { class CordappSmokeTest {
private companion object { private companion object {
private const val CORDAPPS_DIR_NAME = "cordapps"
val user = User("user1", "test", permissions = setOf("ALL")) val user = User("user1", "test", permissions = setOf("ALL"))
val port = AtomicInteger(15100) val port = AtomicInteger(15100)
} }
@ -38,9 +38,10 @@ class CordappSmokeTest {
users = listOf(user) users = listOf(user)
) )
@Test @Test
fun `FlowContent appName returns the filename of the CorDapp jar`() { fun `FlowContent appName returns the filename of the CorDapp jar`() {
val cordappsDir = (factory.baseDirectory(aliceConfig) / CordappLoader.CORDAPPS_DIR_NAME).createDirectories() val cordappsDir = (factory.baseDirectory(aliceConfig) / CORDAPPS_DIR_NAME).createDirectories()
// Find the jar file for the smoke tests of this module // Find the jar file for the smoke tests of this module
val selfCordapp = Paths.get("build", "libs").list { val selfCordapp = Paths.get("build", "libs").list {
it.filter { "-smokeTests" in it.toString() }.toList().single() it.filter { "-smokeTests" in it.toString() }.toList().single()
@ -61,7 +62,7 @@ class CordappSmokeTest {
@Test @Test
fun `empty cordapps directory`() { fun `empty cordapps directory`() {
(factory.baseDirectory(aliceConfig) / CordappLoader.CORDAPPS_DIR_NAME).createDirectories() (factory.baseDirectory(aliceConfig) / CORDAPPS_DIR_NAME).createDirectories()
factory.create(aliceConfig).close() factory.create(aliceConfig).close()
} }

View File

@ -18,14 +18,12 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
public class FlowsInJavaTest { public class FlowsInJavaTest {
private final MockNetwork mockNet = new MockNetwork(); private final MockNetwork mockNet = new MockNetwork();
private StartedNode<MockNetwork.MockNode> aliceNode; private StartedNode<MockNetwork.MockNode> aliceNode;
private StartedNode<MockNetwork.MockNode> bobNode; private StartedNode<MockNetwork.MockNode> bobNode;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
mockNet.createNotaryNode();
aliceNode = mockNet.createPartyNode(TestConstants.getALICE().getName()); aliceNode = mockNet.createPartyNode(TestConstants.getALICE().getName());
bobNode = mockNet.createPartyNode(TestConstants.getBOB().getName()); bobNode = mockNet.createPartyNode(TestConstants.getBOB().getName());
mockNet.runNetwork(); mockNet.runNetwork();

View File

@ -14,7 +14,6 @@ import net.corda.testing.ALICE
import net.corda.testing.ALICE_NAME import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNodeArgs
import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.MockNodeParameters
import net.corda.testing.singleIdentity import net.corda.testing.singleIdentity
import org.junit.After import org.junit.After
@ -109,15 +108,13 @@ class AttachmentTests {
} }
@Test @Test
fun `malicious response`() { fun maliciousResponse() {
// Make a node that doesn't do sanity checking at load time. // Make a node that doesn't do sanity checking at load time.
val aliceNode = mockNet.createNotaryNode(MockNodeParameters(legalName = ALICE.name), nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> { val aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE.name), nodeFactory = { args ->
override fun create(args: MockNodeArgs): MockNetwork.MockNode { object : MockNetwork.MockNode(args) {
return object : MockNetwork.MockNode(args) { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
}
} }
}, validating = false) })
val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name)) val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name))
mockNet.runNetwork() mockNet.runNetwork()
val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME) val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME)

View File

@ -27,19 +27,18 @@ class CollectSignaturesFlowTests {
private val cordappPackages = listOf("net.corda.testing.contracts") private val cordappPackages = listOf("net.corda.testing.contracts")
} }
lateinit var mockNet: MockNetwork private lateinit var mockNet: MockNetwork
lateinit var aliceNode: StartedNode<MockNetwork.MockNode> private lateinit var aliceNode: StartedNode<MockNetwork.MockNode>
lateinit var bobNode: StartedNode<MockNetwork.MockNode> private lateinit var bobNode: StartedNode<MockNetwork.MockNode>
lateinit var charlieNode: StartedNode<MockNetwork.MockNode> private lateinit var charlieNode: StartedNode<MockNetwork.MockNode>
lateinit var alice: Party private lateinit var alice: Party
lateinit var bob: Party private lateinit var bob: Party
lateinit var charlie: Party private lateinit var charlie: Party
lateinit var notary: Party private lateinit var notary: Party
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork(cordappPackages = cordappPackages) mockNet = MockNetwork(cordappPackages = cordappPackages)
val notaryNode = mockNet.createNotaryNode()
aliceNode = mockNet.createPartyNode(ALICE.name) aliceNode = mockNet.createPartyNode(ALICE.name)
bobNode = mockNet.createPartyNode(BOB.name) bobNode = mockNet.createPartyNode(BOB.name)
charlieNode = mockNet.createPartyNode(CHARLIE.name) charlieNode = mockNet.createPartyNode(CHARLIE.name)
@ -47,7 +46,7 @@ class CollectSignaturesFlowTests {
alice = aliceNode.info.singleIdentity() alice = aliceNode.info.singleIdentity()
bob = bobNode.info.singleIdentity() bob = bobNode.info.singleIdentity()
charlie = charlieNode.info.singleIdentity() charlie = charlieNode.info.singleIdentity()
notary = notaryNode.services.getDefaultNotary() notary = mockNet.defaultNotaryIdentity
} }
@After @After

View File

@ -16,9 +16,9 @@ import net.corda.finance.USD
import net.corda.finance.`issued by` import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.node.internal.CordaRPCOpsImpl import net.corda.node.internal.SecureCordaRPCOps
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
@ -41,14 +41,13 @@ class ContractUpgradeFlowTest {
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows")) mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows"))
val notaryNode = mockNet.createNotaryNode()
aliceNode = mockNet.createPartyNode(ALICE.name) aliceNode = mockNet.createPartyNode(ALICE.name)
bobNode = mockNet.createPartyNode(BOB.name) bobNode = mockNet.createPartyNode(BOB.name)
// Process registration // Process registration
mockNet.runNetwork() mockNet.runNetwork()
notary = notaryNode.services.getDefaultNotary() notary = mockNet.defaultNotaryIdentity
} }
@After @After
@ -118,7 +117,7 @@ class ContractUpgradeFlowTest {
return startRpcClient<CordaRPCOps>( return startRpcClient<CordaRPCOps>(
rpcAddress = startRpcServer( rpcAddress = startRpcServer(
rpcUser = user, rpcUser = user,
ops = CordaRPCOpsImpl(node.services, node.smm, node.database, node.services) ops = SecureCordaRPCOps(node.services, node.smm, node.database, node.services)
).get().broker.hostAndPort!!, ).get().broker.hostAndPort!!,
username = user.username, username = user.username,
password = user.password password = user.password
@ -134,10 +133,10 @@ class ContractUpgradeFlowTest {
val stx = bobNode.services.addSignature(signedByA) val stx = bobNode.services.addSignature(signedByA)
val user = rpcTestUser.copy(permissions = setOf( val user = rpcTestUser.copy(permissions = setOf(
startFlowPermission<FinalityInvoker>(), startFlow<FinalityInvoker>(),
startFlowPermission<ContractUpgradeFlow.Initiate<*, *>>(), startFlow<ContractUpgradeFlow.Initiate<*, *>>(),
startFlowPermission<ContractUpgradeFlow.Authorise>(), startFlow<ContractUpgradeFlow.Authorise>(),
startFlowPermission<ContractUpgradeFlow.Deauthorise>() startFlow<ContractUpgradeFlow.Deauthorise>()
)) ))
val rpcA = startProxy(aliceNode, user) val rpcA = startProxy(aliceNode, user)
val rpcB = startProxy(bobNode, user) val rpcB = startProxy(bobNode, user)

View File

@ -26,7 +26,6 @@ class FinalityFlowTests {
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset")) mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
val notaryNode = mockNet.createNotaryNode()
val aliceNode = mockNet.createPartyNode(ALICE_NAME) val aliceNode = mockNet.createPartyNode(ALICE_NAME)
val bobNode = mockNet.createPartyNode(BOB_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME)
mockNet.runNetwork() mockNet.runNetwork()
@ -34,7 +33,7 @@ class FinalityFlowTests {
bobServices = bobNode.services bobServices = bobNode.services
alice = aliceNode.info.singleIdentity() alice = aliceNode.info.singleIdentity()
bob = bobNode.info.singleIdentity() bob = bobNode.info.singleIdentity()
notary = notaryNode.services.getDefaultNotary() notary = aliceServices.getDefaultNotary()
} }
@After @After

View File

@ -13,7 +13,7 @@ import org.junit.Test
class ReceiveMultipleFlowTests { class ReceiveMultipleFlowTests {
@Test @Test
fun `receive all messages in parallel using map style`() { fun `receive all messages in parallel using map style`() {
network(3) { nodes, _ -> network(3) { nodes ->
val doubleValue = 5.0 val doubleValue = 5.0
nodes[1].registerAnswer(AlgorithmDefinition::class, doubleValue) nodes[1].registerAnswer(AlgorithmDefinition::class, doubleValue)
val stringValue = "Thriller" val stringValue = "Thriller"
@ -30,7 +30,7 @@ class ReceiveMultipleFlowTests {
@Test @Test
fun `receive all messages in parallel using list style`() { fun `receive all messages in parallel using list style`() {
network(3) { nodes, _ -> network(3) { nodes ->
val value1 = 5.0 val value1 = 5.0
nodes[1].registerAnswer(ParallelAlgorithmList::class, value1) nodes[1].registerAnswer(ParallelAlgorithmList::class, value1)
val value2 = 6.0 val value2 = 6.0

View File

@ -8,7 +8,9 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.sequence import net.corda.core.utilities.sequence
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.* import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP
import net.corda.testing.chooseIdentity
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.getDefaultNotary import net.corda.testing.getDefaultNotary
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
@ -26,18 +28,18 @@ import kotlin.test.assertNull
// DOCSTART 3 // DOCSTART 3
class ResolveTransactionsFlowTest { class ResolveTransactionsFlowTest {
lateinit var mockNet: MockNetwork private lateinit var mockNet: MockNetwork
lateinit var notaryNode: StartedNode<MockNetwork.MockNode> private lateinit var notaryNode: StartedNode<MockNetwork.MockNode>
lateinit var megaCorpNode: StartedNode<MockNetwork.MockNode> private lateinit var megaCorpNode: StartedNode<MockNetwork.MockNode>
lateinit var miniCorpNode: StartedNode<MockNetwork.MockNode> private lateinit var miniCorpNode: StartedNode<MockNetwork.MockNode>
lateinit var megaCorp: Party private lateinit var megaCorp: Party
lateinit var miniCorp: Party private lateinit var miniCorp: Party
lateinit var notary: Party private lateinit var notary: Party
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
notaryNode = mockNet.createNotaryNode() notaryNode = mockNet.defaultNotaryNode
megaCorpNode = mockNet.createPartyNode(MEGA_CORP.name) megaCorpNode = mockNet.createPartyNode(MEGA_CORP.name)
miniCorpNode = mockNet.createPartyNode(MINI_CORP.name) miniCorpNode = mockNet.createPartyNode(MINI_CORP.name)
megaCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java) megaCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java)

View File

@ -17,7 +17,6 @@ import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.node.utilities.currentDBSession import net.corda.node.utilities.currentDBSession
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNodeArgs
import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.MockNodeParameters
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -156,11 +155,9 @@ class AttachmentSerializationTest {
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String { private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
client.dispose() client.dispose()
client = mockNet.createNode(MockNodeParameters(client.internals.id), object : MockNetwork.Factory<MockNetwork.MockNode> { client = mockNet.createNode(MockNodeParameters(client.internals.id), { args ->
override fun create(args: MockNodeArgs): MockNetwork.MockNode { object : MockNetwork.MockNode(args) {
return object : MockNetwork.MockNode(args) { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
}
} }
}) })
return (client.smm.allStateMachines[0].stateMachine.resultFuture.apply { mockNet.runNetwork() }.getOrThrow() as ClientResult).attachmentContent return (client.smm.allStateMachines[0].stateMachine.resultFuture.apply { mockNet.runNetwork() }.getOrThrow() as ClientResult).attachmentContent

View File

@ -1,9 +1,9 @@
package net.corda.core.contracts package net.corda.core.transactions
import net.corda.core.contracts.*
import net.corda.core.contracts.ComponentGroupEnum.* import net.corda.core.contracts.ComponentGroupEnum.*
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.transactions.*
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
@ -399,8 +399,7 @@ class CompatibleTransactionTests {
@Test @Test
fun `FilteredTransaction signer manipulation tests`() { fun `FilteredTransaction signer manipulation tests`() {
// Required to call the private constructor. // Required to call the private constructor.
val ftxConstructor = FilteredTransaction::class.java.declaredConstructors[1] val ftxConstructor = ::FilteredTransaction
ftxConstructor.isAccessible = true
// 1st and 3rd commands require a signature from KEY_1. // 1st and 3rd commands require a signature from KEY_1.
val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public)) val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public))
@ -479,12 +478,12 @@ class CompatibleTransactionTests {
// A command with no corresponding signer detected // A command with no corresponding signer detected
// because the pointer of CommandData (3rd leaf) cannot find a corresponding (3rd) signer. // because the pointer of CommandData (3rd leaf) cannot find a corresponding (3rd) signer.
val updatedFilteredComponentsNoSignersKey1SamePMT = listOf(key1CommandsFtx.filteredComponentGroups[0], noLastSignerGroupSamePartialTree) val updatedFilteredComponentsNoSignersKey1SamePMT = listOf(key1CommandsFtx.filteredComponentGroups[0], noLastSignerGroupSamePartialTree)
assertFails { ftxConstructor.newInstance(key1CommandsFtx.id, updatedFilteredComponentsNoSignersKey1SamePMT, key1CommandsFtx.groupHashes) } assertFails { ftxConstructor.invoke(key1CommandsFtx.id, updatedFilteredComponentsNoSignersKey1SamePMT, key1CommandsFtx.groupHashes) }
// Remove both last signer (KEY1) and related command. // Remove both last signer (KEY1) and related command.
// Update partial Merkle tree for signers. // Update partial Merkle tree for signers.
val updatedFilteredComponentsNoLastCommandAndSigners = listOf(noLastCommandDataGroup, noLastSignerGroup) val updatedFilteredComponentsNoLastCommandAndSigners = listOf(noLastCommandDataGroup, noLastSignerGroup)
val ftxNoLastCommandAndSigners = ftxConstructor.newInstance(key1CommandsFtx.id, updatedFilteredComponentsNoLastCommandAndSigners, key1CommandsFtx.groupHashes) as FilteredTransaction val ftxNoLastCommandAndSigners = ftxConstructor.invoke(key1CommandsFtx.id, updatedFilteredComponentsNoLastCommandAndSigners, key1CommandsFtx.groupHashes) as FilteredTransaction
// verify() will pass as the transaction is well-formed. // verify() will pass as the transaction is well-formed.
ftxNoLastCommandAndSigners.verify() ftxNoLastCommandAndSigners.verify()
// checkCommandVisibility() will not pass, because checkAllComponentsVisible(ComponentGroupEnum.SIGNERS_GROUP) will fail. // checkCommandVisibility() will not pass, because checkAllComponentsVisible(ComponentGroupEnum.SIGNERS_GROUP) will fail.
@ -493,7 +492,7 @@ class CompatibleTransactionTests {
// Remove last signer for which there is no pointer from a visible commandData. This is the case of Key2. // Remove last signer for which there is no pointer from a visible commandData. This is the case of Key2.
// Do not change partial Merkle tree for signers. // Do not change partial Merkle tree for signers.
// This time the object can be constructed as there is no pointer mismatch. // This time the object can be constructed as there is no pointer mismatch.
val ftxNoLastSigner = ftxConstructor.newInstance(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2SamePMT, key2CommandsFtx.groupHashes) as FilteredTransaction val ftxNoLastSigner = ftxConstructor.invoke(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2SamePMT, key2CommandsFtx.groupHashes) as FilteredTransaction
// verify() will fail as we didn't change the partial Merkle tree. // verify() will fail as we didn't change the partial Merkle tree.
assertFailsWith<FilteredTransactionVerificationException> { ftxNoLastSigner.verify() } assertFailsWith<FilteredTransactionVerificationException> { ftxNoLastSigner.verify() }
// checkCommandVisibility() will not pass. // checkCommandVisibility() will not pass.
@ -501,7 +500,7 @@ class CompatibleTransactionTests {
// Remove last signer for which there is no pointer from a visible commandData. This is the case of Key2. // Remove last signer for which there is no pointer from a visible commandData. This is the case of Key2.
// Update partial Merkle tree for signers. // Update partial Merkle tree for signers.
val ftxNoLastSignerB = ftxConstructor.newInstance(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2, key2CommandsFtx.groupHashes) as FilteredTransaction val ftxNoLastSignerB = ftxConstructor.invoke(key2CommandsFtx.id, updatedFilteredComponentsNoSignersKey2, key2CommandsFtx.groupHashes) as FilteredTransaction
// verify() will pass, the transaction is well-formed. // verify() will pass, the transaction is well-formed.
ftxNoLastSignerB.verify() ftxNoLastSignerB.verify()
// But, checkAllComponentsVisible() will not pass. // But, checkAllComponentsVisible() will not pass.
@ -526,20 +525,18 @@ class CompatibleTransactionTests {
val alterFilteredComponents = listOf(key1CommandsFtx.filteredComponentGroups[0], alterSignerGroup) val alterFilteredComponents = listOf(key1CommandsFtx.filteredComponentGroups[0], alterSignerGroup)
// Do not update groupHashes. // Do not update groupHashes.
val ftxAlterSigner = ftxConstructor.newInstance(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes) as FilteredTransaction val ftxAlterSigner = ftxConstructor.invoke(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes) as FilteredTransaction
// Visible components in signers group cannot be verified against their partial Merkle tree. // Visible components in signers group cannot be verified against their partial Merkle tree.
assertFailsWith<FilteredTransactionVerificationException> { ftxAlterSigner.verify() } assertFailsWith<FilteredTransactionVerificationException> { ftxAlterSigner.verify() }
// Also, checkAllComponentsVisible() will not pass (groupHash matching will fail). // Also, checkAllComponentsVisible() will not pass (groupHash matching will fail).
assertFailsWith<ComponentVisibilityException> { ftxAlterSigner.checkCommandVisibility(DUMMY_KEY_1.public) } assertFailsWith<ComponentVisibilityException> { ftxAlterSigner.checkCommandVisibility(DUMMY_KEY_1.public) }
// Update groupHashes. // Update groupHashes.
val ftxAlterSignerB = ftxConstructor.newInstance(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes.subList(0, 6) + alterMTree.hash) as FilteredTransaction val ftxAlterSignerB = ftxConstructor.invoke(key1CommandsFtx.id, alterFilteredComponents, key1CommandsFtx.groupHashes.subList(0, 6) + alterMTree.hash) as FilteredTransaction
// Visible components in signers group cannot be verified against their partial Merkle tree. // Visible components in signers group cannot be verified against their partial Merkle tree.
assertFailsWith<FilteredTransactionVerificationException> { ftxAlterSignerB.verify() } assertFailsWith<FilteredTransactionVerificationException> { ftxAlterSignerB.verify() }
// Also, checkAllComponentsVisible() will not pass (top level Merkle tree cannot be verified against transaction's id). // Also, checkAllComponentsVisible() will not pass (top level Merkle tree cannot be verified against transaction's id).
assertFailsWith<ComponentVisibilityException> { ftxAlterSignerB.checkCommandVisibility(DUMMY_KEY_1.public) } assertFailsWith<ComponentVisibilityException> { ftxAlterSignerB.checkCommandVisibility(DUMMY_KEY_1.public) }
ftxConstructor.isAccessible = false
} }
} }

View File

@ -1,8 +1,7 @@
package net.corda.core.contracts package net.corda.core.transactions
import net.corda.core.contracts.*
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices

View File

@ -1,7 +1,9 @@
package net.corda.core.contracts package net.corda.core.transactions
import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.requireThat
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.`issued by` import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
@ -12,7 +14,7 @@ import org.junit.Test
import java.time.Instant import java.time.Instant
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
val TEST_TIMELOCK_ID = "net.corda.core.contracts.TransactionEncumbranceTests\$DummyTimeLock" val TEST_TIMELOCK_ID = "net.corda.core.transactions.TransactionEncumbranceTests\$DummyTimeLock"
class TransactionEncumbranceTests { class TransactionEncumbranceTests {
val defaultIssuer = MEGA_CORP.ref(1) val defaultIssuer = MEGA_CORP.ref(1)

View File

@ -1,11 +1,9 @@
package net.corda.core.contracts package net.corda.core.transactions
import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract

View File

@ -3,7 +3,7 @@ Building a Corda Network on Azure Marketplace
To help you design, build and test applications on Corda, called CorDapps, a Corda network can be deployed on the `Microsoft Azure Marketplace <https://azure.microsoft.com/en-gb/overview/what-is-azure>`_ To help you design, build and test applications on Corda, called CorDapps, a Corda network can be deployed on the `Microsoft Azure Marketplace <https://azure.microsoft.com/en-gb/overview/what-is-azure>`_
This Corda network offering builds a pre-configured network of Corda nodes as Ubuntu virtual machines (VM). The network comprises of a Network Map Service node, a Notary node and up to nine Corda nodes using a version of Corda of your choosing. The following guide will also show you how to load a simple Yo! CorDapp which demonstrates the basic principles of Corda. When you are ready to go further with developing on Corda and start making contributions to the project head over to the `Corda.net <https://www.corda.net/>`_. This Corda network offering builds a pre-configured network of Corda nodes as Ubuntu virtual machines (VM). The network comprises of a Notary node and up to nine Corda nodes using a version of Corda of your choosing. The following guide will also show you how to load a simple Yo! CorDapp which demonstrates the basic principles of Corda. When you are ready to go further with developing on Corda and start making contributions to the project head over to the `Corda.net <https://www.corda.net/>`_.
Pre-requisites Pre-requisites
-------------- --------------

View File

@ -8,6 +8,8 @@ UNRELEASED
---------- ----------
* ``ConfigUtilities`` now read system properties for a node. This allow to specify data source properties at runtime. * ``ConfigUtilities`` now read system properties for a node. This allow to specify data source properties at runtime.
* ``CordaRPCOps`` implementation now checks permissions for any function invocation, rather than just when starting flows.
* ``OpaqueBytes.bytes`` now returns a clone of its underlying ``ByteArray``, and has been redeclared as ``final``. * ``OpaqueBytes.bytes`` now returns a clone of its underlying ``ByteArray``, and has been redeclared as ``final``.
This is a minor change to the public API, but is required to ensure that classes like ``SecureHash`` are immutable. This is a minor change to the public API, but is required to ensure that classes like ``SecureHash`` are immutable.
@ -72,6 +74,9 @@ UNRELEASED
* The ``ReceiveTransactionFlow`` can now be told to record the transaction at the same time as receiving it. Using this * The ``ReceiveTransactionFlow`` can now be told to record the transaction at the same time as receiving it. Using this
feature, better support for observer/regulator nodes has been added. See :doc:`tutorial-observer-nodes`. feature, better support for observer/regulator nodes has been added. See :doc:`tutorial-observer-nodes`.
* Moved ``NodeInfoSchema`` to internal package as the node info's database schema is not part of the public API. This is
needed to allow new ``node_info_hash`` column to be added for the network map redesign work.
.. _changelog_v1: .. _changelog_v1:
Release 1.0 Release 1.0

View File

@ -41,7 +41,7 @@ The syntax for adding an RPC user is:
... ...
] ]
Currently, users need special permissions to start flows via RPC. These permissions are added as follows: Users need permissions to invoke any RPC call. By default, nothing is allowed. These permissions are specified as follows:
.. container:: codeset .. container:: codeset
@ -62,6 +62,15 @@ Currently, users need special permissions to start flows via RPC. These permissi
.. note:: Currently, the node's web server has super-user access, meaning that it can run any RPC operation without .. note:: Currently, the node's web server has super-user access, meaning that it can run any RPC operation without
logging in. This will be changed in a future release. logging in. This will be changed in a future release.
Permissions Syntax
^^^^^^^^^^^^^^^^^^
Fine grained permissions allow a user to invoke a specific RPC operation, or to start a specific flow. The syntax is:
- to start a specific flow: ``StartFlow.<fully qualified flow name>`` e.g., ``StartFlow.net.corda.flows.ExampleFlow1``.
- to invoke a RPC operation: ``InvokeRpc.<rpc method name>`` e.g., ``InvokeRpc.nodeInfo``.
.. note:: Permission ``InvokeRpc.startFlow`` allows a user to initiate all flows.
Observables Observables
----------- -----------
The RPC system handles observables in a special way. When a method returns an observable, whether directly or The RPC system handles observables in a special way. When a method returns an observable, whether directly or

View File

@ -118,10 +118,6 @@ path to the node's base directory.
Only one of ``raft``, ``bftSMaRt`` or ``custom`` configuration values may be specified. Only one of ``raft``, ``bftSMaRt`` or ``custom`` configuration values may be specified.
:minimumPlatformVersion: Used by the node if it's running the network map service to enforce a minimum version requirement
on registrations - any node on a Platform Version lower than this value will have their registration rejected.
Defaults to 1 if absent.
:useHTTPS: If false the node's web server will be plain HTTP. If true the node will use the same certificate and private :useHTTPS: If false the node's web server will be plain HTTP. If true the node will use the same certificate and private
key from the ``<workspace>/certificates/sslkeystore.jks`` file as the ArtemisMQ port for HTTPS. If HTTPS is enabled key from the ``<workspace>/certificates/sslkeystore.jks`` file as the ArtemisMQ port for HTTPS. If HTTPS is enabled
then unencrypted HTTP traffic to the node's **webAddress** port is not supported. then unencrypted HTTP traffic to the node's **webAddress** port is not supported.

View File

@ -16,7 +16,7 @@ Running DemoBench
Configuring a Node Configuring a Node
Each node must have a unique name to identify it to the network map service. DemoBench will suggest node names, nearest cities and local port numbers to use. Each node must have a unique name to identify it to the network map service. DemoBench will suggest node names, nearest cities and local port numbers to use.
The first node will host the network map service, and we are forcing that node also to be a notary. Hence only notary services will be available to be selected in the ``Services`` list. For subsequent nodes you may also select any of Corda's other built-in services. The first node will be a notary. Hence only notary services will be available to be selected in the ``Services`` list. For subsequent nodes you may also select any of Corda's other built-in services.
Press the ``Start node`` button to launch the Corda node with your configuration. Press the ``Start node`` button to launch the Corda node with your configuration.

View File

@ -41,11 +41,12 @@ notary node:
cordapps = [] cordapps = []
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]] rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
} }
// Example of explicit addresses being used.
node { node {
name "CN=NodeC,O=NodeC,L=Paris,C=FR" name "CN=NodeC,O=NodeC,L=Paris,C=FR"
p2pPort 10011 p2pAddress "localhost:10011"
rpcPort 10012 rpcAddress "localhost:10012"
webPort 10013 webAddress "localhost:10013"
cordapps = [] cordapps = []
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]] rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
} }

View File

@ -1,6 +1,7 @@
package net.corda.docs package net.corda.docs
import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.concurrent.transpose
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.messaging.vaultTrackBy import net.corda.core.messaging.vaultTrackBy
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
@ -10,7 +11,8 @@ import net.corda.finance.DOLLARS
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
@ -24,17 +26,22 @@ class IntegrationTestingTutorial {
driver(startNodesInProcess = true, driver(startNodesInProcess = true,
extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset")) { extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset")) {
val aliceUser = User("aliceUser", "testPassword1", permissions = setOf( val aliceUser = User("aliceUser", "testPassword1", permissions = setOf(
startFlowPermission<CashIssueFlow>(), startFlow<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>() startFlow<CashPaymentFlow>(),
invokeRpc("vaultTrackBy"),
invokeRpc(CordaRPCOps::notaryIdentities),
invokeRpc(CordaRPCOps::networkMapFeed)
)) ))
val bobUser = User("bobUser", "testPassword2", permissions = setOf( val bobUser = User("bobUser", "testPassword2", permissions = setOf(
startFlowPermission<CashPaymentFlow>() startFlow<CashPaymentFlow>(),
invokeRpc("vaultTrackBy"),
invokeRpc(CordaRPCOps::networkMapFeed)
)) ))
val (alice, bob) = listOf( val (alice, bob) = listOf(
startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)), startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)),
startNode(providedName = BOB.name, rpcUsers = listOf(bobUser)), startNode(providedName = BOB.name, rpcUsers = listOf(bobUser))
startNotaryNode(DUMMY_NOTARY.name)
).transpose().getOrThrow() ).transpose().getOrThrow()
// END 1 // END 1
// START 2 // START 2
@ -43,9 +50,6 @@ class IntegrationTestingTutorial {
val bobClient = bob.rpcClientToNode() val bobClient = bob.rpcClientToNode()
val bobProxy = bobClient.start("bobUser", "testPassword2").proxy val bobProxy = bobClient.start("bobUser", "testPassword2").proxy
aliceProxy.waitUntilNetworkReady().getOrThrow()
bobProxy.waitUntilNetworkReady().getOrThrow()
// END 2 // END 2
// START 3 // START 3

View File

@ -1,3 +1,5 @@
@file:Suppress("unused")
package net.corda.docs package net.corda.docs
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
@ -8,16 +10,15 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.finance.USD import net.corda.finance.USD
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashExitFlow
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.graphstream.graph.Edge import org.graphstream.graph.Edge
import org.graphstream.graph.Node import org.graphstream.graph.Node
@ -42,19 +43,18 @@ fun main(args: Array<String>) {
val printOrVisualise = PrintOrVisualise.valueOf(args[0]) val printOrVisualise = PrintOrVisualise.valueOf(args[0])
val baseDirectory = Paths.get("build/rpc-api-tutorial") val baseDirectory = Paths.get("build/rpc-api-tutorial")
val user = User("user", "password", permissions = setOf(startFlowPermission<CashIssueFlow>(), val user = User("user", "password", permissions = setOf(startFlow<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>(), startFlow<CashPaymentFlow>(),
startFlowPermission<CashExitFlow>())) startFlow<CashExitFlow>(),
invokeRpc(CordaRPCOps::nodeInfo)
))
driver(driverDirectory = baseDirectory, extraCordappPackagesToScan = listOf("net.corda.finance")) { driver(driverDirectory = baseDirectory, extraCordappPackagesToScan = listOf("net.corda.finance")) {
startNotaryNode(DUMMY_NOTARY.name)
val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)).get() val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)).get()
// END 1 // END 1
// START 2 // START 2
val client = node.rpcClientToNode() val client = node.rpcClientToNode()
val proxy = client.start("user", "password").proxy val proxy = client.start("user", "password").proxy
proxy.waitUntilNetworkReady().getOrThrow()
thread { thread {
generateTransactions(proxy) generateTransactions(proxy)
@ -143,4 +143,4 @@ class ExampleRPCSerializationWhitelist : SerializationWhitelist {
// Add classes like this. // Add classes like this.
override val whitelist = listOf(ExampleRPCValue::class.java) override val whitelist = listOf(ExampleRPCValue::class.java)
} }
// END 7 // END 7

View File

@ -55,7 +55,6 @@ class TutorialMockNetwork {
} }
lateinit private var mockNet: MockNetwork lateinit private var mockNet: MockNetwork
lateinit private var notary: StartedNode<MockNetwork.MockNode>
lateinit private var nodeA: StartedNode<MockNetwork.MockNode> lateinit private var nodeA: StartedNode<MockNetwork.MockNode>
lateinit private var nodeB: StartedNode<MockNetwork.MockNode> lateinit private var nodeB: StartedNode<MockNetwork.MockNode>
@ -66,7 +65,6 @@ class TutorialMockNetwork {
@Before @Before
fun setUp() { fun setUp() {
mockNet = MockNetwork() mockNet = MockNetwork()
notary = mockNet.createNotaryNode()
nodeA = mockNet.createPartyNode() nodeA = mockNet.createPartyNode()
nodeB = mockNet.createPartyNode() nodeB = mockNet.createPartyNode()

View File

@ -1,8 +1,4 @@
myLegalName : "O=Bank A,L=London,C=GB" myLegalName : "O=Bank A,L=London,C=GB"
p2pAddress : "my-corda-node:10002" p2pAddress : "my-corda-node:10002"
webAddress : "localhost:10003" webAddress : "localhost:10003"
networkMapService : {
address : "my-network-map:10000"
legalName : "O=Network Map Service,OU=corda,L=London,C=GB"
}
verifierType: "OutOfProcess" verifierType: "OutOfProcess"

View File

@ -10,7 +10,8 @@ import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.CashSchemaV1
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.* import net.corda.testing.chooseIdentity
import net.corda.testing.getDefaultNotary
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import org.junit.After import org.junit.After
import org.junit.Assert import org.junit.Assert
@ -19,23 +20,20 @@ import org.junit.Test
import java.util.* import java.util.*
class CustomVaultQueryTest { class CustomVaultQueryTest {
private lateinit var mockNet: MockNetwork
lateinit var mockNet: MockNetwork private lateinit var nodeA: StartedNode<MockNetwork.MockNode>
lateinit var nodeA: StartedNode<MockNetwork.MockNode> private lateinit var nodeB: StartedNode<MockNetwork.MockNode>
lateinit var nodeB: StartedNode<MockNetwork.MockNode> private lateinit var notary: Party
lateinit var notary: Party
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork( mockNet = MockNetwork(threadPerNode = true,
threadPerNode = true,
cordappPackages = listOf( cordappPackages = listOf(
"net.corda.finance.contracts.asset", "net.corda.finance.contracts.asset",
CashSchemaV1::class.packageName, CashSchemaV1::class.packageName,
"net.corda.docs" "net.corda.docs"
) )
) )
mockNet.createNotaryNode()
nodeA = mockNet.createPartyNode() nodeA = mockNet.createPartyNode()
nodeB = mockNet.createPartyNode() nodeB = mockNet.createPartyNode()
nodeA.internals.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java) nodeA.internals.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java)

View File

@ -10,7 +10,8 @@ import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.CashSchemaV1
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.* import net.corda.testing.chooseIdentity
import net.corda.testing.getDefaultNotary
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -18,15 +19,14 @@ import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
class FxTransactionBuildTutorialTest { class FxTransactionBuildTutorialTest {
lateinit var mockNet: MockNetwork private lateinit var mockNet: MockNetwork
lateinit var nodeA: StartedNode<MockNetwork.MockNode> private lateinit var nodeA: StartedNode<MockNetwork.MockNode>
lateinit var nodeB: StartedNode<MockNetwork.MockNode> private lateinit var nodeB: StartedNode<MockNetwork.MockNode>
lateinit var notary: Party private lateinit var notary: Party
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName)) mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName))
mockNet.createNotaryNode()
nodeA = mockNet.createPartyNode() nodeA = mockNet.createPartyNode()
nodeB = mockNet.createPartyNode() nodeB = mockNet.createPartyNode()
nodeB.internals.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java) nodeB.internals.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java)

View File

@ -10,7 +10,8 @@ import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.services.api.StartedNodeServices import net.corda.node.services.api.StartedNodeServices
import net.corda.testing.* import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB_NAME
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -18,11 +19,11 @@ import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
class WorkflowTransactionBuildTutorialTest { class WorkflowTransactionBuildTutorialTest {
lateinit var mockNet: MockNetwork private lateinit var mockNet: MockNetwork
lateinit var aliceServices: StartedNodeServices private lateinit var aliceServices: StartedNodeServices
lateinit var bobServices: StartedNodeServices private lateinit var bobServices: StartedNodeServices
lateinit var alice: Party private lateinit var alice: Party
lateinit var bob: Party private lateinit var bob: Party
// Helper method to locate the latest Vault version of a LinearState // Helper method to locate the latest Vault version of a LinearState
private inline fun <reified T : LinearState> ServiceHub.latest(ref: UniqueIdentifier): StateAndRef<T> { private inline fun <reified T : LinearState> ServiceHub.latest(ref: UniqueIdentifier): StateAndRef<T> {
@ -33,8 +34,6 @@ class WorkflowTransactionBuildTutorialTest {
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.docs")) mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.docs"))
// While we don't use the notary, we need there to be one on the network
mockNet.createNotaryNode()
val aliceNode = mockNet.createPartyNode(ALICE_NAME) val aliceNode = mockNet.createPartyNode(ALICE_NAME)
val bobNode = mockNet.createPartyNode(BOB_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME)
aliceNode.internals.registerInitiatedFlow(RecordCompletionFlow::class.java) aliceNode.internals.registerInitiatedFlow(RecordCompletionFlow::class.java)

View File

@ -65,11 +65,6 @@ for maintenance and other minor purposes.
corresponding bridge is used to forward the message to an advertising peer's p2p queue. Once a peer is picked the corresponding bridge is used to forward the message to an advertising peer's p2p queue. Once a peer is picked the
session continues on as normal. session continues on as normal.
:``internal.networkmap``:
This is another private queue just for the node which functions in a similar manner to the ``internal.peers.*`` queues
except this is used to form a connection to the network map node. The node running the network map service is treated
differently as it provides information about the rest of the network.
:``rpc.requests``: :``rpc.requests``:
RPC clients send their requests here, and it's only open for sending by clients authenticated as RPC users. RPC clients send their requests here, and it's only open for sending by clients authenticated as RPC users.

View File

@ -139,28 +139,17 @@ physical host and port information required for the physical
``ArtemisMQ`` messaging layer. ``ArtemisMQ`` messaging layer.
PersistentNetworkMapService and NetworkMapService PersistentNetworkMapService
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``NetworkMapService`` is a node internal component responsible for The ``PersistentNetworkMapService`` keeps track of ``NodeInfo`` and
managing and communicating the directory of authenticated registered persists it to the database. It and will include nodes that are not currently active.
nodes and advertised services in the Corda network. Only a single node The networking layer will persist any messages directed at such inactive
in the network (in future this will be a clustered service) should host
the NetworkMapService implementation. All other Corda nodes initiate
their remote connection to the ``NetworkMapService`` early in the
start-up sequence and wait to synchronise their local
``NetworkMapCache`` before activating any flows. For the
``PersistentNetworkMapService`` registered ``NodeInfo`` data is
persisted and will include nodes that are not currently active. The
networking layer will persist any messages directed at such inactive
nodes with the expectation that they will be delivered eventually, or nodes with the expectation that they will be delivered eventually, or
else that the source flow will be terminated by admin intervention. else that the source flow will be terminated by admin intervention.
An ``InMemoryNetworkMapService`` is also available for unit tests An ``InMemoryNetworkMapService`` is also available for unit tests
without a database. without a database.
The ``NetworkMapService`` should not be used by any flows, or
contracts. Instead they should access the NetworkMapCache service to
access this data.
Storage and persistence related services Storage and persistence related services
---------------------------------------- ----------------------------------------

View File

@ -3,8 +3,7 @@
Creating a Corda network Creating a Corda network
======================== ========================
A Corda network consists of a number of machines running nodes, including a single node operating as the network map A Corda network consists of a number of machines running nodes. These nodes communicate using persistent protocols in order to create and validate transactions.
service. These nodes communicate using persistent protocols in order to create and validate transactions.
There are four broader categories of functionality one such node may have. These pieces of functionality are provided as There are four broader categories of functionality one such node may have. These pieces of functionality are provided as
services, and one node may run several of them. services, and one node may run several of them.

View File

@ -122,7 +122,7 @@ In the instructions above the server node permissions are configured programmati
.. code-block:: text .. code-block:: text
driver(driverDirectory = baseDirectory) { driver(driverDirectory = baseDirectory) {
val user = User("user", "password", permissions = setOf(startFlowPermission<CashFlow>())) val user = User("user", "password", permissions = setOf(startFlow<CashFlow>()))
val node = startNode("CN=Alice Corp,O=Alice Corp,L=London,C=GB", rpcUsers = listOf(user)).get() val node = startNode("CN=Alice Corp,O=Alice Corp,L=London,C=GB", rpcUsers = listOf(user)).get()
When starting a standalone node using a configuration file we must supply the RPC credentials as follows: When starting a standalone node using a configuration file we must supply the RPC credentials as follows:

View File

@ -1,41 +1,38 @@
package net.corda.finance.contracts.asset package net.corda.finance.contracts.asset
import net.corda.core.internal.packageName
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.flows.CashException import net.corda.finance.flows.CashException
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.finance.schemas.CashSchemaV1
import net.corda.testing.chooseIdentity
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.MockNodeParameters
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After
import org.junit.Test import org.junit.Test
class CashSelectionH2Test { class CashSelectionH2Test {
private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance"))
@After
fun cleanUp() {
mockNet.stopNodes()
}
@Test @Test
fun `check does not hold connection over retries`() { fun `check does not hold connection over retries`() {
val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName)) val bankA = mockNet.createNode(MockNodeParameters(configOverrides = {
try { // Tweak connections to be minimal to make this easier (1 results in a hung node during start up, so use 2 connections).
val notaryNode = mockNet.createNotaryNode() it.dataSourceProperties.setProperty("maximumPoolSize", "2")
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). val notary = mockNet.defaultNotaryIdentity
existingConfig.dataSourceProperties.setProperty("maximumPoolSize", "2")
}))
mockNet.startNodes()
// Start more cash spends than we have connections. If spend leaks a connection on retry, we will run out of connections. // Start more cash spends than we have connections. If spend leaks a connection on retry, we will run out of connections.
val flow1 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity())) val flow1 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary))
val flow2 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity())) val flow2 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary))
val flow3 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notaryNode.info.chooseIdentity())) val flow3 = bankA.services.startFlow(CashPaymentFlow(amount = 100.DOLLARS, anonymous = false, recipient = notary))
assertThatThrownBy { flow1.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) assertThatThrownBy { flow1.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
assertThatThrownBy { flow2.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) assertThatThrownBy { flow2.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
assertThatThrownBy { flow3.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java) assertThatThrownBy { flow3.resultFuture.getOrThrow() }.isInstanceOf(CashException::class.java)
} finally {
mockNet.stopNodes()
}
} }
} }

View File

@ -7,7 +7,9 @@ import net.corda.finance.DOLLARS
import net.corda.finance.`issued by` import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.* import net.corda.testing.BOC
import net.corda.testing.chooseIdentity
import net.corda.testing.getDefaultNotary
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNetwork.MockNode
@ -23,15 +25,13 @@ class CashExitFlowTests {
private val ref = OpaqueBytes.of(0x01) private val ref = OpaqueBytes.of(0x01)
private lateinit var bankOfCordaNode: StartedNode<MockNode> private lateinit var bankOfCordaNode: StartedNode<MockNode>
private lateinit var bankOfCorda: Party private lateinit var bankOfCorda: Party
private lateinit var notaryNode: StartedNode<MockNode>
private lateinit var notary: Party private lateinit var notary: Party
@Before @Before
fun start() { fun start() {
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset")) mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(),
notaryNode = mockNet.createNotaryNode() cordappPackages = listOf("net.corda.finance.contracts.asset"))
bankOfCordaNode = mockNet.createPartyNode(BOC.name) bankOfCordaNode = mockNet.createPartyNode(BOC.name)
notary = notaryNode.services.getDefaultNotary()
bankOfCorda = bankOfCordaNode.info.chooseIdentity() bankOfCorda = bankOfCordaNode.info.chooseIdentity()
mockNet.runNetwork() mockNet.runNetwork()

View File

@ -7,9 +7,8 @@ import net.corda.finance.DOLLARS
import net.corda.finance.`issued by` import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.chooseIdentity
import net.corda.testing.getDefaultNotary
import net.corda.testing.BOC import net.corda.testing.BOC
import net.corda.testing.chooseIdentity
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNetwork.MockNode
@ -23,16 +22,14 @@ class CashIssueFlowTests {
private lateinit var mockNet: MockNetwork private lateinit var mockNet: MockNetwork
private lateinit var bankOfCordaNode: StartedNode<MockNode> private lateinit var bankOfCordaNode: StartedNode<MockNode>
private lateinit var bankOfCorda: Party private lateinit var bankOfCorda: Party
private lateinit var notaryNode: StartedNode<MockNode>
private lateinit var notary: Party private lateinit var notary: Party
@Before @Before
fun start() { fun start() {
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset")) mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset"))
notaryNode = mockNet.createNotaryNode()
bankOfCordaNode = mockNet.createPartyNode(BOC.name) bankOfCordaNode = mockNet.createPartyNode(BOC.name)
bankOfCorda = bankOfCordaNode.info.chooseIdentity() bankOfCorda = bankOfCordaNode.info.chooseIdentity()
notary = notaryNode.services.getDefaultNotary() notary = mockNet.defaultNotaryIdentity
mockNet.runNetwork() mockNet.runNetwork()
} }

View File

@ -26,18 +26,16 @@ class CashPaymentFlowTests {
private val ref = OpaqueBytes.of(0x01) private val ref = OpaqueBytes.of(0x01)
private lateinit var bankOfCordaNode: StartedNode<MockNode> private lateinit var bankOfCordaNode: StartedNode<MockNode>
private lateinit var bankOfCorda: Party private lateinit var bankOfCorda: Party
private lateinit var notaryNode: StartedNode<MockNode> private lateinit var aliceNode: StartedNode<MockNode>
private lateinit var notary: Party
@Before @Before
fun start() { fun start() {
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset")) mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset"))
notaryNode = mockNet.createNotaryNode()
bankOfCordaNode = mockNet.createPartyNode(BOC.name) bankOfCordaNode = mockNet.createPartyNode(BOC.name)
aliceNode = mockNet.createPartyNode(ALICE.name)
bankOfCorda = bankOfCordaNode.info.chooseIdentity() bankOfCorda = bankOfCordaNode.info.chooseIdentity()
notary = notaryNode.services.getDefaultNotary()
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture
mockNet.runNetwork() mockNet.runNetwork()
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, mockNet.defaultNotaryIdentity)).resultFuture
future.getOrThrow() future.getOrThrow()
} }
@ -48,7 +46,7 @@ class CashPaymentFlowTests {
@Test @Test
fun `pay some cash`() { fun `pay some cash`() {
val payTo = notaryNode.info.chooseIdentity() val payTo = aliceNode.info.chooseIdentity()
val expectedPayment = 500.DOLLARS val expectedPayment = 500.DOLLARS
val expectedChange = 1500.DOLLARS val expectedChange = 1500.DOLLARS
@ -56,7 +54,7 @@ class CashPaymentFlowTests {
// Register for vault updates // Register for vault updates
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL) val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
val (_, vaultUpdatesBoc) = bankOfCordaNode.services.vaultService.trackBy<Cash.State>(criteria) val (_, vaultUpdatesBoc) = bankOfCordaNode.services.vaultService.trackBy<Cash.State>(criteria)
val (_, vaultUpdatesBankClient) = notaryNode.services.vaultService.trackBy<Cash.State>(criteria) val (_, vaultUpdatesBankClient) = aliceNode.services.vaultService.trackBy<Cash.State>(criteria)
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expectedPayment, val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expectedPayment,
payTo)).resultFuture payTo)).resultFuture
@ -88,7 +86,7 @@ class CashPaymentFlowTests {
@Test @Test
fun `pay more than we have`() { fun `pay more than we have`() {
val payTo = notaryNode.info.chooseIdentity() val payTo = aliceNode.info.chooseIdentity()
val expected = 4000.DOLLARS val expected = 4000.DOLLARS
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected, val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected,
payTo)).resultFuture payTo)).resultFuture
@ -100,7 +98,7 @@ class CashPaymentFlowTests {
@Test @Test
fun `pay zero cash`() { fun `pay zero cash`() {
val payTo = notaryNode.info.chooseIdentity() val payTo = aliceNode.info.chooseIdentity()
val expected = 0.DOLLARS val expected = 0.DOLLARS
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected, val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected,
payTo)).resultFuture payTo)).resultFuture

View File

@ -61,7 +61,8 @@ class CordappPlugin : Plugin<Project> {
} }
filteredDeps.forEach { filteredDeps.forEach {
// net.corda or com.r3.corda.enterprise may be a core dependency which shouldn't be included in this cordapp so give a warning // net.corda or com.r3.corda.enterprise may be a core dependency which shouldn't be included in this cordapp so give a warning
if ((it.group.startsWith("net.corda.") || it.group.startsWith("com.r3.corda.enterprise."))) { val group = it.group?.toString() ?: ""
if (group.startsWith("net.corda.") || group.startsWith("com.r3.corda.enterprise.")) {
project.logger.warn("You appear to have included a Corda platform component ($it) using a 'compile' or 'runtime' dependency." + project.logger.warn("You appear to have included a Corda platform component ($it) using a 'compile' or 'runtime' dependency." +
"This can cause node stability problems. Please use 'corda' instead." + "This can cause node stability problems. Please use 'corda' instead." +
"See http://docs.corda.net/cordapp-build-systems.html") "See http://docs.corda.net/cordapp-build-systems.html")

View File

@ -58,7 +58,16 @@ public class CordformNode implements NodeDefinition {
} }
/** /**
* Set the Artemis P2P port for this node. * Get the artemis address for this node.
*
* @return This node's P2P address.
*/
public String getP2pAddress() {
return config.getString("p2pAddress");
}
/**
* Set the Artemis P2P port for this node on localhost.
* *
* @param p2pPort The Artemis messaging queue port. * @param p2pPort The Artemis messaging queue port.
*/ */
@ -67,7 +76,16 @@ public class CordformNode implements NodeDefinition {
} }
/** /**
* Set the Artemis RPC port for this node. * Set the Artemis P2P address for this node.
*
* @param p2pAddress The Artemis messaging queue host and port.
*/
public void p2pAddress(String p2pAddress) {
config = config.withValue("p2pAddress", ConfigValueFactory.fromAnyRef(p2pAddress));
}
/**
* Set the Artemis RPC port for this node on localhost.
* *
* @param rpcPort The Artemis RPC queue port. * @param rpcPort The Artemis RPC queue port.
*/ */
@ -75,6 +93,15 @@ public class CordformNode implements NodeDefinition {
config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + rpcPort)); config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + rpcPort));
} }
/**
* Set the Artemis RPC address for this node.
*
* @param rpcAddress The Artemis RPC queue host and port.
*/
public void rpcAddress(String rpcAddress) {
config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(rpcAddress));
}
/** /**
* Set the path to a file with optional properties, which are appended to the generated node.conf file. * Set the path to a file with optional properties, which are appended to the generated node.conf file.
* *

View File

@ -61,7 +61,7 @@ class Node(private val project: Project) : CordformNode() {
} }
/** /**
* Set the HTTP web server port for this node. * Set the HTTP web server port for this node. Will use localhost as the address.
* *
* @param webPort The web port number for this node. * @param webPort The web port number for this node.
*/ */
@ -70,6 +70,16 @@ class Node(private val project: Project) : CordformNode() {
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$webPort")) ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$webPort"))
} }
/**
* Set the HTTP web server address and port for this node.
*
* @param webAddress The web address for this node.
*/
fun webAddress(webAddress: String) {
config = config.withValue("webAddress",
ConfigValueFactory.fromAnyRef(webAddress))
}
/** /**
* Set the network map address for this node. * Set the network map address for this node.
* *
@ -107,15 +117,6 @@ class Node(private val project: Project) : CordformNode() {
appendOptionalConfig() appendOptionalConfig()
} }
/**
* Get the artemis address for this node.
*
* @return This node's P2P address.
*/
fun getP2PAddress(): String {
return config.getString("p2pAddress")
}
internal fun rootDir(rootDir: Path) { internal fun rootDir(rootDir: Path) {
if(name == null) { if(name == null) {
project.logger.error("Node has a null name - cannot create node") project.logger.error("Node has a null name - cannot create node")

Binary file not shown.

View File

@ -1,6 +1,6 @@
#Thu Aug 24 12:32:31 BST 2017 #Mon Nov 06 15:05:49 GMT 2017
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip

View File

@ -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()
}

View File

@ -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
}

View File

@ -4,15 +4,25 @@ package net.corda.nodeapi.internal.serialization
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.SerializationDefaults
import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
/* /*
* Serialisation contexts for the client. * Serialisation contexts for the client.
* These have been refactored into a separate file to prevent * These have been refactored into a separate file to prevent
* servers from trying to instantiate any of them. * servers from trying to instantiate any of them.
*/ */
val KRYO_RPC_CLIENT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1, val KRYO_RPC_CLIENT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader, SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(), emptyMap(),
true, true,
SerializationContext.UseCase.RPCClient) SerializationContext.UseCase.RPCClient)
val AMQP_RPC_CLIENT_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(),
true,
SerializationContext.UseCase.RPCClient)

View File

@ -12,6 +12,7 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.nodeapi.internal.serialization.amqp.hasAnnotationInHierarchy import net.corda.nodeapi.internal.serialization.amqp.hasAnnotationInHierarchy
import net.corda.nodeapi.internal.serialization.kryo.ThrowableSerializer
import java.io.PrintWriter import java.io.PrintWriter
import java.lang.reflect.Modifier.isAbstract import java.lang.reflect.Modifier.isAbstract
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets

View File

@ -1,26 +1,13 @@
package net.corda.nodeapi.internal.serialization package net.corda.nodeapi.internal.serialization
import co.paralleluniverse.fibers.Fiber
import co.paralleluniverse.io.serialization.kryo.KryoSerializer
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.pool.KryoPool
import com.esotericsoftware.kryo.serializers.ClosureSerializer
import com.google.common.cache.Cache import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder import com.google.common.cache.CacheBuilder
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.LazyPool
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.OpaqueBytes
import net.corda.nodeapi.internal.AttachmentsClassLoader import net.corda.nodeapi.internal.AttachmentsClassLoader
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.ByteArrayOutputStream
import java.io.NotSerializableException import java.io.NotSerializableException
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@ -158,124 +145,8 @@ open class SerializationFactoryImpl : SerializationFactory() {
override fun hashCode(): Int = registeredSchemes.hashCode() override fun hashCode(): Int = registeredSchemes.hashCode()
} }
private object AutoCloseableSerialisationDetector : Serializer<AutoCloseable>() {
override fun write(kryo: Kryo, output: Output, closeable: AutoCloseable) {
val message = "${closeable.javaClass.name}, which is a closeable resource, has been detected during flow checkpointing. " +
"Restoring such resources across node restarts is not supported. Make sure code accessing it is " +
"confined to a private method or the reference is nulled out."
throw UnsupportedOperationException(message)
}
override fun read(kryo: Kryo, input: Input, type: Class<AutoCloseable>) = throw IllegalStateException("Should not reach here!")
}
abstract class AbstractKryoSerializationScheme : SerializationScheme {
private val kryoPoolsForContexts = ConcurrentHashMap<Pair<ClassWhitelist, ClassLoader>, KryoPool>()
protected abstract fun rpcClientKryoPool(context: SerializationContext): KryoPool
protected abstract fun rpcServerKryoPool(context: SerializationContext): KryoPool
private fun getPool(context: SerializationContext): KryoPool {
return kryoPoolsForContexts.computeIfAbsent(Pair(context.whitelist, context.deserializationClassLoader)) {
when (context.useCase) {
SerializationContext.UseCase.Checkpoint ->
KryoPool.Builder {
val serializer = Fiber.getFiberSerializer(false) as KryoSerializer
val classResolver = CordaClassResolver(context).apply { setKryo(serializer.kryo) }
// TODO The ClassResolver can only be set in the Kryo constructor and Quasar doesn't provide us with a way of doing that
val field = Kryo::class.java.getDeclaredField("classResolver").apply { isAccessible = true }
serializer.kryo.apply {
field.set(this, classResolver)
DefaultKryoCustomizer.customize(this)
addDefaultSerializer(AutoCloseable::class.java, AutoCloseableSerialisationDetector)
register(ClosureSerializer.Closure::class.java, CordaClosureSerializer)
classLoader = it.second
}
}.build()
SerializationContext.UseCase.RPCClient ->
rpcClientKryoPool(context)
SerializationContext.UseCase.RPCServer ->
rpcServerKryoPool(context)
else ->
KryoPool.Builder {
DefaultKryoCustomizer.customize(CordaKryo(CordaClassResolver(context))).apply { classLoader = it.second }
}.build()
}
}
}
private fun <T : Any> withContext(kryo: Kryo, context: SerializationContext, block: (Kryo) -> T): T {
kryo.context.ensureCapacity(context.properties.size)
context.properties.forEach { kryo.context.put(it.key, it.value) }
try {
return block(kryo)
} finally {
kryo.context.clear()
}
}
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
val pool = getPool(context)
val headerSize = KryoHeaderV0_1.size
val header = byteSequence.take(headerSize)
if (header != KryoHeaderV0_1) {
throw KryoException("Serialized bytes header does not match expected format.")
}
Input(byteSequence.bytes, byteSequence.offset + headerSize, byteSequence.size - headerSize).use { input ->
return pool.run { kryo ->
withContext(kryo, context) {
if (context.objectReferencesEnabled) {
uncheckedCast(kryo.readClassAndObject(input))
} else {
kryo.withoutReferences { uncheckedCast<Any?, T>(kryo.readClassAndObject(input)) }
}
}
}
}
}
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
val pool = getPool(context)
return pool.run { kryo ->
withContext(kryo, context) {
serializeOutputStreamPool.run { stream ->
serializeBufferPool.run { buffer ->
Output(buffer).use {
it.outputStream = stream
it.writeBytes(KryoHeaderV0_1.bytes)
if (context.objectReferencesEnabled) {
kryo.writeClassAndObject(it, obj)
} else {
kryo.withoutReferences { kryo.writeClassAndObject(it, obj) }
}
}
SerializedBytes(stream.toByteArray())
}
}
}
}
}
}
private val serializeBufferPool = LazyPool(
newInstance = { ByteArray(64 * 1024) }
)
private val serializeOutputStreamPool = LazyPool(
clear = ByteArrayOutputStream::reset,
shouldReturnToPool = { it.size() < 256 * 1024 }, // Discard if it grew too large
newInstance = { ByteArrayOutputStream(64 * 1024) }
)
// "corda" + majorVersionByte + minorVersionMSB + minorVersionLSB
val KryoHeaderV0_1: OpaqueBytes = OpaqueBytes("corda\u0000\u0000\u0001".toByteArray(Charsets.UTF_8))
val KRYO_P2P_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(),
true,
SerializationContext.UseCase.P2P)
interface SerializationScheme { interface SerializationScheme {
// byteSequence expected to just be the 8 bytes necessary for versioning // byteSequence expected to just be the 8 bytes necessary for versioning

View File

@ -6,6 +6,11 @@ import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.SerializationDefaults
import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0 import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
object QuasarWhitelist : ClassWhitelist {
override fun hasListed(type: Class<*>): Boolean = true
}
/* /*
* Serialisation contexts for the server. * Serialisation contexts for the server.
@ -16,18 +21,28 @@ import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
* CANNOT always be instantiated outside of the server and so * CANNOT always be instantiated outside of the server and so
* MUST be kept separate! * MUST be kept separate!
*/ */
val KRYO_RPC_SERVER_CONTEXT = SerializationContextImpl(KryoHeaderV0_1, val KRYO_RPC_SERVER_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader, SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(), emptyMap(),
true, true,
SerializationContext.UseCase.RPCServer) SerializationContext.UseCase.RPCServer)
val KRYO_STORAGE_CONTEXT = SerializationContextImpl(KryoHeaderV0_1, val KRYO_STORAGE_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader, SerializationDefaults.javaClass.classLoader,
AllButBlacklisted, AllButBlacklisted,
emptyMap(), emptyMap(),
true, true,
SerializationContext.UseCase.Storage) SerializationContext.UseCase.Storage)
val KRYO_P2P_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(),
true,
SerializationContext.UseCase.P2P)
val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1, val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader, SerializationDefaults.javaClass.classLoader,
QuasarWhitelist, QuasarWhitelist,
@ -35,9 +50,6 @@ val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
true, true,
SerializationContext.UseCase.Checkpoint) SerializationContext.UseCase.Checkpoint)
object QuasarWhitelist : ClassWhitelist {
override fun hasListed(type: Class<*>): Boolean = true
}
val AMQP_STORAGE_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0, val AMQP_STORAGE_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
SerializationDefaults.javaClass.classLoader, SerializationDefaults.javaClass.classLoader,
@ -45,3 +57,18 @@ val AMQP_STORAGE_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
emptyMap(), emptyMap(),
true, true,
SerializationContext.UseCase.Storage) SerializationContext.UseCase.Storage)
val AMQP_P2P_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(),
true,
SerializationContext.UseCase.P2P)
val AMQP_RPC_SERVER_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(),
true,
SerializationContext.UseCase.RPCServer)

View File

@ -1,13 +1,12 @@
@file:JvmName("AMQPSerializationScheme") @file:JvmName("AMQPSerializationScheme")
package net.corda.nodeapi.internal.serialization package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.ByteSequence
import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0 import net.corda.nodeapi.internal.serialization.DefaultWhitelist
import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput import net.corda.nodeapi.internal.serialization.MutableClassWhitelist
import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput import net.corda.nodeapi.internal.serialization.SerializationScheme
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@ -25,7 +24,7 @@ fun SerializerFactory.addToWhitelist(vararg types: Class<*>) {
} }
abstract class AbstractAMQPSerializationScheme : SerializationScheme { abstract class AbstractAMQPSerializationScheme : SerializationScheme {
internal companion object { companion object {
private val serializationWhitelists: List<SerializationWhitelist> by lazy { private val serializationWhitelists: List<SerializationWhitelist> by lazy {
ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist
} }
@ -132,9 +131,3 @@ class AMQPClientSerializationScheme : AbstractAMQPSerializationScheme() {
} }
val AMQP_P2P_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(),
true,
SerializationContext.UseCase.P2P)

View File

@ -1,4 +1,4 @@
package net.corda.nodeapi.internal.serialization package net.corda.nodeapi.internal.serialization.kryo
import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output

View File

@ -1,4 +1,4 @@
package net.corda.nodeapi.internal.serialization package net.corda.nodeapi.internal.serialization.kryo
import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.Serializer import com.esotericsoftware.kryo.Serializer
@ -23,6 +23,10 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.NonEmptySet
import net.corda.core.utilities.toNonEmptySet import net.corda.core.utilities.toNonEmptySet
import net.corda.nodeapi.internal.serialization.CordaClassResolver
import net.corda.nodeapi.internal.serialization.DefaultWhitelist
import net.corda.nodeapi.internal.serialization.GeneratedAttachment
import net.corda.nodeapi.internal.serialization.MutableClassWhitelist
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name

View File

@ -1,4 +1,4 @@
package net.corda.nodeapi.internal.serialization package net.corda.nodeapi.internal.serialization.kryo
import com.esotericsoftware.kryo.* import com.esotericsoftware.kryo.*
import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory
@ -21,11 +21,9 @@ import net.corda.core.serialization.SerializedBytes
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.toObservable import net.corda.core.toObservable
import net.corda.core.transactions.* import net.corda.core.transactions.*
import net.corda.core.transactions.CoreTransaction
import net.corda.core.transactions.NotaryChangeWireTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.SgxSupport import net.corda.core.utilities.SgxSupport
import net.corda.nodeapi.internal.serialization.CordaClassResolver
import net.corda.nodeapi.internal.serialization.serializationContextKey
import org.bouncycastle.asn1.ASN1InputStream import org.bouncycastle.asn1.ASN1InputStream
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509CertificateHolder
@ -288,7 +286,7 @@ object SignedTransactionSerializer : Serializer<SignedTransaction>() {
sealed class UseCaseSerializer<T>(private val allowedUseCases: EnumSet<SerializationContext.UseCase>) : Serializer<T>() { sealed class UseCaseSerializer<T>(private val allowedUseCases: EnumSet<SerializationContext.UseCase>) : Serializer<T>() {
protected fun checkUseCase() { protected fun checkUseCase() {
checkUseCase(allowedUseCases) net.corda.nodeapi.internal.serialization.checkUseCase(allowedUseCases)
} }
} }

View File

@ -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) }
)

View File

@ -1,4 +1,4 @@
package net.corda.nodeapi.internal.serialization package net.corda.nodeapi.internal.serialization.kryo
import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoException import com.esotericsoftware.kryo.KryoException

View File

@ -5,6 +5,8 @@ import net.corda.core.serialization.SerializationContext;
import net.corda.core.serialization.SerializationFactory; import net.corda.core.serialization.SerializationFactory;
import net.corda.core.serialization.SerializedBytes; import net.corda.core.serialization.SerializedBytes;
import net.corda.testing.SerializationEnvironmentRule; import net.corda.testing.SerializationEnvironmentRule;
import net.corda.nodeapi.internal.serialization.kryo.CordaClosureBlacklistSerializer;
import net.corda.nodeapi.internal.serialization.kryo.KryoSerializationSchemeKt;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@ -31,7 +33,7 @@ public final class ForbiddenLambdaSerializationTests {
EnumSet<SerializationContext.UseCase> contexts = EnumSet.complementOf(EnumSet.of(SerializationContext.UseCase.Checkpoint)); EnumSet<SerializationContext.UseCase> contexts = EnumSet.complementOf(EnumSet.of(SerializationContext.UseCase.Checkpoint));
contexts.forEach(ctx -> { contexts.forEach(ctx -> {
SerializationContext context = new SerializationContextImpl(SerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx); SerializationContext context = new SerializationContextImpl(KryoSerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx);
String value = "Hey"; String value = "Hey";
Callable<String> target = (Callable<String> & Serializable) () -> value; Callable<String> target = (Callable<String> & Serializable) () -> value;
@ -54,7 +56,7 @@ public final class ForbiddenLambdaSerializationTests {
EnumSet<SerializationContext.UseCase> contexts = EnumSet.complementOf(EnumSet.of(SerializationContext.UseCase.Checkpoint)); EnumSet<SerializationContext.UseCase> contexts = EnumSet.complementOf(EnumSet.of(SerializationContext.UseCase.Checkpoint));
contexts.forEach(ctx -> { contexts.forEach(ctx -> {
SerializationContext context = new SerializationContextImpl(SerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx); SerializationContext context = new SerializationContextImpl(KryoSerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, ctx);
String value = "Hey"; String value = "Hey";
Callable<String> target = () -> value; Callable<String> target = () -> value;

View File

@ -5,6 +5,8 @@ import net.corda.core.serialization.SerializationContext;
import net.corda.core.serialization.SerializationFactory; import net.corda.core.serialization.SerializationFactory;
import net.corda.core.serialization.SerializedBytes; import net.corda.core.serialization.SerializedBytes;
import net.corda.testing.SerializationEnvironmentRule; import net.corda.testing.SerializationEnvironmentRule;
import net.corda.nodeapi.internal.serialization.kryo.CordaClosureSerializer;
import net.corda.nodeapi.internal.serialization.kryo.KryoSerializationSchemeKt;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@ -24,7 +26,7 @@ public final class LambdaCheckpointSerializationTest {
@Before @Before
public void setup() { public void setup() {
factory = testSerialization.env.getSERIALIZATION_FACTORY(); factory = testSerialization.env.getSERIALIZATION_FACTORY();
context = new SerializationContextImpl(SerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, SerializationContext.UseCase.Checkpoint); context = new SerializationContextImpl(KryoSerializationSchemeKt.getKryoHeaderV0_1(), this.getClass().getClassLoader(), AllWhitelist.INSTANCE, Maps.newHashMap(), true, SerializationContext.UseCase.Checkpoint);
} }
@Test @Test

View File

@ -10,6 +10,8 @@ import net.corda.core.node.services.AttachmentStorage
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.nodeapi.internal.AttachmentsClassLoader import net.corda.nodeapi.internal.AttachmentsClassLoader
import net.corda.nodeapi.internal.AttachmentsClassLoaderTests import net.corda.nodeapi.internal.AttachmentsClassLoaderTests
import net.corda.nodeapi.internal.serialization.kryo.CordaKryo
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
import net.corda.testing.node.MockAttachmentStorage import net.corda.testing.node.MockAttachmentStorage
import net.corda.testing.rigorousMock import net.corda.testing.rigorousMock
import org.junit.Rule import org.junit.Rule

View File

@ -13,6 +13,7 @@ import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.sequence import net.corda.core.utilities.sequence
import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.serialization.KryoServerSerializationScheme
import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
import net.corda.testing.ALICE_PUBKEY import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@ -7,6 +7,7 @@ import net.corda.node.services.statemachine.SessionData
import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput
import net.corda.nodeapi.internal.serialization.amqp.Envelope import net.corda.nodeapi.internal.serialization.amqp.Envelope
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
import net.corda.testing.amqpSpecific import net.corda.testing.amqpSpecific
import net.corda.testing.kryoSpecific import net.corda.testing.kryoSpecific
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule

View File

@ -6,6 +6,7 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.node.services.statemachine.SessionData import net.corda.node.services.statemachine.SessionData
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
import net.corda.testing.amqpSpecific import net.corda.testing.amqpSpecific
import net.corda.testing.kryoSpecific import net.corda.testing.kryoSpecific
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
@ -84,4 +85,4 @@ class MapsSerializationTest {
} }
assertArrayEquals(output.toByteArray(), serializedForm.bytes) assertArrayEquals(output.toByteArray(), serializedForm.bytes)
} }
} }

View File

@ -5,6 +5,10 @@ import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.nodeapi.internal.serialization.kryo.CordaKryo
import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.rigorousMock import net.corda.testing.rigorousMock
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@ -5,6 +5,7 @@ import com.esotericsoftware.kryo.util.DefaultClassResolver
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.node.services.statemachine.SessionData import net.corda.node.services.statemachine.SessionData
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
import net.corda.testing.kryoSpecific import net.corda.testing.kryoSpecific
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertArrayEquals

View File

@ -13,7 +13,6 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationFactory import net.corda.core.serialization.SerializationFactory
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.client.rpc.RPCException import net.corda.client.rpc.RPCException
import net.corda.nodeapi.internal.serialization.AbstractAMQPSerializationScheme
import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.EmptyWhitelist import net.corda.nodeapi.internal.serialization.EmptyWhitelist
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive

View File

@ -19,9 +19,6 @@ configurations {
integrationTestCompile.extendsFrom testCompile integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime integrationTestRuntime.extendsFrom testRuntime
smokeTestCompile.extendsFrom compile
smokeTestRuntime.extendsFrom runtime
} }
sourceSets { sourceSets {
@ -40,20 +37,6 @@ sourceSets {
srcDir file('src/integration-test/resources') srcDir file('src/integration-test/resources')
} }
} }
smokeTest {
kotlin {
// We must NOT have any Node code on the classpath, so do NOT
// include the test or integrationTest dependencies here.
compileClasspath += main.output
runtimeClasspath += main.output
srcDir file('src/smoke-test/kotlin')
}
java {
compileClasspath += main.output
runtimeClasspath += main.output
srcDir file('src/smoke-test/java')
}
}
} }
// Use manual resource copying of log4j2.xml rather than source sets. // Use manual resource copying of log4j2.xml rather than source sets.
@ -62,13 +45,6 @@ processResources {
from file("$rootDir/config/dev/log4j2.xml") from file("$rootDir/config/dev/log4j2.xml")
} }
processSmokeTestResources {
// Bring in the fully built corda.jar for use by NodeFactory in the smoke tests
from(project(':node:capsule').tasks.buildCordaJAR) {
rename 'corda-(.*)', 'corda.jar'
}
}
// To find potential version conflicts, run "gradle htmlDependencyReport" and then look in // To find potential version conflicts, run "gradle htmlDependencyReport" and then look in
// build/reports/project/dependencies/index.html for green highlighted parts of the tree. // build/reports/project/dependencies/index.html for green highlighted parts of the tree.
@ -184,11 +160,6 @@ dependencies {
integrationTestCompile "junit:junit:$junit_version" integrationTestCompile "junit:junit:$junit_version"
integrationTestCompile "org.assertj:assertj-core:${assertj_version}" integrationTestCompile "org.assertj:assertj-core:${assertj_version}"
// Smoke tests do NOT have any Node code on the classpath!
smokeTestCompile project(':smoke-test-utils')
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
smokeTestCompile "junit:junit:$junit_version"
// Jetty dependencies for NetworkMapClient test. // Jetty dependencies for NetworkMapClient test.
// Web stuff: for HTTP[S] servlets // Web stuff: for HTTP[S] servlets
testCompile "org.eclipse.jetty:jetty-servlet:${jetty_version}" testCompile "org.eclipse.jetty:jetty-servlet:${jetty_version}"
@ -206,17 +177,6 @@ task integrationTest(type: Test) {
classpath = sourceSets.integrationTest.runtimeClasspath classpath = sourceSets.integrationTest.runtimeClasspath
} }
task smokeTestJar(type: Jar) {
classifier 'smokeTests'
from sourceSets.smokeTest.output
}
task smokeTest(type: Test) {
dependsOn smokeTestJar
testClassesDirs = sourceSets.smokeTest.output.classesDirs
classpath = sourceSets.smokeTest.runtimeClasspath
}
jar { jar {
baseName 'corda-node' baseName 'corda-node'
} }

View File

@ -7,29 +7,24 @@ import net.corda.core.internal.div
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.NodeStartup import net.corda.node.internal.NodeStartup
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.nodeapi.internal.ServiceType
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.ProjectStructure.projectRootDir import net.corda.testing.ProjectStructure.projectRootDir
import net.corda.testing.driver.ListenProcessDeathException
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.io.* import java.io.*
import java.nio.file.Files import java.nio.file.Files
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class BootTests { class BootTests {
@Test @Test
fun `java deserialization is disabled`() { fun `java deserialization is disabled`() {
driver { driver {
val user = User("u", "p", setOf(startFlowPermission<ObjectInputStreamFlow>())) val user = User("u", "p", setOf(startFlow<ObjectInputStreamFlow>()))
val future = startNode(rpcUsers = listOf(user)).getOrThrow().rpcClientToNode(). val future = startNode(rpcUsers = listOf(user)).getOrThrow().rpcClientToNode().
start(user.username, user.password).proxy.startFlow(::ObjectInputStreamFlow).returnValue start(user.username, user.password).proxy.startFlow(::ObjectInputStreamFlow).returnValue
assertThatThrownBy { future.getOrThrow() }.isInstanceOf(InvalidClassException::class.java).hasMessage("filter status: REJECTED") assertThatThrownBy { future.getOrThrow() }.isInstanceOf(InvalidClassException::class.java).hasMessage("filter status: REJECTED")
@ -53,16 +48,6 @@ class BootTests {
assertEquals(1, numberOfNodesThatLogged) assertEquals(1, numberOfNodesThatLogged)
} }
} }
@Ignore("Need rewriting to produce too big network map registration (adverticed services trick doesn't work after services removal).")
@Test
fun `node quits on failure to register with network map`() {
val tooManyAdvertisedServices = (1..100).map { ServiceInfo(ServiceType.notary.getSubType("$it")) }.toSet()
driver {
val future = startNode(providedName = ALICE.name)
assertFailsWith(ListenProcessDeathException::class) { future.getOrThrow() }
}
}
} }
@StartableByRPC @StartableByRPC

View File

@ -7,7 +7,7 @@ import net.corda.core.internal.concurrent.transpose
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.BOB import net.corda.testing.BOB
@ -19,7 +19,7 @@ import org.junit.Test
class CordappScanningDriverTest { class CordappScanningDriverTest {
@Test @Test
fun `sub-classed initiated flow pointing to the same initiating flow as its super-class`() { fun `sub-classed initiated flow pointing to the same initiating flow as its super-class`() {
val user = User("u", "p", setOf(startFlowPermission<ReceiveFlow>())) val user = User("u", "p", setOf(startFlow<ReceiveFlow>()))
// The driver will automatically pick up the annotated flows below // The driver will automatically pick up the annotated flows below
driver { driver {
val (alice, bob) = listOf( val (alice, bob) = listOf(

View File

@ -12,12 +12,12 @@ import net.corda.core.utilities.minutes
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.chooseIdentity
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.node.NotarySpec
import net.corda.testing.performance.div import net.corda.testing.performance.div
import net.corda.testing.performance.startPublishingFixedRateInjector import net.corda.testing.performance.startPublishingFixedRateInjector
import net.corda.testing.performance.startReporter import net.corda.testing.performance.startReporter
@ -59,7 +59,7 @@ class NodePerformanceTests {
@Test @Test
fun `empty flow per second`() { fun `empty flow per second`() {
driver(startNodesInProcess = true) { driver(startNodesInProcess = true) {
val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlowPermission<EmptyFlow>())))).get() val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow<EmptyFlow>())))).get()
a.rpcClientToNode().use("A", "A") { connection -> a.rpcClientToNode().use("A", "A") { connection ->
val timings = Collections.synchronizedList(ArrayList<Long>()) val timings = Collections.synchronizedList(ArrayList<Long>())
@ -89,7 +89,7 @@ class NodePerformanceTests {
@Test @Test
fun `empty flow rate`() { fun `empty flow rate`() {
driver(startNodesInProcess = true) { driver(startNodesInProcess = true) {
val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlowPermission<EmptyFlow>())))).get() val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow<EmptyFlow>())))).get()
a as NodeHandle.InProcess a as NodeHandle.InProcess
val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics) val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics)
a.rpcClientToNode().use("A", "A") { connection -> a.rpcClientToNode().use("A", "A") { connection ->
@ -102,26 +102,25 @@ class NodePerformanceTests {
@Test @Test
fun `self pay rate`() { fun `self pay rate`() {
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) { val user = User("A", "A", setOf(startFlow<CashIssueFlow>(), startFlow<CashPaymentFlow>()))
val a = startNotaryNode( driver(
DUMMY_NOTARY.name, notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, rpcUsers = listOf(user))),
rpcUsers = listOf(User("A", "A", setOf(startFlowPermission<CashIssueFlow>(), startFlowPermission<CashPaymentFlow>()))) startNodesInProcess = true,
).getOrThrow() extraCordappPackagesToScan = listOf("net.corda.finance")
a as NodeHandle.InProcess ) {
val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics) val notary = defaultNotaryNode.getOrThrow() as NodeHandle.InProcess
a.rpcClientToNode().use("A", "A") { connection -> val metricRegistry = startReporter(shutdownManager, notary.node.services.monitoringService.metrics)
val notary = connection.proxy.notaryIdentities().first() notary.rpcClientToNode().use("A", "A") { connection ->
println("ISSUING") println("ISSUING")
val doneFutures = (1..100).toList().parallelStream().map { val doneFutures = (1..100).toList().parallelStream().map {
connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), notary).returnValue connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue
}.toList() }.toList()
doneFutures.transpose().get() doneFutures.transpose().get()
println("STARTING PAYMENT") println("STARTING PAYMENT")
startPublishingFixedRateInjector(metricRegistry, 8, 5.minutes, 100L / TimeUnit.SECONDS) { startPublishingFixedRateInjector(metricRegistry, 8, 5.minutes, 100L / TimeUnit.SECONDS) {
connection.proxy.startFlow(::CashPaymentFlow, 1.DOLLARS, a.nodeInfo.chooseIdentity()).returnValue.get() connection.proxy.startFlow(::CashPaymentFlow, 1.DOLLARS, defaultNotaryIdentity).returnValue.get()
} }
} }
} }
} }
} }

View File

@ -13,14 +13,13 @@ import net.corda.core.internal.div
import net.corda.core.internal.toLedgerTransaction import net.corda.core.internal.toLedgerTransaction
import net.corda.core.serialization.SerializationFactory import net.corda.core.serialization.SerializationFactory
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.seconds
import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.node.internal.cordapp.CordappProviderImpl
import net.corda.nodeapi.User import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.* import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.driver.DriverDSLExposedInterface import net.corda.testing.driver.DriverDSLExposedInterface
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
@ -53,20 +52,15 @@ class AttachmentLoadingTests {
val bankAName = CordaX500Name("BankA", "Zurich", "CH") val bankAName = CordaX500Name("BankA", "Zurich", "CH")
val bankBName = CordaX500Name("BankB", "Zurich", "CH") val bankBName = CordaX500Name("BankB", "Zurich", "CH")
val notaryName = CordaX500Name("Notary", "Zurich", "CH") val flowInitiatorClass: Class<out FlowLogic<*>> =
val flowInitiatorClass =
Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR))) Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR)))
.asSubclass(FlowLogic::class.java) .asSubclass(FlowLogic::class.java)
private fun DriverDSLExposedInterface.createTwoNodesAndNotary(): List<NodeHandle> { private fun DriverDSLExposedInterface.createTwoNodes(): List<NodeHandle> {
val adminUser = User("admin", "admin", permissions = setOf("ALL")) return listOf(
val nodes = listOf( startNode(providedName = bankAName),
startNode(providedName = bankAName, rpcUsers = listOf(adminUser)), startNode(providedName = bankBName)
startNode(providedName = bankBName, rpcUsers = listOf(adminUser)), ).transpose().getOrThrow()
startNotaryNode(providedName = notaryName, rpcUsers = listOf(adminUser), validating = false)
).transpose().getOrThrow() // Wait for all nodes to start up.
nodes.forEach { it.rpc.waitUntilNetworkReady().getOrThrow() }
return nodes
} }
private fun DriverDSLExposedInterface.installIsolatedCordappTo(nodeName: CordaX500Name) { private fun DriverDSLExposedInterface.installIsolatedCordappTo(nodeName: CordaX500Name) {
@ -79,15 +73,6 @@ class AttachmentLoadingTests {
} }
} }
} }
// Due to cluster instability after nodes been started it may take some time to all the nodes to become available
// *and* discover each other to reliably communicate. Hence, eventual nature of the test.
// TODO: Remove this method and usages of it once NetworkMap service been re-worked
private fun eventuallyPassingTest(block: () -> Unit) {
eventually<Throwable, Unit>(30.seconds) {
block()
}
}
} }
private lateinit var services: Services private lateinit var services: Services
@ -103,9 +88,8 @@ class AttachmentLoadingTests {
val contractClass = appClassLoader.loadClass(ISOLATED_CONTRACT_ID).asSubclass(Contract::class.java) val contractClass = appClassLoader.loadClass(ISOLATED_CONTRACT_ID).asSubclass(Contract::class.java)
val generateInitialMethod = contractClass.getDeclaredMethod("generateInitial", PartyAndReference::class.java, Integer.TYPE, Party::class.java) val generateInitialMethod = contractClass.getDeclaredMethod("generateInitial", PartyAndReference::class.java, Integer.TYPE, Party::class.java)
val contract = contractClass.newInstance() val contract = contractClass.newInstance()
val txBuilder = generateInitialMethod.invoke(contract, PartyAndReference(DUMMY_BANK_A, OpaqueBytes(kotlin.ByteArray(1))), 1, DUMMY_NOTARY) as TransactionBuilder val txBuilder = generateInitialMethod.invoke(contract, DUMMY_BANK_A.ref(1), 1, DUMMY_NOTARY) as TransactionBuilder
val context = SerializationFactory.defaultFactory.defaultContext val context = SerializationFactory.defaultFactory.defaultContext.withClassLoader(appClassLoader)
.withClassLoader(appClassLoader)
val ledgerTx = txBuilder.toLedgerTransaction(services, context) val ledgerTx = txBuilder.toLedgerTransaction(services, context)
contract.verify(ledgerTx) contract.verify(ledgerTx)
@ -119,11 +103,9 @@ class AttachmentLoadingTests {
fun `test that attachments retrieved over the network are not used for code`() { fun `test that attachments retrieved over the network are not used for code`() {
driver(initialiseSerialization = false) { driver(initialiseSerialization = false) {
installIsolatedCordappTo(bankAName) installIsolatedCordappTo(bankAName)
val (bankA, bankB, _) = createTwoNodesAndNotary() val (bankA, bankB) = createTwoNodes()
eventuallyPassingTest { assertFailsWith<UnexpectedFlowEndException>("Party C=CH,L=Zurich,O=BankB rejected session request: Don't know net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator") {
assertFailsWith<UnexpectedFlowEndException>("Party C=CH,L=Zurich,O=BankB rejected session request: Don't know net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator") { bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
}
} }
} }
} }
@ -133,10 +115,8 @@ class AttachmentLoadingTests {
driver(initialiseSerialization = false) { driver(initialiseSerialization = false) {
installIsolatedCordappTo(bankAName) installIsolatedCordappTo(bankAName)
installIsolatedCordappTo(bankBName) installIsolatedCordappTo(bankBName)
val (bankA, bankB, _) = createTwoNodesAndNotary() val (bankA, bankB) = createTwoNodes()
eventuallyPassingTest { bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
}
} }
} }
} }

View File

@ -13,6 +13,8 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.deleteIfExists import net.corda.core.internal.deleteIfExists
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.NotaryService
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
@ -21,15 +23,16 @@ import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.config.BFTSMaRtConfiguration
import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minClusterSize
import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.services.transactions.minCorrectReplicas
import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.common.internal.NetworkParametersCopier
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.dummyCommand import net.corda.testing.dummyCommand
import net.corda.testing.getDefaultNotary
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode
import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.MockNodeParameters
import org.junit.After import org.junit.After
import org.junit.Test import org.junit.Test
@ -38,39 +41,47 @@ import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
class BFTNotaryServiceTests { class BFTNotaryServiceTests {
companion object {
private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH")
}
private val mockNet = MockNetwork() private val mockNet = MockNetwork()
private val node = mockNet.createNode() private lateinit var notary: Party
private lateinit var node: StartedNode<MockNode>
@After @After
fun stopNodes() { fun stopNodes() {
mockNet.stopNodes() mockNet.stopNodes()
} }
private fun bftNotaryCluster(clusterSize: Int, exposeRaces: Boolean = false) { private fun startBftClusterAndNode(clusterSize: Int, exposeRaces: Boolean = false) {
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists? (Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
val replicaIds = (0 until clusterSize) val replicaIds = (0 until clusterSize)
ServiceIdentityGenerator.generateToDisk(
notary = ServiceIdentityGenerator.generateToDisk(
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) }, replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
clusterName) CordaX500Name("BFT", "Zurich", "CH"),
NotaryService.constructId(validating = false, bft = true))
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notary, false))))
val clusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) } val clusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) }
replicaIds.forEach { replicaId ->
mockNet.createNode(MockNodeParameters(configOverrides = { val nodes = replicaIds.map { replicaId ->
mockNet.createUnstartedNode(MockNodeParameters(configOverrides = {
val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces)) val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces))
doReturn(notary).whenever(it).notary doReturn(notary).whenever(it).notary
})) }))
} } + mockNet.createUnstartedNode()
mockNet.runNetwork() // Exchange initial network map registration messages.
// MockNetwork doesn't support BFT clusters, so we create all the nodes we need unstarted, and then install the
// network-parameters in their directories before they're started.
node = nodes.map { node ->
networkParameters.install(mockNet.baseDirectory(node.id))
node.start()
}.last()
} }
/** Failure mode is the redundant replica gets stuck in startup, so we can't dispose it cleanly at the end. */ /** Failure mode is the redundant replica gets stuck in startup, so we can't dispose it cleanly at the end. */
@Test @Test
fun `all replicas start even if there is a new consensus during startup`() { fun `all replicas start even if there is a new consensus during startup`() {
bftNotaryCluster(minClusterSize(1), true) // This true adds a sleep to expose the race. startBftClusterAndNode(minClusterSize(1), exposeRaces = true) // This true adds a sleep to expose the race.
val notary = node.services.getDefaultNotary()
val f = node.run { val f = node.run {
val trivialTx = signInitialTransaction(notary) { val trivialTx = signInitialTransaction(notary) {
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint) addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
@ -94,8 +105,7 @@ class BFTNotaryServiceTests {
private fun detectDoubleSpend(faultyReplicas: Int) { private fun detectDoubleSpend(faultyReplicas: Int) {
val clusterSize = minClusterSize(faultyReplicas) val clusterSize = minClusterSize(faultyReplicas)
bftNotaryCluster(clusterSize) startBftClusterAndNode(clusterSize)
val notary = node.services.getDefaultNotary()
node.run { node.run {
val issueTx = signInitialTransaction(notary) { val issueTx = signInitialTransaction(notary) {
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint) addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
@ -138,15 +148,13 @@ class BFTNotaryServiceTests {
} }
} }
} }
}
private fun StartedNode<*>.signInitialTransaction( private fun StartedNode<*>.signInitialTransaction(notary: Party, block: TransactionBuilder.() -> Any?): SignedTransaction {
notary: Party, return services.signInitialTransaction(
block: TransactionBuilder.() -> Any? TransactionBuilder(notary).apply {
): SignedTransaction { addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey))
return services.signInitialTransaction( block()
TransactionBuilder(notary).apply { }
addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey)) )
block() }
})
} }

View File

@ -11,65 +11,70 @@ import net.corda.core.utilities.getOrThrow
import net.corda.finance.POUNDS import net.corda.finance.POUNDS
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.node.DriverBasedTest import net.corda.testing.node.ClusterSpec
import net.corda.testing.node.NotarySpec
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
import rx.Observable import rx.Observable
import java.util.* import java.util.*
import kotlin.test.assertEquals
class DistributedServiceTests : DriverBasedTest() { class DistributedServiceTests {
lateinit var alice: NodeHandle private lateinit var alice: NodeHandle
lateinit var notaries: List<NodeHandle.OutOfProcess> private lateinit var notaryNodes: List<NodeHandle.OutOfProcess>
lateinit var aliceProxy: CordaRPCOps private lateinit var aliceProxy: CordaRPCOps
lateinit var raftNotaryIdentity: Party private lateinit var raftNotaryIdentity: Party
lateinit var notaryStateMachines: Observable<Pair<Party, StateMachineUpdate>> private lateinit var notaryStateMachines: Observable<Pair<Party, StateMachineUpdate>>
override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) { private fun setup(testBlock: () -> Unit) {
// Start Alice and 3 notaries in a RAFT cluster
val clusterSize = 3
val testUser = User("test", "test", permissions = setOf( val testUser = User("test", "test", permissions = setOf(
startFlowPermission<CashIssueFlow>(), startFlow<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>()) startFlow<CashPaymentFlow>(),
) invokeRpc(CordaRPCOps::nodeInfo),
val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)) invokeRpc(CordaRPCOps::stateMachinesFeed))
val notariesFuture = startNotaryCluster(
DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.id),
rpcUsers = listOf(testUser),
clusterSize = clusterSize
) )
alice = aliceFuture.get() driver(
val (notaryIdentity, notaryNodes) = notariesFuture.get() extraCordappPackagesToScan = listOf("net.corda.finance.contracts"),
raftNotaryIdentity = notaryIdentity notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, rpcUsers = listOf(testUser), cluster = ClusterSpec.Raft(clusterSize = 3))))
notaries = notaryNodes.map { it as NodeHandle.OutOfProcess } {
alice = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)).getOrThrow()
raftNotaryIdentity = defaultNotaryIdentity
notaryNodes = defaultNotaryHandle.nodeHandles.getOrThrow().map { it as NodeHandle.OutOfProcess }
assertEquals(notaries.size, clusterSize) assertThat(notaryNodes).hasSize(3)
// Check that each notary has different identity as a node.
assertEquals(notaries.size, notaries.map { it.nodeInfo.chooseIdentity() }.toSet().size) for (notaryNode in notaryNodes) {
// Connect to Alice and the notaries assertThat(notaryNode.nodeInfo.legalIdentities).contains(raftNotaryIdentity)
fun connectRpc(node: NodeHandle): CordaRPCOps { }
val client = node.rpcClientToNode()
return client.start("test", "test").proxy // 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 // TODO Use a dummy distributed service rather than a Raft Notary Service as this test is only about Artemis' ability
// to handle distributed services // to handle distributed services
@Test @Test
fun `requests are distributed evenly amongst the nodes`() { fun `requests are distributed evenly amongst the nodes`() = setup {
// Issue 100 pounds, then pay ourselves 50x2 pounds // Issue 100 pounds, then pay ourselves 50x2 pounds
issueCash(100.POUNDS) issueCash(100.POUNDS)
@ -97,7 +102,7 @@ class DistributedServiceTests : DriverBasedTest() {
// TODO This should be in RaftNotaryServiceTests // TODO This should be in RaftNotaryServiceTests
@Test @Test
fun `cluster survives if a notary is killed`() { fun `cluster survives if a notary is killed`() = setup {
// Issue 100 pounds, then pay ourselves 10x5 pounds // Issue 100 pounds, then pay ourselves 10x5 pounds
issueCash(100.POUNDS) issueCash(100.POUNDS)
@ -105,8 +110,8 @@ class DistributedServiceTests : DriverBasedTest() {
paySelf(5.POUNDS) paySelf(5.POUNDS)
} }
// Now kill a notary // Now kill a notary node
with(notaries[0].process) { with(notaryNodes[0].process) {
destroy() destroy()
waitFor() waitFor()
} }
@ -137,4 +142,4 @@ class DistributedServiceTests : DriverBasedTest() {
private fun paySelf(amount: Amount<Currency>) { private fun paySelf(amount: Amount<Currency>) {
aliceProxy.startFlow(::CashPaymentFlow, amount, alice.nodeInfo.chooseIdentity()).returnValue.getOrThrow() aliceProxy.startFlow(::CashPaymentFlow, amount, alice.nodeInfo.chooseIdentity()).returnValue.getOrThrow()
} }
} }

View File

@ -18,6 +18,8 @@ import net.corda.testing.contracts.DummyContract
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.dummyCommand import net.corda.testing.dummyCommand
import net.corda.testing.node.ClusterSpec
import net.corda.testing.node.NotarySpec
import org.junit.Test import org.junit.Test
import java.util.* import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -28,13 +30,16 @@ class RaftNotaryServiceTests {
@Test @Test
fun `detect double spend`() { fun `detect double spend`() {
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) { driver(
val (notaryParty) = startNotaryCluster(notaryName, 3).getOrThrow() startNodesInProcess = true,
extraCordappPackagesToScan = listOf("net.corda.testing.contracts"),
notarySpecs = listOf(NotarySpec(notaryName, cluster = ClusterSpec.Raft(clusterSize = 3))))
{
val bankA = startNode(providedName = DUMMY_BANK_A.name).map { (it as NodeHandle.InProcess).node }.getOrThrow() val bankA = startNode(providedName = DUMMY_BANK_A.name).map { (it as NodeHandle.InProcess).node }.getOrThrow()
val inputState = issueState(bankA, notaryParty) val inputState = issueState(bankA, defaultNotaryIdentity)
val firstTxBuilder = TransactionBuilder(notaryParty) val firstTxBuilder = TransactionBuilder(defaultNotaryIdentity)
.addInputState(inputState) .addInputState(inputState)
.addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey)) .addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey))
val firstSpendTx = bankA.services.signInitialTransaction(firstTxBuilder) val firstSpendTx = bankA.services.signInitialTransaction(firstTxBuilder)
@ -42,7 +47,7 @@ class RaftNotaryServiceTests {
val firstSpend = bankA.services.startFlow(NotaryFlow.Client(firstSpendTx)) val firstSpend = bankA.services.startFlow(NotaryFlow.Client(firstSpendTx))
firstSpend.resultFuture.getOrThrow() firstSpend.resultFuture.getOrThrow()
val secondSpendBuilder = TransactionBuilder(notaryParty).withItems(inputState).run { val secondSpendBuilder = TransactionBuilder(defaultNotaryIdentity).withItems(inputState).run {
val dummyState = DummyContract.SingleOwnerState(0, bankA.info.chooseIdentity()) val dummyState = DummyContract.SingleOwnerState(0, bankA.info.chooseIdentity())
addOutputState(dummyState, DummyContract.PROGRAM_ID) addOutputState(dummyState, DummyContract.PROGRAM_ID)
addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey)) addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey))

View File

@ -6,15 +6,11 @@ import net.corda.cordform.CordformNode
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.KeyManagementService
import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.nodeapi.NodeInfoFilesCopier import net.corda.nodeapi.NodeInfoFilesCopier
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.ALICE_KEY import net.corda.testing.ALICE_KEY
import net.corda.testing.DEV_TRUST_ROOT
import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.getTestPartyAndCertificate
import net.corda.testing.internal.NodeBasedTest import net.corda.testing.internal.NodeBasedTest
import net.corda.testing.node.MockKeyManagementService
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.contentOf import org.assertj.core.api.Assertions.contentOf
import org.junit.Before import org.junit.Before
@ -31,7 +27,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
val nodeInfo = NodeInfo(listOf(), listOf(getTestPartyAndCertificate(ALICE)), 0, 0) val nodeInfo = NodeInfo(listOf(), listOf(getTestPartyAndCertificate(ALICE)), 0, 0)
} }
private lateinit var keyManagementService: KeyManagementService
private lateinit var nodeInfoPath: Path private lateinit var nodeInfoPath: Path
private val scheduler = TestScheduler() private val scheduler = TestScheduler()
private val testSubscriber = TestSubscriber<NodeInfo>() private val testSubscriber = TestSubscriber<NodeInfo>()
@ -41,16 +36,15 @@ class NodeInfoWatcherTest : NodeBasedTest() {
@Before @Before
fun start() { fun start() {
val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
keyManagementService = MockKeyManagementService(identityService, ALICE_KEY)
nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler = scheduler) nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler = scheduler)
nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY
} }
@Test @Test
fun `save a NodeInfo`() { fun `save a NodeInfo`() {
assertEquals(0, tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.size) assertEquals(0,
NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, keyManagementService) tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.size)
NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, ALICE_KEY)
val nodeInfoFiles = tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) } val nodeInfoFiles = tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }
assertEquals(1, nodeInfoFiles.size) assertEquals(1, nodeInfoFiles.size)
@ -65,7 +59,7 @@ class NodeInfoWatcherTest : NodeBasedTest() {
fun `save a NodeInfo to JimFs`() { fun `save a NodeInfo to JimFs`() {
val jimFs = Jimfs.newFileSystem(Configuration.unix()) val jimFs = Jimfs.newFileSystem(Configuration.unix())
val jimFolder = jimFs.getPath("/nodeInfo") val jimFolder = jimFs.getPath("/nodeInfo")
NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, keyManagementService) NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, ALICE_KEY)
} }
@Test @Test
@ -134,6 +128,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
// Write a nodeInfo under the right path. // Write a nodeInfo under the right path.
private fun createNodeInfoFileInPath(nodeInfo: NodeInfo) { private fun createNodeInfoFileInPath(nodeInfo: NodeInfo) {
NodeInfoWatcher.saveToFile(nodeInfoPath, nodeInfo, keyManagementService) NodeInfoWatcher.saveToFile(nodeInfoPath, nodeInfo, ALICE_KEY)
} }
} }

View File

@ -8,7 +8,7 @@ import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_REGULATOR
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.internal.NodeBasedTest import net.corda.testing.internal.NodeBasedTest
import org.junit.Before import org.junit.Before
@ -16,9 +16,9 @@ import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
class PersistentNetworkMapCacheTest : NodeBasedTest() { class PersistentNetworkMapCacheTest : NodeBasedTest() {
private val partiesList = listOf(DUMMY_NOTARY, ALICE, BOB) private val partiesList = listOf(DUMMY_REGULATOR, ALICE, BOB)
private val addressesMap = HashMap<CordaX500Name, NetworkHostAndPort>() private val addressesMap = HashMap<CordaX500Name, NetworkHostAndPort>()
private val infos: MutableSet<NodeInfo> = HashSet() private val infos = HashSet<NodeInfo>()
@Before @Before
fun start() { fun start() {
@ -37,8 +37,8 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() {
alice.database.transaction { alice.database.transaction {
val res = netCache.getNodeByLegalIdentity(alice.info.chooseIdentity()) val res = netCache.getNodeByLegalIdentity(alice.info.chooseIdentity())
assertEquals(alice.info, res) assertEquals(alice.info, res)
val res2 = netCache.getNodeByLegalName(DUMMY_NOTARY.name) val res2 = netCache.getNodeByLegalName(DUMMY_REGULATOR.name)
assertEquals(infos.singleOrNull { DUMMY_NOTARY.name in it.legalIdentitiesAndCerts.map { it.name } }, res2) assertEquals(infos.singleOrNull { DUMMY_REGULATOR.name in it.legalIdentities.map { it.name } }, res2)
} }
} }

View File

@ -8,7 +8,7 @@ import net.corda.core.messaging.startFlow
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.aliceBobAndNotary import net.corda.testing.aliceAndBob
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState import net.corda.testing.contracts.DummyState
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
@ -65,7 +65,7 @@ class LargeTransactionsTest {
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2) val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2)
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3) val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3)
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) { driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
val (alice, _, _) = aliceBobAndNotary() val (alice, _) = aliceAndBob()
alice.useRPC { alice.useRPC {
val hash1 = it.uploadAttachment(bigFile1.inputStream) val hash1 = it.uploadAttachment(bigFile1.inputStream)
val hash2 = it.uploadAttachment(bigFile2.inputStream) val hash2 = it.uploadAttachment(bigFile2.inputStream)

View File

@ -18,7 +18,6 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.cert.X509CertificateHolder
import org.junit.Test import org.junit.Test
import java.nio.file.Files import java.nio.file.Files

View File

@ -21,6 +21,8 @@ import net.corda.testing.chooseIdentity
import net.corda.testing.driver.DriverDSLExposedInterface import net.corda.testing.driver.DriverDSLExposedInterface
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.node.ClusterSpec
import net.corda.testing.node.NotarySpec
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
import java.util.* import java.util.*
@ -35,16 +37,14 @@ class P2PMessagingTest {
@Test @Test
fun `communicating with a distributed service which we're part of`() { fun `communicating with a distributed service which we're part of`() {
driver(startNodesInProcess = true) { startDriverWithDistributedService { distributedService ->
val distributedService = startDistributedService()
assertAllNodesAreUsed(distributedService, DISTRIBUTED_SERVICE_NAME, distributedService[0]) assertAllNodesAreUsed(distributedService, DISTRIBUTED_SERVICE_NAME, distributedService[0])
} }
} }
@Test @Test
fun `distributed service requests are retried if one of the nodes in the cluster goes down without sending a response`() { fun `distributed service requests are retried if one of the nodes in the cluster goes down without sending a response`() {
driver(startNodesInProcess = true) { startDriverWithDistributedService { distributedServiceNodes ->
val distributedServiceNodes = startDistributedService()
val alice = startAlice() val alice = startAlice()
val serviceAddress = alice.services.networkMapCache.run { val serviceAddress = alice.services.networkMapCache.run {
val notaryParty = notaryIdentities.randomOrNull()!! val notaryParty = notaryIdentities.randomOrNull()!!
@ -77,8 +77,7 @@ class P2PMessagingTest {
@Test @Test
fun `distributed service request retries are persisted across client node restarts`() { fun `distributed service request retries are persisted across client node restarts`() {
driver(startNodesInProcess = true) { startDriverWithDistributedService { distributedServiceNodes ->
val distributedServiceNodes = startDistributedService()
val alice = startAlice() val alice = startAlice()
val serviceAddress = alice.services.networkMapCache.run { val serviceAddress = alice.services.networkMapCache.run {
val notaryParty = notaryIdentities.randomOrNull()!! val notaryParty = notaryIdentities.randomOrNull()!!
@ -117,11 +116,10 @@ class P2PMessagingTest {
} }
} }
private fun DriverDSLExposedInterface.startDistributedService(): List<StartedNode<Node>> { private fun startDriverWithDistributedService(dsl: DriverDSLExposedInterface.(List<StartedNode<Node>>) -> Unit) {
return startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2) driver(startNodesInProcess = true, notarySpecs = listOf(NotarySpec(DISTRIBUTED_SERVICE_NAME, cluster = ClusterSpec.Raft(clusterSize = 2)))) {
.getOrThrow() dsl(defaultNotaryHandle.nodeHandles.getOrThrow().map { (it as NodeHandle.InProcess).node })
.second }
.map { (it as NodeHandle.InProcess).node }
} }
private fun DriverDSLExposedInterface.startAlice(): StartedNode<Node> { private fun DriverDSLExposedInterface.startAlice(): StartedNode<Node> {

View File

@ -7,7 +7,6 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.transpose
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState import net.corda.core.schemas.PersistentState
@ -18,14 +17,12 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.services.FlowPermissions import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.driver.DriverDSLExposedInterface
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.junit.Assume import org.junit.Assume.assumeFalse
import org.junit.Test import org.junit.Test
import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
import javax.persistence.Column import javax.persistence.Column
@ -35,30 +32,26 @@ import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
class NodeStatePersistenceTests { class NodeStatePersistenceTests {
@Test @Test
fun `persistent state survives node restart`() { fun `persistent state survives node restart`() {
// Temporary disable this test when executed on Windows. It is known to be sporadically failing. // Temporary disable this test when executed on Windows. It is known to be sporadically failing.
// More investigation is needed to establish why. // More investigation is needed to establish why.
Assume.assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win")) assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win"))
val user = User("mark", "dadada", setOf(FlowPermissions.startFlowPermission<SendMessageFlow>())) val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery")))
val message = Message("Hello world!") val message = Message("Hello world!")
driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) { driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
val (nodeName, notaryNodeHandle) = { val nodeName = {
val notaryNodeHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow()
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
ensureAcquainted(notaryNodeHandle, nodeHandle)
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name val nodeName = nodeHandle.nodeInfo.chooseIdentity().name
nodeHandle.rpcClientToNode().start(user.username, user.password).use { nodeHandle.rpcClientToNode().start(user.username, user.password).use {
it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow() it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow()
} }
nodeHandle.stop().getOrThrow() nodeHandle.stop()
nodeName to notaryNodeHandle nodeName
}() }()
val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow() val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow()
ensureAcquainted(notaryNodeHandle, nodeHandle)
nodeHandle.rpcClientToNode().start(user.username, user.password).use { nodeHandle.rpcClientToNode().start(user.username, user.password).use {
val page = it.proxy.vaultQuery(MessageState::class.java) val page = it.proxy.vaultQuery(MessageState::class.java)
val stateAndRef = page.states.singleOrNull() val stateAndRef = page.states.singleOrNull()
@ -68,10 +61,6 @@ class NodeStatePersistenceTests {
} }
} }
} }
private fun DriverDSLExposedInterface.ensureAcquainted(one: NodeHandle, another: NodeHandle) {
listOf(one.pollUntilKnowsAbout(another), another.pollUntilKnowsAbout(one)).transpose().getOrThrow()
}
} }
fun isQuasarAgentSpecified(): Boolean { fun isQuasarAgentSpecified(): Boolean {

Some files were not shown because too many files have changed in this diff Show More