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

View File

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

9
.idea/compiler.xml generated
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 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.

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

View File

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

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.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo
import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
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.transactions.LedgerTransaction
import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash
@ -12,7 +14,7 @@ import org.junit.Test
import java.time.Instant
import java.time.temporal.ChronoUnit
val TEST_TIMELOCK_ID = "net.corda.core.contracts.TransactionEncumbranceTests\$DummyTimeLock"
val TEST_TIMELOCK_ID = "net.corda.core.transactions.TransactionEncumbranceTests\$DummyTimeLock"
class TransactionEncumbranceTests {
val defaultIssuer = MEGA_CORP.ref(1)

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.CompositeKey
import net.corda.core.identity.Party
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract

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>`_
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
--------------

View File

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

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

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.
:minimumPlatformVersion: Used by the node if it's running the network map service to enforce a minimum version requirement
on registrations - any node on a Platform Version lower than this value will have their registration rejected.
Defaults to 1 if absent.
:useHTTPS: If false the node's web server will be plain HTTP. If true the node will use the same certificate and private
key from the ``<workspace>/certificates/sslkeystore.jks`` file as the ArtemisMQ port for HTTPS. If HTTPS is enabled
then unencrypted HTTP traffic to the node's **webAddress** port is not supported.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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
session continues on as normal.
:``internal.networkmap``:
This is another private queue just for the node which functions in a similar manner to the ``internal.peers.*`` queues
except this is used to form a connection to the network map node. The node running the network map service is treated
differently as it provides information about the rest of the network.
:``rpc.requests``:
RPC clients send their requests here, and it's only open for sending by clients authenticated as RPC users.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip

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.SerializationDefaults
import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
/*
* Serialisation contexts for the client.
* These have been refactored into a separate file to prevent
* servers from trying to instantiate any of them.
*/
val KRYO_RPC_CLIENT_CONTEXT = SerializationContextImpl(KryoHeaderV0_1,
SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(),
true,
SerializationContext.UseCase.RPCClient)
val AMQP_RPC_CLIENT_CONTEXT = SerializationContextImpl(AmqpHeaderV1_0,
SerializationDefaults.javaClass.classLoader,
GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()),
emptyMap(),
true,
SerializationContext.UseCase.RPCClient)

View File

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

View File

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

View File

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

View File

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

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.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.Serializer
@ -23,6 +23,10 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.NonEmptySet
import net.corda.core.utilities.toNonEmptySet
import net.corda.nodeapi.internal.serialization.CordaClassResolver
import net.corda.nodeapi.internal.serialization.DefaultWhitelist
import net.corda.nodeapi.internal.serialization.GeneratedAttachment
import net.corda.nodeapi.internal.serialization.MutableClassWhitelist
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.bouncycastle.asn1.x500.X500Name

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

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.KryoException

View File

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

View File

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

View File

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

View File

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

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.Envelope
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
import net.corda.testing.amqpSpecific
import net.corda.testing.kryoSpecific
import net.corda.testing.SerializationEnvironmentRule

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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