Merge remote-tracking branch 'open/master' into shams-os-merge-250118

# Conflicts:
#	client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt
#	client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt
#	core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java
#	docs/source/changelog.rst
#	docs/source/corda-configuration-file.rst
#	docs/source/upgrade-notes.rst
#	finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt
#	finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt
#	gradle/wrapper/gradle-wrapper.properties
#	node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt
#	node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/SetsSerializationTest.kt
#	node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt
#	node/src/integration-test/kotlin/net/corda/node/BootTests.kt
#	node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt
#	node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt
#	node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt
#	node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt
#	node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt
#	node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt
#	node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt
#	node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt
#	node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt
#	node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt
#	node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt
#	node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt
#	node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt
#	node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
#	node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt
#	node/src/main/resources/reference.conf
#	node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java
#	node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt
#	node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt
#	node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt
#	node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt
#	samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt
#	samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt
#	samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt
#	samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt
#	testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt
#	testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt
#	testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt
#	tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt
#	verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt
#	webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt
This commit is contained in:
Shams Asari 2018-01-25 17:51:13 +00:00
commit 4b86362391
314 changed files with 4394 additions and 3169 deletions

View File

@ -1,15 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Explorer - demo nodes" type="JetRunConfigurationType" factoryName="Kotlin" singleton="true">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.corda.explorer.MainKt" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="ALTERNATIVE_JRE_PATH" value="1.8" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="explorer_main" />
<envs />
<method />
</configuration>
</component>

View File

@ -1,15 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Explorer - demo nodes (simulation)" type="JetRunConfigurationType" factoryName="Kotlin" singleton="true">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.corda.explorer.MainKt" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="-S" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="ALTERNATIVE_JRE_PATH" value="1.8" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="explorer_main" />
<envs />
<method />
</configuration>
</component>

View File

@ -58,7 +58,7 @@ buildscript {
ext.h2_version = '1.4.194' // Update docs if renamed or removed.
ext.postgresql_version = '42.1.4'
ext.rxjava_version = '1.2.4'
ext.dokka_version = '0.9.14'
ext.dokka_version = '0.9.16-eap-2'
ext.eddsa_version = '0.2.0'
ext.dependency_checker_version = '3.0.1'
ext.commons_collections_version = '4.1'
@ -79,6 +79,9 @@ buildscript {
mavenLocal()
mavenCentral()
jcenter()
maven {
url 'https://dl.bintray.com/kotlin/kotlin-eap/'
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

View File

@ -10,8 +10,11 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.node.ServiceHub
import net.corda.core.transactions.SignedTransaction
import net.corda.finance.USD
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.rigorousMock
import org.junit.Before
import org.junit.Rule

View File

@ -28,7 +28,7 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.testing.*
import net.corda.testing.core.*
import net.corda.testing.driver.driver
import net.corda.testing.node.User
import net.corda.testing.internal.IntegrationTest
@ -85,7 +85,7 @@ class NodeMonitorModelTest : IntegrationTest() {
vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed()
networkMapUpdates = monitor.networkMap.bufferUntilSubscribed()
monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
monitor.register(aliceNodeHandle.configuration.rpcOptions.address!!, cashUser.username, cashUser.password)
rpc = monitor.proxyObservable.value!!
notaryParty = defaultNotaryIdentity
@ -93,7 +93,7 @@ class NodeMonitorModelTest : IntegrationTest() {
bobNode = bobNodeHandle.nodeInfo
val monitorBob = NodeMonitorModel()
stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed()
monitorBob.register(bobNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
monitorBob.register(bobNodeHandle.configuration.rpcOptions.address!!, cashUser.username, cashUser.password)
rpcBob = monitorBob.proxyObservable.value!!
runTest()
}

View File

@ -10,7 +10,7 @@ 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.testing.CoreTestUtils;
import net.corda.testing.core.TestUtils;
import net.corda.testing.node.User;
import net.corda.testing.internal.IntegrationTestKt;
import net.corda.testing.internal.IntegrationTestSchemas;
@ -31,7 +31,7 @@ import static net.corda.finance.Currencies.DOLLARS;
import static net.corda.finance.contracts.GetBalances.getCashBalance;
import static net.corda.node.services.Permissions.invokeRpc;
import static net.corda.node.services.Permissions.startFlow;
import static net.corda.testing.TestConstants.ALICE_NAME;
import static net.corda.testing.core.TestConstants.ALICE_NAME;
public class CordaRPCJavaClientTest extends NodeBasedTest {
public CordaRPCJavaClientTest() {
@ -64,7 +64,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
public void setUp() throws Exception {
super.setUp();
node = startNode(ALICE_NAME, 1, singletonList(rpcUser));
client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress()));
client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcOptions().getAddress()));
}
@After
@ -83,7 +83,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
FlowHandle<AbstractCashFlow.Result> flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
DOLLARS(123), OpaqueBytes.of((byte)0),
CoreTestUtils.chooseIdentity(node.getInfo()));
TestUtils.chooseIdentity(node.getInfo()));
System.out.println("Started issuing cash, waiting on result");
flowHandle.getReturnValue().get();

View File

@ -8,6 +8,7 @@ import net.corda.core.messaging.startFlow
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.getOrThrow
import net.corda.testing.*
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas

View File

@ -20,7 +20,7 @@ import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.testing.*
import net.corda.testing.core.*
import net.corda.testing.node.User
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
@ -61,7 +61,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
override fun setUp() {
super.setUp()
node = startNode(ALICE_NAME, rpcUsers = listOf(rpcUser))
client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
client = CordaRPCClient(node.internals.configuration.rpcOptions.address!!)
identity = node.info.identityFromX500Name(ALICE_NAME)
}

View File

@ -17,6 +17,8 @@ import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.testThreadFactory
import net.corda.testing.internal.toDatabaseSchemaNames
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.internal.*
import net.corda.testing.node.internal.*
import org.apache.activemq.artemis.api.core.SimpleString
import org.junit.After

View File

@ -10,6 +10,7 @@ import net.corda.core.serialization.internal.effectiveSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
import java.time.Duration
@ -67,10 +68,12 @@ data class CordaRPCClientConfiguration(val connectionMaxRetryInterval: Duration)
*
* @param hostAndPort The network address to connect to.
* @param configuration An optional configuration used to tweak client behaviour.
* @param sslConfiguration An optional [SSLConfiguration] used to enable secure communication with the server.
*/
class CordaRPCClient @JvmOverloads constructor(
hostAndPort: NetworkHostAndPort,
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
sslConfiguration: SSLConfiguration? = null
) {
init {
try {
@ -85,7 +88,7 @@ class CordaRPCClient @JvmOverloads constructor(
}
private val rpcClient = RPCClient<CordaRPCOps>(
tcpTransport(ConnectionDirection.Outbound(), hostAndPort, config = null),
tcpTransport(ConnectionDirection.Outbound(), hostAndPort, config = sslConfiguration),
configuration.toRpcClientConfiguration(),
KRYO_RPC_CLIENT_CONTEXT
)

View File

@ -47,6 +47,7 @@ public class StandaloneCordaRPCJavaClientTest {
port.getAndIncrement(),
port.getAndIncrement(),
port.getAndIncrement(),
port.getAndIncrement(),
true,
Collections.singletonList(rpcUser),
true,

View File

@ -60,6 +60,7 @@ class StandaloneCordaRPClientTest {
legalName = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
rpcAdminPort = port.andIncrement,
webPort = port.andIncrement,
isNotary = true,
users = listOf(user)

View File

@ -5,7 +5,7 @@ import net.corda.core.internal.concurrent.flatMap
import net.corda.core.internal.concurrent.map
import net.corda.core.messaging.RPCOps
import net.corda.node.services.messaging.RPCServerConfiguration
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.node.User
import net.corda.testing.node.internal.RPCDriverDSL
import net.corda.testing.node.internal.rpcTestUser

View File

@ -5,7 +5,7 @@ import net.corda.core.concurrent.CordaFuture
import net.corda.core.internal.concurrent.openFuture
import net.corda.core.messaging.*
import net.corda.core.utilities.getOrThrow
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.node.internal.rpcDriver
import net.corda.testing.node.internal.startRpcClient
import org.assertj.core.api.Assertions.assertThatThrownBy

View File

@ -14,7 +14,10 @@ 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.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME
import net.corda.testing.core.singleIdentity
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.startFlow
import org.junit.After

View File

@ -2,7 +2,7 @@ package net.corda.confidential
import net.corda.core.identity.*
import net.corda.core.utilities.getOrThrow
import net.corda.testing.*
import net.corda.testing.core.*
import net.corda.testing.node.MockNetwork
import org.junit.Before
import net.corda.testing.node.startFlow

View File

@ -1,4 +1,4 @@
gradlePluginsVersion=3.0.3
gradlePluginsVersion=3.0.4
kotlinVersion=1.1.60
platformVersion=3
guavaVersion=21.0

View File

@ -497,6 +497,7 @@ object Crypto {
* It returns true if it succeeds, but it always throws an exception if verification fails.
* Strategy on identifying the actual signing scheme is based on the [PublicKey] type, but if the schemeCodeName is known,
* then better use doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray).
*
* @param publicKey the signer's [PublicKey].
* @param signatureData the signatureData on a message.
* @param clearData the clear data/message that was signed (usually the Merkle root).

View File

@ -244,7 +244,9 @@ abstract class SignTransactionFlow(val otherSideSession: FlowSession,
}
@Suspendable private fun checkSignatures(stx: SignedTransaction) {
val signingWellKnownIdentities = groupPublicKeysByWellKnownParty(serviceHub, stx.sigs.map(TransactionSignature::by))
// We set `ignoreUnrecognisedParties` to `true` in `groupPublicKeysByWellKnownParty`. This is because we don't
// need to recognise all keys, but just the initiator's.
val signingWellKnownIdentities = groupPublicKeysByWellKnownParty(serviceHub, stx.sigs.map(TransactionSignature::by), true)
require(otherSideSession.counterparty in signingWellKnownIdentities) {
"The Initiator of CollectSignaturesFlow must have signed the transaction. Found ${signingWellKnownIdentities}, expected ${otherSideSession}"
}

View File

@ -1,6 +1,7 @@
package net.corda.core.identity
import net.corda.core.internal.CertRole
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.CordaSerializable
import java.security.PublicKey
import java.security.cert.*
@ -45,15 +46,16 @@ class PartyAndCertificate(val certPath: CertPath) {
// Apply Corda-specific validity rules to the chain. This only applies to chains with any roles present, so
// an all-null chain is in theory valid.
var parentRole: CertRole? = CertRole.extract(result.trustAnchor.trustedCert)
for (certIdx in (0 until certPath.certificates.size).reversed()) {
val certificate = certPath.certificates[certIdx]
val certChain: List<X509Certificate> = uncheckedCast(certPath.certificates)
for (certIdx in (0 until certChain.size).reversed()) {
val certificate = certChain[certIdx]
val role = CertRole.extract(certificate)
if (parentRole != null) {
if (role == null) {
throw CertPathValidatorException("Child certificate whose issuer includes a Corda role, must also specify Corda role")
}
if (!role.isValidParent(parentRole)) {
val certificateString = (certificate as? X509Certificate)?.subjectDN?.toString() ?: certificate.toString()
val certificateString = certificate.subjectDN.toString()
throw CertPathValidatorException("The issuing certificate for $certificateString has role $parentRole, expected one of ${role.validParents}")
}
}

View File

@ -1,12 +1,12 @@
package net.corda.core.internal
import net.corda.core.CordaOID
import net.corda.core.utilities.NonEmptySet
import org.bouncycastle.asn1.ASN1Encodable
import org.bouncycastle.asn1.ASN1Integer
import org.bouncycastle.asn1.ASN1Primitive
import org.bouncycastle.asn1.DEROctetString
import java.math.BigInteger
import java.security.cert.Certificate
import java.security.cert.X509Certificate
/**
@ -23,40 +23,38 @@ import java.security.cert.X509Certificate
// NOTE: The order of the entries in the enum MUST NOT be changed, as their ordinality is used as an identifier. Please
// also note that IDs are numbered from 1 upwards, matching numbering of other enum types in ASN.1 specifications.
// TODO: Link to the specification once it has a permanent URL
enum class CertRole(val validParents: Set<CertRole?>, val isIdentity: Boolean, val isWellKnown: Boolean) : ASN1Encodable {
enum class CertRole(val validParents: NonEmptySet<CertRole?>, val isIdentity: Boolean, val isWellKnown: Boolean) : ASN1Encodable {
/**
* Intermediate CA (Doorman service).
*/
INTERMEDIATE_CA(setOf(null), false, false),
INTERMEDIATE_CA(NonEmptySet.of(null), false, false),
/** Signing certificate for the network map. */
NETWORK_MAP(setOf(null), false, false),
NETWORK_MAP(NonEmptySet.of(null), false, false),
/** Well known (publicly visible) identity of a service (such as notary). */
SERVICE_IDENTITY(setOf(INTERMEDIATE_CA), true, true),
SERVICE_IDENTITY(NonEmptySet.of(INTERMEDIATE_CA), true, true),
/** Node level CA from which the TLS and well known identity certificates are issued. */
NODE_CA(setOf(INTERMEDIATE_CA), false, false),
NODE_CA(NonEmptySet.of(INTERMEDIATE_CA), false, false),
/** Transport layer security certificate for a node. */
TLS(setOf(NODE_CA), false, false),
TLS(NonEmptySet.of(NODE_CA), false, false),
/** Well known (publicly visible) identity of a legal entity. */
LEGAL_IDENTITY(setOf(INTERMEDIATE_CA, NODE_CA), true, true),
LEGAL_IDENTITY(NonEmptySet.of(INTERMEDIATE_CA, NODE_CA), true, true),
/** Confidential (limited visibility) identity of a legal entity. */
CONFIDENTIAL_LEGAL_IDENTITY(setOf(LEGAL_IDENTITY), true, false);
CONFIDENTIAL_LEGAL_IDENTITY(NonEmptySet.of(LEGAL_IDENTITY), true, false);
companion object {
private var cachedRoles: Array<CertRole>? = null
private val values by lazy(LazyThreadSafetyMode.NONE, CertRole::values)
/**
* Get a role from its ASN.1 encoded form.
*
* @throws IllegalArgumentException if the encoded data is not a valid role.
*/
fun getInstance(id: ASN1Integer): CertRole {
if (cachedRoles == null) {
cachedRoles = CertRole.values()
}
val idVal = id.value
require(idVal.compareTo(BigInteger.ZERO) > 0) { "Invalid role ID" }
require(idVal > BigInteger.ZERO) { "Invalid role ID" }
return try {
val ordinal = idVal.intValueExact() - 1
cachedRoles!![ordinal]
values[ordinal]
} catch (ex: ArithmeticException) {
throw IllegalArgumentException("Invalid role ID")
} catch (ex: ArrayIndexOutOfBoundsException) {
@ -71,21 +69,6 @@ enum class CertRole(val validParents: Set<CertRole?>, val isIdentity: Boolean, v
*/
fun getInstance(data: ByteArray): CertRole = getInstance(ASN1Integer.getInstance(data))
/**
* Get a role from a certificate.
*
* @return the role if the extension is present, or null otherwise.
* @throws IllegalArgumentException if the extension is present but is invalid.
*/
fun extract(cert: Certificate): CertRole? {
val x509Cert = cert as? X509Certificate
return if (x509Cert != null) {
extract(x509Cert)
} else {
null
}
}
/**
* Get a role from a certificate.
*
@ -93,12 +76,9 @@ enum class CertRole(val validParents: Set<CertRole?>, val isIdentity: Boolean, v
* @throws IllegalArgumentException if the extension is present but is invalid.
*/
fun extract(cert: X509Certificate): CertRole? {
val extensionData: ByteArray? = cert.getExtensionValue(CordaOID.X509_EXTENSION_CORDA_ROLE)
return if (extensionData != null) {
val extensionString = DEROctetString.getInstance(extensionData)
return cert.getExtensionValue(CordaOID.X509_EXTENSION_CORDA_ROLE)?.let {
val extensionString = DEROctetString.getInstance(it)
getInstance(extensionString.octets)
} else {
null
}
}
}

View File

@ -242,14 +242,10 @@ private fun IntProgression.toSpliterator(): Spliterator.OfInt {
}
fun IntProgression.stream(parallel: Boolean = false): IntStream = StreamSupport.intStream(toSpliterator(), parallel)
inline fun <reified T> Stream<out T>.toTypedArray() = toTypedArray(T::class.java)
// When toArray has filled in the array, the component type is no longer T? but T (that may itself be nullable):
fun <T> Stream<out T>.toTypedArray(componentType: Class<T>): Array<T> = toArray { size ->
uncheckedCast<Any, Array<T?>>(java.lang.reflect.Array.newInstance(componentType, size))
}
fun <T> Stream<out T?>.filterNotNull(): Stream<T> = uncheckedCast(filter(Objects::nonNull))
fun <K, V> Stream<out Pair<K, V>>.toMap(): Map<K, V> = collect<LinkedHashMap<K, V>>(::LinkedHashMap, { m, (k, v) -> m.put(k, v) }, { m, t -> m.putAll(t) })
// When toArray has filled in the array, the component type is no longer T? but T (that may itself be nullable):
inline fun <reified T> Stream<out T>.toTypedArray(): Array<T> = uncheckedCast(toArray { size -> arrayOfNulls<T>(size) })
fun <T> Class<T>.castIfPossible(obj: Any): T? = if (isInstance(obj)) cast(obj) else null
/** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [Class]. */
@ -258,13 +254,12 @@ fun <T> Class<*>.staticField(name: String): DeclaredField<T> = DeclaredField(thi
/** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [KClass]. */
fun <T> KClass<*>.staticField(name: String): DeclaredField<T> = DeclaredField(java, name, null)
/** @suppress Returns a [DeclaredField] wrapper around the declared (possibly non-public) instance field of the receiver object. */
/** Returns a [DeclaredField] wrapper around the declared (possibly non-public) instance field of the receiver object. */
fun <T> Any.declaredField(name: String): DeclaredField<T> = DeclaredField(javaClass, name, this)
/**
* Returns a [DeclaredField] wrapper around the (possibly non-public) instance field of the receiver object, but declared
* in its superclass [clazz].
* @suppress
*/
fun <T> Any.declaredField(clazz: KClass<*>, name: String): DeclaredField<T> = DeclaredField(clazz.java, name, this)
@ -299,18 +294,12 @@ fun <T, U : T> uncheckedCast(obj: T) = obj as U
fun <K, V> Iterable<Pair<K, V>>.toMultiMap(): Map<K, List<V>> = this.groupBy({ it.first }) { it.second }
/**
* Provide access to internal method for AttachmentClassLoaderTests
* @suppress
*/
/** Provide access to internal method for AttachmentClassLoaderTests */
fun TransactionBuilder.toWireTransaction(cordappProvider: CordappProvider, serializationContext: SerializationContext): WireTransaction {
return toWireTransactionWithContext(cordappProvider, serializationContext)
}
/**
* Provide access to internal method for AttachmentClassLoaderTests
* @suppress
*/
/** Provide access to internal method for AttachmentClassLoaderTests */
fun TransactionBuilder.toLedgerTransaction(services: ServicesForResolution, serializationContext: SerializationContext) = toLedgerTransactionWithContext(services, serializationContext)
/** Convenience method to get the package name of a class literal. */

View File

@ -354,4 +354,19 @@ interface ServiceHub : ServicesForResolution {
* @return A new [Connection]
*/
fun jdbcSession(): Connection
/**
* Allows the registration of a callback that may inform services when the app is shutting down.
*
* The intent is to allow the cleaning up of resources - e.g. releasing ports.
*
* You should not rely on this to clean up executing flows - that's what quasar is for.
*
* Please note that the shutdown handler is not guaranteed to be called. In production the node process may crash,
* be killed by the operating system and other forms of fatal termination may occur that result in this code never
* running. So you should use this functionality only for unit/integration testing or for code that can optimise
* this shutdown e.g. by cleaning up things that would otherwise trigger a slow recovery process next time the
* node starts.
*/
fun registerUnloadHandler(runOnStop: () -> Unit)
}

View File

@ -37,6 +37,7 @@ class NodeVersioningTest {
legalName = CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
rpcAdminPort = port.andIncrement,
webPort = port.andIncrement,
isNotary = false,
users = listOf(user)

View File

@ -33,6 +33,7 @@ class CordappSmokeTest {
legalName = CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
rpcAdminPort = port.andIncrement,
webPort = port.andIncrement,
isNotary = false,
users = listOf(user)

View File

@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable;
import com.google.common.primitives.Primitives;
import net.corda.core.identity.Party;
import net.corda.node.internal.StartedNode;
import net.corda.testing.TestConstants;
import net.corda.testing.core.TestConstants;
import net.corda.testing.node.MockNetwork;
import org.junit.After;
import org.junit.Before;
@ -14,7 +14,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import static java.util.Collections.emptyList;
import static net.corda.testing.CoreTestUtils.singleIdentity;
import static net.corda.testing.core.TestUtils.singleIdentity;
import static net.corda.testing.node.NodeTestUtils.startFlow;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.Assert.fail;

View File

@ -2,7 +2,7 @@ package net.corda.core.flows;
import net.corda.core.serialization.SerializationDefaults;
import net.corda.core.serialization.SerializationFactory;
import net.corda.testing.SerializationEnvironmentRule;
import net.corda.testing.core.SerializationEnvironmentRule;
import org.junit.Rule;
import org.junit.Test;

View File

@ -3,7 +3,7 @@ package net.corda.core.contracts
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.testing.TestIdentity
import net.corda.testing.core.TestIdentity
import org.assertj.core.api.Assertions
import org.junit.Test
import org.junit.runner.RunWith

View File

@ -6,9 +6,12 @@ import net.corda.core.cordapp.CordappProvider
import net.corda.core.crypto.SecureHash
import net.corda.core.internal.UpgradeCommand
import net.corda.core.node.ServicesForResolution
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyContractV2
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.rigorousMock
import org.junit.Rule
import org.junit.Test

View File

@ -6,9 +6,13 @@ import net.corda.core.internal.div
import net.corda.core.serialization.serialize
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.toBase58String
import net.corda.nodeapi.internal.crypto.*
import net.corda.testing.SerializationEnvironmentRule
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.loadKeyStore
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.internal.kryoSpecific
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
@ -341,9 +345,9 @@ class CompositeKeyTests {
// Store certificate to keystore.
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
val keystore = loadOrCreateKeyStore(keystorePath, "password")
keystore.setCertificateEntry("CompositeKey", compositeKeyCert)
keystore.save(keystorePath, "password")
X509KeyStore.fromFile(keystorePath, "password", createNew = true).update {
setCertificate("CompositeKey", compositeKeyCert)
}
// Load keystore from disk.
val keystore2 = loadKeyStore(keystorePath, "password")
@ -352,7 +356,7 @@ class CompositeKeyTests {
val key = keystore2.getCertificate("CompositeKey").publicKey
// Convert sun public key to Composite key.
val compositeKey2 = Crypto.toSupportedPublicKey(key)
assertTrue { compositeKey2 is CompositeKey }
assertThat(compositeKey2).isInstanceOf(CompositeKey::class.java)
// Run the same composite key test again.
assertTrue { compositeKey2.isFulfilledBy(signatures.byKeys()) }

View File

@ -13,7 +13,10 @@ import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash
import net.corda.node.services.api.IdentityServiceInternal
import net.corda.testing.*
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TEST_TX_TIME
import net.corda.testing.core.TestIdentity
import net.corda.testing.dsl.LedgerDSL
import net.corda.testing.dsl.TestLedgerDSLInterpreter
import net.corda.testing.dsl.TestTransactionDSLInterpreter

View File

@ -2,7 +2,7 @@ package net.corda.core.crypto
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.serialize
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Before
import org.junit.Rule
import org.junit.Test

View File

@ -1,6 +1,6 @@
package net.corda.core.crypto
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Rule
import org.junit.Test
import java.security.SignatureException

View File

@ -1,7 +1,9 @@
package net.corda.core.crypto
import net.corda.core.identity.CordaX500Name
import net.corda.nodeapi.internal.crypto.*
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.GeneralName
@ -9,7 +11,6 @@ import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.Test
import java.security.KeyStore
import java.security.cert.CertPathValidator
import java.security.cert.CertPathValidatorException
import java.security.cert.PKIXParameters
@ -19,37 +20,32 @@ import kotlin.test.assertTrue
class X509NameConstraintsTest {
private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<KeyStore, KeyStore> {
private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<X509KeyStore, X509KeyStore> {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val nodeCaCert = X509Utilities.createCertificate(
CertificateType.NODE_CA,
intermediateCa.certificate,
intermediateCa.keyPair,
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal,
nodeCaKeyPair.public,
nameConstraints = nameConstraints)
val keyPass = "password"
val trustStore = KeyStore.getInstance(KEYSTORE_TYPE)
trustStore.load(null, keyPass.toCharArray())
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
val trustStore = X509KeyStore("password").apply {
setCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
}
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val tlsCert = X509Utilities.createCertificate(
CertificateType.TLS,
nodeCaCert,
nodeCaKeyPair,
X500Principal(subjectName.encoded),
tlsKeyPair.public)
val keyStore = X509KeyStore("password").apply {
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val nodeCaCert = X509Utilities.createCertificate(
CertificateType.NODE_CA,
intermediateCa.certificate,
intermediateCa.keyPair,
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal,
nodeCaKeyPair.public,
nameConstraints = nameConstraints)
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val tlsCert = X509Utilities.createCertificate(
CertificateType.TLS,
nodeCaCert,
nodeCaKeyPair,
X500Principal(subjectName.encoded),
tlsKeyPair.public)
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCa.certificate))
}
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
keyStore.load(null, keyPass.toCharArray())
keyStore.addOrReplaceKey(
X509Utilities.CORDA_CLIENT_TLS,
tlsKeyPair.private,
keyPass.toCharArray(),
arrayOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCa.certificate))
return Pair(keyStore, trustStore)
}
@ -60,30 +56,29 @@ class X509NameConstraintsTest {
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
val pathValidator = CertPathValidator.getInstance("PKIX")
val certFactory = X509CertificateFactory()
assertFailsWith(CertPathValidatorException::class) {
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank B"), nameConstraints)
val params = PKIXParameters(trustStore)
val params = PKIXParameters(trustStore.internal)
params.isRevocationEnabled = false
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
pathValidator.validate(certPath, params)
}
assertTrue {
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A TLS, O=Bank A"), nameConstraints)
val params = PKIXParameters(trustStore)
val params = PKIXParameters(trustStore.internal)
params.isRevocationEnabled = false
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
pathValidator.validate(certPath, params)
true
}
assertTrue {
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A"), nameConstraints)
val params = PKIXParameters(trustStore)
val params = PKIXParameters(trustStore.internal)
params.isRevocationEnabled = false
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
pathValidator.validate(certPath, params)
true
}
@ -95,40 +90,39 @@ class X509NameConstraintsTest {
.map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray()
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
val certFactory = X509CertificateFactory().delegate
Crypto.ECDSA_SECP256R1_SHA256
val pathValidator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME)
assertFailsWith(CertPathValidatorException::class) {
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A"), nameConstraints)
val params = PKIXParameters(trustStore)
val params = PKIXParameters(trustStore.internal)
params.isRevocationEnabled = false
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
pathValidator.validate(certPath, params)
}
assertFailsWith(CertPathValidatorException::class) {
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A, UID=12345"), nameConstraints)
val params = PKIXParameters(trustStore)
val params = PKIXParameters(trustStore.internal)
params.isRevocationEnabled = false
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
pathValidator.validate(certPath, params)
}
assertTrue {
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A TLS, UID=, E=me@email.com, C=GB"), nameConstraints)
val params = PKIXParameters(trustStore)
val params = PKIXParameters(trustStore.internal)
params.isRevocationEnabled = false
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
pathValidator.validate(certPath, params)
true
}
assertTrue {
val (keystore, trustStore) = makeKeyStores(X500Name("O=Bank A, UID=, E=me@email.com, C=GB"), nameConstraints)
val params = PKIXParameters(trustStore)
val params = PKIXParameters(trustStore.internal)
params.isRevocationEnabled = false
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
pathValidator.validate(certPath, params)
true
}

View File

@ -10,7 +10,9 @@ import net.corda.core.internal.FetchDataFlow
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode
import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.testing.*
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.singleIdentity
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNodeParameters
import net.corda.testing.node.startFlow

View File

@ -12,8 +12,8 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.*
import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockServices

View File

@ -19,8 +19,8 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.node.internal.SecureCordaRPCOps
import net.corda.node.internal.StartedNode
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB_NAME
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.node.User
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyContractV2
@ -29,7 +29,7 @@ import net.corda.testing.node.internal.rpcDriver
import net.corda.testing.node.internal.rpcTestUser
import net.corda.testing.node.internal.startRpcClient
import net.corda.testing.node.MockNetwork
import net.corda.testing.singleIdentity
import net.corda.testing.core.singleIdentity
import net.corda.testing.node.startFlow
import org.junit.After
import org.junit.Before

View File

@ -7,7 +7,7 @@ import net.corda.finance.POUNDS
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.issuedBy
import net.corda.node.services.api.StartedNodeServices
import net.corda.testing.*
import net.corda.testing.core.*
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.startFlow
import org.junit.After

View File

@ -6,7 +6,7 @@ import net.corda.core.utilities.UntrustworthyData
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap
import net.corda.testing.node.MockNetwork
import net.corda.testing.singleIdentity
import net.corda.testing.core.singleIdentity
import net.corda.testing.node.startFlow
import org.assertj.core.api.Assertions.assertThat
import org.junit.After

View File

@ -1,21 +1,19 @@
package net.corda.core.identity
import com.google.common.jimfs.Configuration.unix
import com.google.common.jimfs.Jimfs
import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.internal.read
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.save
import net.corda.testing.DEV_ROOT_CA
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.getTestPartyAndCertificate
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.testing.core.DEV_ROOT_CA
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.getTestPartyAndCertificate
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import java.io.File
import java.math.BigInteger
import java.security.KeyStore
import kotlin.test.assertFailsWith
class PartyAndCertificateTest {
@ -25,7 +23,7 @@ class PartyAndCertificateTest {
@Test
fun `reject a path with no roles`() {
val path = X509CertificateFactory().generateCertPath(DEV_ROOT_CA.certificate)
val path = X509Utilities.buildCertPath(DEV_ROOT_CA.certificate)
assertFailsWith<IllegalArgumentException> { PartyAndCertificate(path) }
}
@ -46,17 +44,18 @@ class PartyAndCertificateTest {
CordaX500Name(organisation = "Test Corp", locality = "Madrid", country = "ES"),
entropyToKeyPair(BigInteger.valueOf(83)).public))
val original = identity.certificate
val alias = identity.name.toString()
val storePassword = "test"
val keyStoreFilePath = File.createTempFile("serialization_test", "jks").toPath()
var keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
keyStore.load(null, storePassword.toCharArray())
keyStore.setCertificateEntry(identity.name.toString(), original)
keyStore.save(keyStoreFilePath, storePassword)
Jimfs.newFileSystem(unix()).use {
val keyStoreFile = it.getPath("/serialization_test.jks")
// Load the key store back in again
keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
keyStoreFilePath.read { keyStore.load(it, storePassword.toCharArray()) }
val copy = keyStore.getCertificate(identity.name.toString())
assertThat(copy).isEqualTo(original) // .isNotSameAs(original)
X509KeyStore.fromFile(keyStoreFile, storePassword, createNew = true).update {
setCertificate(alias, original)
}
// Load the key store back in again
val copy = X509KeyStore.fromFile(keyStoreFile, storePassword).getCertificate(alias)
assertThat(copy).isEqualTo(original)
}
}
}

View File

@ -1,7 +1,7 @@
package net.corda.core.identity
import net.corda.core.crypto.entropyToKeyPair
import net.corda.testing.ALICE_NAME
import net.corda.testing.core.ALICE_NAME
import org.junit.Test
import java.math.BigInteger
import kotlin.test.assertEquals

View File

@ -1,7 +1,7 @@
package net.corda.core.internal
import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB_NAME
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After
import org.junit.AfterClass

View File

@ -3,7 +3,6 @@ package net.corda.core.internal
import org.assertj.core.api.Assertions
import org.junit.Assert.assertArrayEquals
import org.junit.Test
import java.io.Serializable
import java.util.stream.IntStream
import java.util.stream.Stream
import kotlin.test.assertEquals
@ -88,17 +87,5 @@ class InternalUtilsTest {
val b: Array<String?> = Stream.of("one", "two", null).toTypedArray()
assertEquals(Array<String?>::class.java, b.javaClass)
assertArrayEquals(arrayOf("one", "two", null), b)
val c: Array<CharSequence> = Stream.of("x", "y").toTypedArray(CharSequence::class.java)
assertEquals(Array<CharSequence>::class.java, c.javaClass)
assertArrayEquals(arrayOf("x", "y"), c)
val d: Array<CharSequence?> = Stream.of("x", "y", null).toTypedArray(uncheckedCast(CharSequence::class.java))
assertEquals(Array<CharSequence?>::class.java, d.javaClass)
assertArrayEquals(arrayOf("x", "y", null), d)
}
@Test
fun `Stream of Pairs toMap works`() {
val m: Map<Comparable<*>, Serializable> = Stream.of<Pair<Comparable<*>, Serializable>>("x" to "y", 0 to 1, "x" to '2').toMap()
assertEquals<Map<*, *>>(mapOf("x" to '2', 0 to 1), m)
}
}

View File

@ -11,7 +11,7 @@ import net.corda.core.utilities.sequence
import net.corda.node.internal.StartedNode
import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.MockNetwork
import net.corda.testing.singleIdentity
import net.corda.testing.core.singleIdentity
import net.corda.testing.node.startFlow
import org.junit.After
import org.junit.Before

View File

@ -5,8 +5,8 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.node.services.Vault
import net.corda.core.transactions.LedgerTransaction
import net.corda.testing.DUMMY_NOTARY_NAME
import net.corda.testing.TestIdentity
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.TestIdentity
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

View File

@ -16,11 +16,11 @@ import net.corda.node.internal.InitiatedFlowFactory
import net.corda.node.internal.StartedNode
import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.nodeapi.internal.persistence.currentDBSession
import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB_NAME
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNodeParameters
import net.corda.testing.singleIdentity
import net.corda.testing.core.singleIdentity
import net.corda.testing.node.startFlow
import org.junit.After
import org.junit.Before

View File

@ -2,7 +2,7 @@ package net.corda.core.serialization
import net.corda.finance.contracts.CommercialPaper
import net.corda.finance.contracts.asset.Cash
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals

View File

@ -8,7 +8,7 @@ import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.seconds
import net.corda.finance.POUNDS
import net.corda.testing.*
import net.corda.testing.core.*
import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices
import org.junit.Before

View File

@ -7,7 +7,7 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.node.services.UniquenessException
import net.corda.core.node.services.UniquenessProvider
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals

View File

@ -5,9 +5,9 @@ import net.corda.core.contracts.ComponentGroupEnum.*
import net.corda.core.crypto.*
import net.corda.core.serialization.serialize
import net.corda.core.utilities.OpaqueBytes
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState
import net.corda.testing.core.*
import org.junit.Rule
import org.junit.Test
import java.time.Instant
@ -287,7 +287,7 @@ class CompatibleTransactionTests {
@Test
fun `Command visibility tests`() {
// 1st and 3rd commands require a signature from KEY_1.
val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public))
val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public))
val componentGroups = listOf(
inputGroup,
outputGroup,
@ -409,7 +409,7 @@ class CompatibleTransactionTests {
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))
val twoCommandsforKey1 = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_2.public), dummyCommand(DUMMY_KEY_1.public))
val componentGroups = listOf(
inputGroup,
outputGroup,

View File

@ -8,8 +8,8 @@ import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.node.services.api.IdentityServiceInternal
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.*
import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices
import org.junit.Before

View File

@ -11,7 +11,9 @@ import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash
import net.corda.node.services.api.IdentityServiceInternal
import net.corda.testing.*
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger

View File

@ -4,8 +4,8 @@ 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.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.*
import net.corda.testing.internal.rigorousMock
import org.junit.Rule
import org.junit.Test

View File

@ -7,7 +7,7 @@ import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.serialization.KRYO_CHECKPOINT_CONTEXT
import net.corda.nodeapi.internal.serialization.KRYO_P2P_CONTEXT
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test

View File

@ -7,7 +7,7 @@ import com.google.common.collect.testing.features.CollectionSize
import junit.framework.TestSuite
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Rule

View File

@ -5,48 +5,57 @@ dependencies {
compile rootProject
}
def internalPackagePrefixes(sourceDirs) {
def prefixes = []
// Kotlin allows packages to deviate from the directory structure, but let's assume they don't:
sourceDirs.collect { sourceDir ->
sourceDir.traverse(type: groovy.io.FileType.DIRECTORIES) {
if (it.name == 'internal') {
prefixes.add sourceDir.toPath().relativize(it.toPath()).toString().replace(File.separator, '.')
}
}
}
prefixes
}
ext {
// TODO: Add '../client/jfx/src/main/kotlin' and '../client/mock/src/main/kotlin' if we decide to make them into public API
dokkaSourceDirs = files('../core/src/main/kotlin', '../client/rpc/src/main/kotlin', '../finance/src/main/kotlin', '../client/jackson/src/main/kotlin',
'../testing/test-utils/src/main/kotlin', '../testing/node-driver/src/main/kotlin')
internalPackagePrefixes = internalPackagePrefixes(dokkaSourceDirs)
}
dokka {
moduleName = 'corda'
outputDirectory = file("${rootProject.rootDir}/docs/build/html/api/kotlin")
processConfigurations = ['compile']
sourceDirs = dokkaSourceDirs
includes = ['packages.md']
jdkVersion = 8
externalDocumentationLink {
url = new URL("http://fasterxml.github.io/jackson-core/javadoc/2.8/")
}
externalDocumentationLink {
url = new URL("https://docs.oracle.com/javafx/2/api/")
}
externalDocumentationLink {
url = new URL("http://www.bouncycastle.org/docs/docs1.5on/")
}
}
task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
moduleName = 'corda'
outputFormat = "javadoc"
outputDirectory = file("${rootProject.rootDir}/docs/build/html/api/javadoc")
processConfigurations = ['compile']
sourceDirs = dokkaSourceDirs
includes = ['packages.md']
jdkVersion = 8
}
externalDocumentationLink {
url = new URL("http://fasterxml.github.io/jackson-core/javadoc/2.8/")
}
externalDocumentationLink {
url = new URL("https://docs.oracle.com/javafx/2/api/")
}
externalDocumentationLink {
url = new URL("http://www.bouncycastle.org/docs/docs1.5on/")
[dokka, dokkaJavadoc].collect {
it.configure {
moduleName = 'corda'
processConfigurations = ['compile']
sourceDirs = dokkaSourceDirs
includes = ['packages.md']
jdkVersion = 8
externalDocumentationLink {
url = new URL("http://fasterxml.github.io/jackson-core/javadoc/2.8/")
}
externalDocumentationLink {
url = new URL("https://docs.oracle.com/javafx/2/api/")
}
externalDocumentationLink {
url = new URL("http://www.bouncycastle.org/docs/docs1.5on/")
}
internalPackagePrefixes.collect { packagePrefix ->
packageOptions {
prefix = packagePrefix
suppress = true
}
}
}
}

View File

@ -23,7 +23,7 @@ is complete.
# Package net.corda.core.contracts
This package contains the base data types for smarts contracts implemented in Corda. To implement a new contract start
with [Contract], or see the examples in [net.corda.finance.contracts].
with [Contract], or see the examples in `net.corda.finance.contracts`.
Corda smart contracts are a combination of state held on the distributed ledger, and verification logic which defines
which transformations of state are valid.

View File

@ -1,67 +0,0 @@
CLI vs IDE
==========
We have tried to make every example, tutorial and sample usable via both the command line and the IntelliJ IDE.
Most developers will find writing, editing and debugging code more easy with tools such as an IDE. But when a production node
is deployed, it will be controlled via the command line - no organisation allows their systems to be running via
a developer environment.
IDE - IntelliJ
--------------
IntelliJ (the preferred IDE for Corda) integrates well with gradle (Corda's default build, deployment and CLI tool).
IntelliJ understands gradle tasks and dependencies, automatically loading them in the background when a project is
first opened or the gradle project changes. Occasionally, however, you may need to refresh the gradle project manually
- but this is hinted to you by the IDE. It's a good idea to do this before carrying on with other work (and in fact you
may find it is essential to pick up new libraries, etc.).
There are some great resources about how to get started using IntelliJ. As opposed to trying to repeat them here, we advise
you to go to the `IntelliJ docs here <https://www.jetbrains.com/idea/documentation/>`_.
Command Line
------------
Windows vs Mac / Unix
*********************
Due to the nature of their respective command interfaces, gradle is typically ran in windows with the command ``gradle.bat``
(or ``gradlew.bat`` if using the wrapper) and in Mac / Unix environments it is ran via ``./gradlew``. For brevity, the
simple windows syntax ``gradle`` is used for the majority of the documentation.
As well as including the most significant run and build configurations in the IDE, we also provide gradle tasks to build, install
and run significant parts of Corda demos and tools. Gradle is highly extensible and we use it for downloading required resources,
building components, installing those built components into shared areas, configuring the scripts that run nodes, starting
up demonstration API calls amongst other things. It is exceptionally good at deriving dependency maps and therefore performing
the preceding tasks required in order to do the requested task. However, when confusing build errors manifest, then sometimes
a ``gradle clean`` may be required in order to clear out any build areas that have an inconsistent state. The total build time
from downloading / cloning the repo to a complete build should be only a few minutes, obviously slightly longer if the
unit tests are run.
Frequently Used Gradle Tasks
****************************
Note that the list of tasks can be ran for any gradle project can be displayed by running the task ``tasks``. Also, note that
gradle is hierarchical and therefore tasks in child directories can be run using a colon separator. For example, if you want to run
the sample attachment demo run configuration ``runSender``, you would use the command ``gradle samples:attachment-demo:runSender``
The most frequent gradle tasks you will probably be running are ``build`` and ``install``. The ``build`` command also executes the
unit tests as well. If you want to build without this level of verification, then use the ``assemble`` command - but we do
not recommend this. After running build, the ``install`` tasks copies over the built jars into the local maven repository
which will then make these available for either the sample code or use with the CorDapp template.
Debugging
---------
Tasks and processes that are run directly via the IDE (including via the usage of the ``driver`` DSL) can be remotely debugged.
We do not have java debugging currently enabled in the ``runnodes`` scripts generated by a process we refer to as 'cordformation'
but we will be implementing that shortly.
Via the IDE
***********
To debug: From the IDE, configure the debug connectivity option by the "Edit Configurations" and choosing "+" and then "Remote".
The debug port start at 5005 and increments for each additional node that starts, the order given by the list in the main
driver configuration (which is primarily listed in the ``main`` function of ``Main.kt`` for each sample. Look for the string
``Listening for transport dt_socket at address:5xxx`` in the log output to determine the exact port for that node. If the log
messages are mixed from several nodes to the same console, then (as earlier stated), the port numbers increment in the order
they are listed in the driver DSL configuration.

72
docs/source/aws-vm.rst Normal file
View File

@ -0,0 +1,72 @@
Building a Corda VM from the AWS Marketplace
============================================
To help you design, build and test applications on Corda, called CorDapps, a Corda network AMI can be deployed from the `AWS Marketplace <https://aws.amazon.com/marketplace/pp/B077PG9SP5>`_. Instructions on running Corda nodes can be found `here <https://docs.corda.net/deploying-a-node.html>`_.
This Corda network offering builds a pre-configured network of Corda nodes as Ubuntu virtual machines (VM). The network consists of a Notary node and three Corda nodes using version 1 of Corda. The following guide will also show you how to load one of four `Corda Sample apps <https://www.corda.net/samples>`_ 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
--------------
* Ensure you have a registered AWS account which can create virtual machines under your subscription(s) and you are logged on to the `AWS portal <https://console.aws.amazon.com>`_
* It is recommended you generate a private-public SSH key pair (see `here <https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys--2/>`_)
Deploying a Corda Network
---------------------------
Browse to the `AWS Marketplace <https://aws.amazon.com/marketplace>`_ and search for Corda.
Follow the instructions to deploy the AMI to an instance of EC2 which is in a region near to your location.
Build and Run a Sample CorDapp
------------------------------
Once the instance is running ssh into the instance using your keypair
.. sourcecode:: shell
cd ~/dev
There are 4 sample apps available by default
.. sourcecode:: shell
ubuntu@ip-xxx-xxx-xxx-xxx:~/dev$ ls -la
total 24
drwxrwxr-x 6 ubuntu ubuntu 4096 Nov 13 21:48 .
drwxr-xr-x 8 ubuntu ubuntu 4096 Nov 21 16:34 ..
drwxrwxr-x 11 ubuntu ubuntu 4096 Oct 31 19:02 cordapp-example
drwxrwxr-x 9 ubuntu ubuntu 4096 Nov 13 21:48 obligation-cordapp
drwxrwxr-x 11 ubuntu ubuntu 4096 Nov 13 21:48 oracle-example
drwxrwxr-x 8 ubuntu ubuntu 4096 Nov 13 21:48 yo-cordapp
cd into the Corda sample you would like to run. For example:
.. sourcecode:: shell
cd cordapp-example/
Follow instructions for the specific sample at https://www.corda.net/samples to build and run the Corda sample
For example: with cordapp-example (IOU app) the following commands would be run:
.. sourcecode:: shell
./gradlew deployNodes
./kotlin-source/build/nodes/runnodes
Then start the Corda webserver
.. sourcecode:: shell
find ~/dev/cordapp-example/kotlin-source/ -name corda-webserver.jar -execdir sh -c 'java -jar {} &' \;
You can now interact with your running CorDapp. See the instructions `here <https://docs.corda.net/tutorial-cordapp.html#via-http>`_
Next Steps
----------
Now you have built a Corda network and used a basic Corda Cordapp do go and visit the `dedicated Corda website <https://www.corda.net>`_
Additional support is available on `Stack Overflow <https://stackoverflow.com/questions/tagged/corda>`_ and the `Corda Slack channel <https://slack.corda.net/>`_.
You can build and run any other `Corda samples <https://www.corda.net/samples>`_ or your own custom CorDapp here.
Or to join the growing Corda community and get straight into the Corda open source codebase, head over to the `Github Corda repo <https://www.github.com/corda>`_

View File

@ -11,6 +11,7 @@ CorDapps
cordapp-build-systems
building-against-master
corda-api
secure-coding-guidelines
flow-cookbook
cheat-sheet
building-a-cordapp-samples

View File

@ -1,20 +1,19 @@
Building against Master
=======================
When developing a CorDapp, it is advisable to use the most recent Corda Milestone release, which has been extensively
tested. However, if you need to use a very recent feature of the codebase, you may need to work against the unstable
Master branch.
It is advisable to develop CorDapps against the most recent Corda stable release. However you may need to build
against the unstable Master branch if you are using a very recent feature, or are testing a PR on the main codebase.
To work against the Master branch, proceed as follows:
* Open a terminal window in the folder where you cloned the Corda repository
1. Open a terminal window in the folder where you cloned the Corda repository
(available `here <https://github.com/corda/corda>`_)
* Use the following command to check out the latest master branch:
2. Use the following command to check out the latest master branch:
``git fetch; git checkout master``
``git checkout master; git pull``
* Publish Corda to your local Maven repository using the following commands:
3. Publish Corda to your local Maven repository using the following commands:
* Unix/Mac OSX: ``./gradlew install``
* Windows: ``gradlew.bat install``
@ -24,11 +23,14 @@ To work against the Master branch, proceed as follows:
* ``~/.m2/repository`` on Unix/Mac OS X
* ``%HOMEPATH%\.m2`` on Windows
This step is not necessary when using a Milestone releases, as the Milestone releases are published online
This step is not necessary when using a stable releases, as the stable releases are published online
.. warning:: If you do modify your local Corda repository after having published it to Maven local, then you must
re-publish it to Maven local for the local installation to reflect the changes you have made.
.. warning:: If you do modify your local Corda repository after having published it to Maven local, then you must
re-publish it to Maven local for the local installation to reflect the changes you have made.
.. warning:: As the Corda repository evolves on a daily basis, two clones of the Master branch at different points in
time may differ. If you are using a Master release and need help debugging an error, then please let us know the
**commit** you are working from. This will help us ascertain the issue.
.. warning:: As the Corda repository evolves on a daily basis, two clones of the Master branch at different points in
time may differ. If you are using a Master release and need help debugging an error, then please let us know the
**commit** you are working from. This will help us ascertain the issue.
4. Update the ``ext.corda_release_version`` property in your CorDapp's root ``build.gradle`` file to match the version
here: https://github.com/corda/corda/blob/master/build.gradle#L7

View File

@ -6,6 +6,20 @@ from previous releases. Please refer to :doc:`upgrade-notes` for detailed instru
UNRELEASED
----------
* Separated our pre-existing Artemis broker into an RPC broker and a P2P broker.
* Refactored ``NodeConfiguration`` to expose ``NodeRpcOptions`` (using top-level "rpcAddress" property still works with warning).
* Modified ``CordaRPCClient`` constructor to take a ``SSLConfiguration?`` additional parameter, defaulted to ``null``.
* Introduced ``CertificateChainCheckPolicy.UsernameMustMatchCommonName`` sub-type, allowing customers to optionally enforce username == CN condition on RPC SSL certificates.
* Modified ``DriverDSL`` and sub-types to allow specifying RPC settings for the Node.
* Modified the ``DriverDSL`` to start Cordformation nodes allowing automatic generation of "rpcSettings.adminAddress" in case "rcpSettings.useSsl" is ``false`` (the default).
* Introduced ``UnsafeCertificatesFactory`` allowing programmatic generation of X509 certificates for test purposes.
* JPA Mapping annotations for States extending ``CommonSchemaV1.LinearState`` and ``CommonSchemaV1.FungibleState`` on the
`participants` collection need to be moved to the actual class. This allows to properly specify the unique table name per a collection.
See: DummyDealStateSchemaV1.PersistentDummyDealState
@ -168,6 +182,9 @@ R3 Corda 3.0 Developer Preview
However, assuming a clean reset of the artemis data and that the nodes are consistent versions,
data persisted via the AMQP serializer will be forward compatible.
* The ability for CordaServices to register callbacks so they can be notified of shutdown and clean up resource such as
open ports.
* Enterprise Corda only: Compatibility with SQL Server 2017 and SQL Azure databases.
* Enterprise Corda only: node configuration property ``database.schema`` and documented existing database properties.

View File

@ -249,6 +249,11 @@ Wire protocol
-------------
The client RPC wire protocol is defined and documented in ``net/corda/client/rpc/RPCApi.kt``.
Wire security
-------------
``CordaRPCClient`` has an optional constructor parameter of type ``SSLConfiguration``, defaulted to ``null``, which allows
communication with the node using SSL. Default ``null`` value means no SSL used in the context of RPC.
Whitelisting classes with the Corda node
----------------------------------------
CorDapps must whitelist any classes used over RPC with Corda's serialization framework, unless they are whitelisted by

View File

@ -0,0 +1,64 @@
Contributing
============
Corda is an open-source project and we welcome contributions. This guide explains how to contribute back to Corda.
.. contents::
Identifying an area to contribute
---------------------------------
There are several ways to identify an area where you can contribute to Corda:
* Browse issues labelled as ``HelpWanted`` on the
`Corda JIRA board <https://r3-cev.atlassian.net/issues/?jql=labels%20%3D%20HelpWanted>`_
* Any issue with a ``HelpWanted`` label is considered ideal for open-source contributions
* If there is a feature you would like to add and there isn't a corresponding issue labelled as ``HelpWanted``, that
doesn't mean your contribution isn't welcome. Please reach out on the Corda Slack channel (see below) to clarify
* Check the `Corda GitHub issues <https://github.com/corda/corda/issues>`_
* It's always worth checking in the Corda Slack channel (see below) whether a given issue is a good target for your
contribution. Someone else may already be working on it, or it may be blocked by an on-going piece of work
* Ask in the `Corda Slack channel <http://slack.corda.net/>`_
Making the required changes
---------------------------
1. Create a fork of the master branch of the `Corda repo <https://github.com/corda/corda>`_
2. Clone the fork to your local machine
3. Make the changes, in accordance with the :doc:`code style guide </codestyle>`
Testing the changes
-------------------
Running the tests
^^^^^^^^^^^^^^^^^
Your changes must pass the tests described :doc:`here </testing>`.
Building against the master branch
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You may also want to test your changes against a CorDapp defined outside of the Corda repo. To do so, please follow the
instructions :doc:`here </building-against-master>`.
Merging the changes back into Corda
-----------------------------------
1. Create a pull request from your fork to the master branch of the Corda repo
2. Complete the pull-request checklist in the comments box:
* State that you have run the tests
* State that you have included JavaDocs for any new public APIs
* State that you have included the change in the :doc:`changelog </changelog>` and
:doc:`release notes </release-notes>` where applicable
* State that you are in agreement with the terms of
`CONTRIBUTING.md <https://github.com/corda/corda/blob/master/CONTRIBUTING.md>`_
3. Request a review from a member of the Corda platform team via the `Corda Slack channel <http://slack.corda.net/>`_
4. Wait for your PR to pass all four types of continuous integration tests (integration, API stability, build and unit)
* Currently, external contributors cannot see the output of these tests. If your PR fails a test that passed
locally, ask the reviewer for further details
5. Once a reviewer has approved the PR and the tests have passed, squash-and-merge the PR as a single commit

View File

@ -47,6 +47,12 @@ The following modules have a stable API we commit not to break in following rele
* **Core (net.corda.core)**: core Corda libraries such as crypto functions, types for Corda's building blocks: states, contracts, transactions, attachments, etc. and some interfaces for nodes and protocols
* **Client RPC (net.corda.client.rpc)**: client RPC
* **Client Jackson (net.corda.client.jackson)**: JSON support for client applications
* **Test Utils (net.corda.testing.core)**: generic test utilities
* **Test Node Driver (net.corda.testing.node, net.corda.testing.driver)**: test utilities to run nodes programmatically
* **Http Test Utils (net.corda.testing.http)**: a small set of utilities for making HttpCalls, aimed at demos and tests.
* **DSL Test Utils (net.corda.testing.dsl)**: a simple DSL for building pseudo-transactions (not the same as the wire protocol) for testing purposes.
* **Dummy Contracts (net.corda.testing.contracts)**: dummy state and contracts for testing purposes
* **Mock Services (net.corda.testing.services)**: mock service implementations for testing purposes
Corda incubating modules
------------------------
@ -54,9 +60,7 @@ Corda incubating modules
The following modules don't yet have a completely stable API, but we will do our best to minimise disruption to
developers using them until we are able to graduate them into the public API:
* **net.corda.node.driver**: test utilities to run nodes programmatically
* **net.corda.confidential.identities**: experimental support for confidential identities on the ledger
* **net.corda.node.test.utils**: generic test utilities
* **net.corda.finance**: a range of elementary contracts (and associated schemas) and protocols, such as abstract fungible assets, cash, obligation and commercial paper
* **net.corda.client.jfx**: support for Java FX UI
* **net.corda.client.mock**: client mock utilities

View File

@ -38,7 +38,12 @@ Simple Notary configuration file.
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
p2pAddress : "localhost:12345"
rpcAddress : "localhost:12346"
rpcSettings = {
useSsl = false
standAloneBroker = false
address : "my-corda-node:10003"
adminAddress : "my-corda-node:10004"
}
webAddress : "localhost:12347"
notary : {
validating : false
@ -94,7 +99,21 @@ path to the node's base directory.
here must be externally accessible when running nodes across a cluster of machines. If the provided host is unreachable,
the node will try to auto-discover its public one.
:rpcAddress: The address of the RPC system on which RPC requests can be made to the node. If not provided then the node will run without RPC.
:rpcAddress: The address of the RPC system on which RPC requests can be made to the node. If not provided then the node will run without RPC. This is now deprecated in favour of the ``rpcSettings`` block.
:rpcSettings: Options for the RPC server.
:useSsl: (optional) boolean, indicates whether the node should require clients to use SSL for RPC connections, defaulted to ``false``.
:standAloneBroker: (optional) boolean, indicates whether the node will connect to a standalone broker for RPC, defaulted to ``false``.
:address: (optional) host and port for the RPC server binding, if any.
:adminAddress: (optional) host and port for the RPC admin binding (only required when ``useSsl`` is ``false``, because the node connects to Artemis using SSL to ensure admin privileges are not accessible outside the node).
:ssl: (optional) SSL settings for the RPC server.
:keyStorePassword: password for the key store.
:trustStorePassword: password for the trust store.
:certificatesDirectory: directory in which the stores will be searched, unless absolute paths are provided.
:sslKeystore: absolute path to the ssl key store, defaulted to ``certificatesDirectory / "sslkeystore.jks"``.
:trustStoreFile: absolute path to the trust store, defaulted to ``certificatesDirectory / "truststore.jks"``.
:security: Contains various nested fields controlling user authentication/authorization, in particular for RPC accesses. See
:doc:`clientrpc` for details.
@ -192,6 +211,15 @@ path to the node's base directory.
:useAMQPBridges: Optionally can be set to ``false`` to use Artemis CORE Bridges for peer-to-peer communications.
Otherwise, defaults to ``true`` and the AMQP 1.0 protocol will be used for message transfer between nodes.
:transactionCacheSizeMegaBytes: Optionally specify how much memory should be used for caching of ledger transactions in memory.
Otherwise defaults to 8MB plus 5% of all heap memory above 300MB.
:attachmentContentCacheSizeMegaBytes: Optionally specify how much memory should be used to cache attachment contents in memory.
Otherwise defaults to 10MB
:attachmentCacheBound: Optionally specify how many attachments should be cached locally. Note that this includes only the key and
metadata, the content is cached separately and can be loaded lazily. Defaults to 1024.
:graphiteOptions: Optionally export metrics to a graphite server. When specified, the node will push out all JMX
metrics to the specified Graphite server at regular intervals.

View File

@ -13,7 +13,7 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.testing.*
import net.corda.testing.core.*
import net.corda.testing.driver.driver
import net.corda.testing.node.User
import net.corda.testing.internal.IntegrationTest

View File

@ -17,7 +17,7 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.testing.ALICE_NAME
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.node.User
import net.corda.testing.driver.driver
import org.graphstream.graph.Edge

View File

@ -16,7 +16,7 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.unwrap
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.schemas.CashSchemaV1
import net.corda.testing.chooseIdentity
import net.corda.testing.core.chooseIdentity
import java.util.*
@CordaSerializable

View File

@ -10,7 +10,7 @@ import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.utils.sumCashBy
import net.corda.testing.chooseIdentityAndCert
import net.corda.testing.core.chooseIdentityAndCert
import java.time.Instant
import java.util.*

View File

@ -8,7 +8,12 @@ dataSourceProperties : {
"dataSource.password" : ""
}
p2pAddress : "my-corda-node:10002"
rpcAddress : "my-corda-node:10003"
rpcSettings = {
useSsl = false
standAloneBroker = false
address : "my-corda-node:10003"
adminAddress : "my-corda-node:10004"
}
webAddress : "localhost:10004"
rpcUsers : [
{ username=user1, password=letmein, permissions=[ StartFlow.net.corda.protocols.CashProtocol ] }

View File

@ -8,7 +8,7 @@ import net.corda.finance.contracts.ICommercialPaperState;
import net.corda.finance.contracts.JavaCommercialPaper;
import net.corda.finance.contracts.asset.Cash;
import net.corda.testing.node.MockServices;
import net.corda.testing.TestIdentity;
import net.corda.testing.core.TestIdentity;
import org.junit.Test;
import java.security.PublicKey;
@ -22,7 +22,9 @@ import static net.corda.finance.contracts.JavaCommercialPaper.JCP_PROGRAM_ID;
import static net.corda.testing.node.MockServicesKt.makeTestIdentityService;
import static net.corda.testing.node.NodeTestUtils.ledger;
import static net.corda.testing.node.NodeTestUtils.transaction;
import static net.corda.testing.TestConstants.*;
import static net.corda.testing.core.TestConstants.ALICE_NAME;
import static net.corda.testing.core.TestConstants.BOB_NAME;
import static net.corda.testing.core.TestConstants.TEST_TX_TIME;
public class CommercialPaperTest {
private static final TestIdentity ALICE = new TestIdentity(ALICE_NAME, 70L);
@ -30,7 +32,7 @@ public class CommercialPaperTest {
private static final TestIdentity BOB = new TestIdentity(BOB_NAME, 80L);
private static final TestIdentity MEGA_CORP = new TestIdentity(new CordaX500Name("MegaCorp", "London", "GB"));
private final byte[] defaultRef = {123};
private final MockServices ledgerServices = new MockServices(emptyList(), makeTestIdentityService(MEGA_CORP.getIdentity()));
private final MockServices ledgerServices = new MockServices(MEGA_CORP);
// DOCSTART 1
private ICommercialPaperState getPaper() {

View File

@ -8,7 +8,7 @@ import net.corda.finance.*
import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow
import net.corda.node.internal.StartedNode
import net.corda.testing.chooseIdentity
import net.corda.testing.core.chooseIdentity
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.startFlow
import org.junit.After

View File

@ -1,12 +1,15 @@
package net.corda.docs
import net.corda.node.services.config.ConfigHelper
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.parseAsNodeConfiguration
import net.corda.verifier.Verifier
import org.junit.Test
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.reflect.KVisibility
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.jvm.isAccessible
class ExampleConfigTest {
@ -17,14 +20,16 @@ class ExampleConfigTest {
val config = loadConfig(Paths.get(configFileResource.toURI()))
// Force the config fields as they are resolved lazily
config.javaClass.kotlin.declaredMemberProperties.forEach { member ->
member.get(config)
if (member.visibility == KVisibility.PUBLIC) {
member.get(config)
}
}
}
}
@Test
fun `example node_confs parses fine`() {
readAndCheckConfigurations(
readAndCheckConfigurations<NodeConfiguration>(
"example-node.conf",
"example-out-of-process-verifier-node.conf",
"example-network-map-node.conf"

View File

@ -8,7 +8,7 @@ import net.corda.finance.*
import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow
import net.corda.node.internal.StartedNode
import net.corda.testing.chooseIdentity
import net.corda.testing.core.chooseIdentity
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.startFlow
import org.junit.After

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.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.startFlow
import org.junit.After

View File

@ -14,7 +14,7 @@ import net.corda.finance.contracts.ICommercialPaperState
import net.corda.finance.contracts.asset.CASH
import net.corda.finance.contracts.asset.Cash
import net.corda.node.services.api.IdentityServiceInternal
import net.corda.testing.*
import net.corda.testing.core.*
import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger

View File

@ -78,9 +78,9 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
directory "./build/nodes"
networkMap "O=Controller,L=London,C=GB"
networkMap "O=NetworkMapAndNotary,L=London,C=GB"
node {
name "O=Controller,L=London,C=GB"
name "O=NetworkMapAndNotary,L=London,C=GB"
// The notary will offer a validating notary service.
notary = [validating : true]
p2pPort 10002
@ -114,7 +114,7 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates
Running this task will create three nodes in the ``build/nodes`` folder:
* A ``Controller`` node that:
* A ``NetworkMapAndNotary`` node that:
* Serves as the network map
* Offers a validating notary service
@ -123,7 +123,7 @@ Running this task will create three nodes in the ``build/nodes`` folder:
* ``PartyA`` and ``PartyB`` nodes that:
* Are pointing at the ``Controller`` as the network map service
* Are pointing at the ``NetworkMapAndNotary`` as the network map service
* Are not offering any services
* Will have a webserver (since ``webPort`` is defined)
* Are running the ``corda-finance`` CorDapp

View File

@ -13,7 +13,7 @@ Deploying our CorDapp
---------------------
Let's take a look at the nodes we're going to deploy. Open the project's ``build.gradle`` file and scroll down to the
``task deployNodes`` section. This section defines three nodes. There are two standard nodes (``PartyA`` and
``PartyB``), plus a special Controller node that is running the network map service and advertises a validating notary
``PartyB``), plus a special network map/notary node that is running the network map service and advertises a validating notary
service.
.. code:: bash
@ -21,7 +21,7 @@ service.
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
directory "./build/nodes"
node {
name "O=Controller,L=London,C=GB"
name "O=NetworkMapAndNotary,L=London,C=GB"
notary = [validating : true]
p2pPort 10002
rpcPort 10003
@ -142,7 +142,7 @@ The vaults of PartyA and PartyB should both display the following output:
- "C=GB,L=London,O=PartyA"
- "C=US,L=New York,O=PartyB"
contract: "com.template.contract.IOUContract"
notary: "C=GB,L=London,O=Controller,CN=corda.notary.validating"
notary: "C=GB,L=London,O=NetworkMapAndNotary,CN=corda.notary.validating"
encumbrance: null
constraint:
attachmentId: "F578320232CAB87BB1E919F3E5DB9D81B7346F9D7EA6D9155DC0F7BA8E472552"
@ -157,7 +157,7 @@ The vaults of PartyA and PartyB should both display the following output:
recordedTime: 1506415268.875000000
consumedTime: null
status: "UNCONSUMED"
notary: "C=GB,L=London,O=Controller,CN=corda.notary.validating"
notary: "C=GB,L=London,O=NetworkMapAndNotary,CN=corda.notary.validating"
lockId: null
lockUpdateTime: 1506415269.548000000
totalStatesAvailable: -1

View File

@ -1,9 +1,8 @@
Node administration
===================
When a node is running, it exposes an RPC interface that lets you monitor it,
you can upload and download attachments, access a REST API and so on. A bundled
Jetty web server exposes the same interface over HTTP.
When a node is running, it exposes an RPC interface that lets you monitor it, upload and download attachments, and so
on.
Logging
-------

View File

@ -4,8 +4,6 @@ Other
.. toctree::
:maxdepth: 1
json
secure-coding-guidelines
corda-repo-layout
building-the-docs
codestyle
json

View File

@ -6,6 +6,5 @@ Quickstart
getting-set-up
tutorial-cordapp
Sample CorDapps <https://www.corda.net/samples/>
building-against-master
CLI-vs-IDE
Other CorDapps <https://www.corda.net/samples/>
Utilities <https://www.corda.net/utilities/>

View File

@ -6,5 +6,6 @@ Release process
release-notes
changelog
contributing
codestyle
testing

View File

@ -8,4 +8,5 @@ Tools
demobench
node-explorer
azure-vm
aws-vm
loadtesting

View File

@ -17,7 +17,7 @@ if:
We will deploy the CorDapp on 4 test nodes:
* **Controller**, which hosts a validating notary service
* **NetworkMapAndNotary**, which hosts a validating notary service
* **PartyA**
* **PartyB**
* **PartyC**
@ -252,7 +252,7 @@ For each node, the ``runnodes`` script creates a node tab/window:
Fri Jul 07 10:33:47 BST 2017>>>
For every node except the controller, the script also creates a webserver terminal tab/window:
For every node except the network map/notary, the script also creates a webserver terminal tab/window:
.. sourcecode:: none
@ -276,7 +276,7 @@ IntelliJ
The node driver defined in ``/src/test/kotlin/com/example/Main.kt`` allows you to specify how many nodes you would like
to run and the configuration settings for each node. For the example CorDapp, the driver starts up four nodes
and adds an RPC user for all but the "Controller" node (which serves as the notary):
and adds an RPC user for all but the network map/notary node:
.. sourcecode:: kotlin
@ -284,7 +284,7 @@ IntelliJ
// No permissions required as we are not invoking flows.
val user = User("user1", "test", permissions = setOf())
driver(isDebug = true, waitForNodesToFinish = true) {
startNode(getX500Name(O="Controller",L="London",C='GB"), setOf(ServiceInfo(ValidatingNotaryService.type)))
startNode(getX500Name(O="NetworkMapAndNotary",L="London",C='GB"), setOf(ServiceInfo(ValidatingNotaryService.type)))
val (nodeA, nodeB, nodeC) = Futures.allAsList(
startNode(getX500Name(O="PartyA",L="London",C="GB"), rpcUsers = listOf(user)),
startNode(getX500Name(O="PartyB",L="New York",C="US"), rpcUsers = listOf(user)),
@ -475,15 +475,15 @@ The nodes can be split across machines and configured to communicate across the
After deploying the nodes, navigate to the build folder (``kotlin-source/build/nodes``) and move some of the individual
node folders to a different machine (e.g. using a USB key). It is important that none of the nodes - including the
controller node - end up on more than one machine. Each computer should also have a copy of ``runnodes`` and
network map/notary node - end up on more than one machine. Each computer should also have a copy of ``runnodes`` and
``runnodes.bat``.
For example, you may end up with the following layout:
* Machine 1: ``controller``, ``nodea``, ``runnodes``, ``runnodes.bat``
* Machine 2: ``nodeb``, ``nodec``, ``runnodes``, ``runnodes.bat``
* Machine 1: ``NetworkMapAndNotary``, ``PartyA``, ``runnodes``, ``runnodes.bat``
* Machine 2: ``PartyB``, ``PartyC``, ``runnodes``, ``runnodes.bat``
You must now edit the configuration file for each node, including the controller. Open each node's config file,
You must now edit the configuration file for each node, including the network map/notary. Open each node's config file,
and make the following changes:
* Change the Artemis messaging address to the machine's IP address (e.g. ``p2pAddress="10.18.0.166:10006"``)
@ -504,7 +504,7 @@ Debugging is done via IntelliJ as follows:
// No permissions required as we are not invoking flows.
val user = User("user1", "test", permissions = setOf())
driver(isDebug = true, waitForNodesToFinish = true) {
startNode(getX500Name(O="Controller",L="London",C="GB"), setOf(ServiceInfo(ValidatingNotaryService.type)))
startNode(getX500Name(O="NetworkMapAndNotary",L="London",C="GB"), setOf(ServiceInfo(ValidatingNotaryService.type)))
val (nodeA, nodeB, nodeC) = Futures.allAsList(
startNode(getX500Name(O="PartyA",L=London,C=GB"), rpcUsers = listOf(user)),
startNode(getX500Name(O="PartyB",L=New York,C=US"), rpcUsers = listOf(user)),

View File

@ -140,25 +140,28 @@ Applies to both gradle deployNodes tasks and/or corda node configuration (node.c
notary = [validating : true]
* For existing contract ORM schemas that extend from `CommonSchemaV1.LinearState` or `CommonSchemaV1.FungibleState`,
you will need to explicitly map the `participants` collection to a database table. Previously this mapping was done in the
superclass, but that makes it impossible to properly configure the table name.
The required change is to add the ``override var participants: MutableSet<AbstractParty>? = null`` field to your class, and
add JPA mappings. For ex., see this example:
* For existing contract ORM schemas that extend from ``CommonSchemaV1.LinearState`` or ``CommonSchemaV1.FungibleState``,
you will need to explicitly map the ``participants`` collection to a database table. Previously this mapping was done
in the superclass, but that makes it impossible to properly configure the table name. The required changes are to:
.. sourcecode:: kotlin
* Add the ``override var participants: MutableSet<AbstractParty>? = null`` field to your class, and
* Add JPA mappings
@Entity
@Table(name = "cash_states_v2",
indexes = arrayOf(Index(name = "ccy_code_idx2", columnList = "ccy_code")))
class PersistentCashState(
For example:
@ElementCollection
@Column(name = "participants")
@CollectionTable(name="cash_states_v2_participants", joinColumns = arrayOf(
JoinColumn(name = "output_index", referencedColumnName = "output_index"),
JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id")))
override var participants: MutableSet<AbstractParty>? = null,
.. sourcecode:: kotlin
@Entity
@Table(name = "cash_states_v2",
indexes = arrayOf(Index(name = "ccy_code_idx2", columnList = "ccy_code")))
class PersistentCashState(
@ElementCollection
@Column(name = "participants")
@CollectionTable(name="cash_states_v2_participants", joinColumns = arrayOf(
JoinColumn(name = "output_index", referencedColumnName = "output_index"),
JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id")))
override var participants: MutableSet<AbstractParty>? = null,
Testing
^^^^^^^
@ -236,11 +239,13 @@ Contract tests
Flow tests
~~~~~~~~~~
* The registration mechanism for CorDapps in ``MockNetwork`` unit tests has changed.
* The registration mechanism for CorDapps in ``MockNetwork`` unit tests has changed:
It is now done via the ``cordappPackages`` constructor parameter of MockNetwork. This takes a list of ``String``
values which should be the package names of the CorDapps containing the contract verification code you wish to load.
The ``unsetCordappPackages`` method is now redundant and has been removed.
* CorDapp registration is now done via the ``cordappPackages`` constructor parameter of MockNetwork.
This parameter
is a list of ``String`` values which should be the
package names of the CorDapps containing the contract verification code you wish to load
*The ``unsetCordappPackages`` method is now redundant and has been removed
* Creation of Notaries in ``MockNetwork`` unit tests has changed.
@ -395,13 +400,13 @@ Finance
V1.0 to V2.0
------------
You only need to update the ``corda_release_version`` identifier in your project gradle file. The
corda_gradle_plugins_version should remain at 1.0.0:
* You need to update the ``corda_release_version`` identifier in your project gradle file. The
corda_gradle_plugins_version should remain at 1.0.0:
.. sourcecode:: shell
.. sourcecode:: shell
ext.corda_release_version = '2.0.0'
ext.corda_gradle_plugins_version = '1.0.0'
ext.corda_release_version = '2.0.0'
ext.corda_gradle_plugins_version = '1.0.0'
Public Beta (M12) to V1.0
-------------------------
@ -417,143 +422,145 @@ Build
A new test driver module dependency needs to be including in your project: ``corda-node-driver``. To continue using the
mock network for testing, add the following entry to your gradle build file:
.. sourcecode:: shell
.. sourcecode:: shell
testCompile "$corda_release_distribution:corda-node-driver:$corda_release_version"
testCompile "$corda_release_distribution:corda-node-driver:$corda_release_version"
.. note:: you may only need `testCompile "$corda_release_distribution:corda-test-utils:$corda_release_version"` if not using the Driver DSL.
.. note:: You may only need ``testCompile "$corda_release_distribution:corda-test-utils:$corda_release_version"`` if not using the Driver
DSL
Configuration
^^^^^^^^^^^^^
* ``CordaPluginRegistry`` has been removed.
The one remaining configuration item ``customizeSerialisation``, which defined a optional whitelist of types for use in
object serialization, has been replaced with the ``SerializationWhitelist`` interface which should be implemented to
define a list of equivalent whitelisted classes.
You will need to rename your services resource file to the new class name:
'resources/META-INF/services/net.corda.core.node.CordaPluginRegistry' becomes 'resources/META-INF/services/net.corda.core.serialization.SerializationWhitelist'
An associated property on ``MockNode`` was renamed from ``testPluginRegistries`` to ``testSerializationWhitelists``.
In general, the ``@CordaSerializable`` annotation is the preferred method for whitelisting as described in :doc:`serialization`
* ``CordaPluginRegistry`` has been removed:
* The one remaining configuration item ``customizeSerialisation``, which defined a optional whitelist of types for
use in object serialization, has been replaced with the ``SerializationWhitelist`` interface which should be
implemented to define a list of equivalent whitelisted classes
* You will need to rename your services resource file. 'resources/META-INF/services/net.corda.core.node.CordaPluginRegistry'
becomes 'resources/META-INF/services/net.corda.core.serialization.SerializationWhitelist'
* ``MockNode.testPluginRegistries`` was renamed to ``MockNode.testSerializationWhitelists``
* In general, the ``@CordaSerializable`` annotation is the preferred method for whitelisting, as described in
:doc:`serialization`
Missing imports
^^^^^^^^^^^^^^^
Use the automatic imports feature of IntelliJ to intelligently resolve the new imports.
Use IntelliJ's automatic imports feature to intelligently resolve the new imports:
* Missing imports for contract types.
* Missing imports for contract types:
* CommercialPaper and Cash are now contained within the ``finance`` module, as are associated helpers functions. For
example:
CommercialPaper and Cash are now contained within the ``finance`` module, as are associated helpers functions.
For example:
``import net.corda.contracts.ICommercialPaperState`` becomes ``import net.corda.finance.contracts.ICommercialPaperState``
``import net.corda.contracts.asset.sumCashBy`` becomes ``import net.corda.finance.utils.sumCashBy``
* ``import net.corda.contracts.asset.sumCashBy`` becomes ``import net.corda.finance.utils.sumCashBy``
``import net.corda.core.contracts.DOLLARS`` becomes ``import net.corda.finance.DOLLARS``
* ``import net.corda.core.contracts.DOLLARS`` becomes ``import net.corda.finance.DOLLARS``
``import net.corda.core.contracts.issued by`` becomes ``import net.corda.finance.issued by``
* ``import net.corda.core.contracts.issued by`` becomes ``import net.corda.finance.issued by``
``import net.corda.contracts.asset.Cash`` becomes ``import net.corda.finance.contracts.asset.Cash``
* ``import net.corda.contracts.asset.Cash`` becomes ``import net.corda.finance.contracts.asset.Cash``
* Missing imports for utility functions.
* Missing imports for utility functions:
Many common types and helper methods have been consolidated into ``net.corda.core.utilities`` package.
For example:
``import net.corda.core.crypto.commonName`` becomes ``import net.corda.core.utilities.commonName``
* Many common types and helper methods have been consolidated into ``net.corda.core.utilities`` package. For example:
``import net.corda.core.crypto.toBase58String`` becomes ``import net.corda.core.utilities.toBase58String``
* ``import net.corda.core.crypto.commonName`` becomes ``import net.corda.core.utilities.commonName``
``import net.corda.core.getOrThrow`` becomes ``import net.corda.core.utilities.getOrThrow``
* ``import net.corda.core.crypto.toBase58String`` becomes ``import net.corda.core.utilities.toBase58String``
* Missing flow imports.
* ``import net.corda.core.getOrThrow`` becomes ``import net.corda.core.utilities.getOrThrow``
In general all reusable library flows are contained within the **core** API `net.corda.core.flows` package.
Financial domain library flows are contained within the **finance** module `net.corda.finance.flows` package.
Other flows that have moved include:
* Missing flow imports:
``import net.corda.core.flows.ResolveTransactionsFlow`` becomes ``import net.corda.core.internal.ResolveTransactionsFlow``
* In general, all reusable library flows are contained within the **core** API ``net.corda.core.flows`` package
* Financial domain library flows are contained within the **finance** module ``net.corda.finance.flows`` package
* Other flows that have moved include ``import net.corda.core.flows.ResolveTransactionsFlow``, which becomes
``import net.corda.core.internal.ResolveTransactionsFlow``
Core data structures
^^^^^^^^^^^^^^^^^^^^
* Missing Contract override.
* Missing ``Contract`` override:
The contract interace attribute ``legalContractReference`` has been removed, and replaced by
the optional annotation ``@LegalProseReference(uri = "<URI>")``
* ``Contract.legalContractReference`` has been removed, and replaced by the optional annotation
``@LegalProseReference(uri = "<URI>")``
* Unresolved reference.
* Unresolved reference:
Calls to ``AuthenticatedObject`` are replaced by ``CommandWithParties``
* ``AuthenticatedObject`` was renamed to ``CommandWithParties``
* Overrides nothing: ``isRelevant`` in ``LinearState``.
* Overrides nothing:
Removed the concept of relevancy from ``LinearState``. A ``ContractState``'s relevance to the vault is now resolved
internally; the vault will process any transaction from a flow which is not derived from transaction resolution verification.
The notion of relevancy is subject to further improvements to enable a developer to control what state the vault thinks
are relevant.
* ``LinearState.isRelevant`` was removed. Whether a node stores a ``LinearState`` in its vault depends on whether the
node is one of the state's ``participants``
* Calls to ``txBuilder.toLedgerTransaction()`` now requires a serviceHub parameter.
Used by the new Contract Constraints functionality to validate and resolve attachments.
* ``txBuilder.toLedgerTransaction`` now requires a ``ServiceHub`` parameter. This is used by the new Contract
Constraints functionality to validate and resolve attachments
Flow framework
^^^^^^^^^^^^^^
* Flow session deprecations
* ``FlowLogic`` communication has been upgraded to use explicit ``FlowSession`` instances to communicate between nodes:
``FlowLogic`` communication has been upgraded to use functions on ``FlowSession`` as the base for communication
between nodes.
* Calls to ``send()``, ``receive()`` and ``sendAndReceive()`` on FlowLogic should be replaced with calls
to the function of the same name on ``FlowSession``. Note that the replacement functions do not take in a destination
parameter, as this is defined in the session.
* ``FlowLogic.send``/``FlowLogic.receive``/``FlowLogic.sendAndReceive`` has been replaced by ``FlowSession.send``/
``FlowSession.receive``/``FlowSession.sendAndReceive``. The replacement functions do not take a destination
parameter, as this is defined implictly by the session used
* Initiated flows now take in a ``FlowSession`` instead of ``Party`` in their constructor. If you need to access the
counterparty identity, it is in the ``counterparty`` property of the flow session.
See ``FlowSession`` for step by step instructions on porting existing flows to use the new mechanism.
counterparty identity, it is in the ``counterparty`` property of the flow session
* ``FinalityFlow`` now returns a single ``SignedTransaction``, instead of a ``List<SignedTransaction>``
* ``TransactionKeyFlow`` renamed to ``SwapIdentitiesFlow``
* ``TransactionKeyFlow`` was renamed to ``SwapIdentitiesFlow``
Note that ``SwapIdentitiesFlow`` must be imported from the *confidential-identities** package ''net.corda.confidential''
* ``SwapIdentitiesFlow`` must be imported from the *confidential-identities* package ``net.corda.confidential``
Node services (ServiceHub)
^^^^^^^^^^^^^^^^^^^^^^^^^^
* VaultQueryService: unresolved reference to ``vaultQueryService``.
Replace all references to ``<services>.vaultQueryService`` with ``<services>.vaultService``.
Previously there were two vault APIs. Now there is a single unified API with the same functions: ``VaultService``.
* Replace all references to ``<services>.vaultQueryService`` with ``<services>.vaultService``
* ``serviceHub.myInfo.legalIdentity`` no longer exists; use the ``ourIdentity`` property of the flow instead.
* Previously there were two vault APIs. Now there is a single unified API with the same functions: ``VaultService``.
``FlowLogic.ourIdentity`` has been introduced as a shortcut for retrieving our identity in a flow
* ``FlowLogic.ourIdentity`` has been introduced as a shortcut for retrieving our identity in a flow
* ``getAnyNotary`` is gone - use ``serviceHub.networkMapCache.notaryIdentities[0]`` instead
* ``serviceHub.myInfo.legalIdentity`` no longer exists
Note: ongoing work to support multiple notary identities is still in progress.
* ``getAnyNotary`` has been removed. Use ``serviceHub.networkMapCache.notaryIdentities[0]`` instead
* ``ServiceHub.networkMapUpdates`` is replaced by ``ServiceHub.networkMapFeed``
* ``ServiceHub.partyFromX500Name`` is replaced by ``ServiceHub.wellKnownPartyFromX500Name``
Note: A "well known" party is one that isn't anonymous and this change was motivated by the confidential identities work.
* A "well known" party is one that isn't anonymous. This change was motivated by the confidential identities work
RPC Client
^^^^^^^^^^
* Missing API methods on ``CordaRPCOps`` interface.
* Missing API methods on the ``CordaRPCOps`` interface:
* Calls to ``verifiedTransactionsFeed()`` and ``verifiedTransactions()`` have been replaced with:
``internalVerifiedTransactionsSnapshot()`` and ``internalVerifiedTransactionsFeed()`` respectively
* ``verifiedTransactionsFeed`` has been replaced by ``internalVerifiedTransactionsFeed``
This is in preparation for the planned integration of Intel SGX™, which will encrypt the transactions feed.
Apps that use this API will not work on encrypted ledgers: you should probably be using the vault query API instead.
* ``verifiedTransactions`` has been replaced by ``internalVerifiedTransactionsSnapshot``
* Accessing the ``networkMapCache`` via ``services.nodeInfo().legalIdentities`` returns a list of identities.
The first element in the list is the Party object referring to a node's single identity.
This is in preparation for allowing a node to host multiple separate identities in future.
* Accessing the ``networkMapCache`` via ``services.nodeInfo().legalIdentities`` returns a list of identities
* This change is in preparation for allowing a node to host multiple separate identities in the future
Testing
^^^^^^^
@ -561,26 +568,22 @@ Testing
Please note that ``Clauses`` have been removed completely as of V1.0.
We will be revisiting this capability in a future release.
* CorDapps must be explicitly registered in ``MockNetwork`` unit tests.
* CorDapps must be explicitly registered in ``MockNetwork`` unit tests:
This is done by calling ``setCordappPackages``, an extension helper function in the ``net.corda.testing`` package,
on the first line of your ``@Before`` method. This takes a variable number of ``String`` arguments which should be the
package names of the CorDapps containing the contract verification code you wish to load.
You should unset CorDapp packages in your ``@After`` method by using ``unsetCordappPackages()`` after ``stopNodes()``.
* CorDapps must be explicitly registered in ``DriverDSL`` and ``RPCDriverDSL`` integration tests.
* CorDapps must be explicitly registered in ``DriverDSL`` and ``RPCDriverDSL`` integration tests:
Similarly, you must also register package names of the CorDapps containing the contract verification code you wish to load
using the ``extraCordappPackagesToScan: List<String>`` constructor parameter of the driver DSL.
* You must register package names of the CorDapps containing the contract verification code you wish to load using
the ``extraCordappPackagesToScan: List<String>`` constructor parameter of the driver DSL
Finance
^^^^^^^
* ``FungibleAsset`` interface simplification.
The ``FungibleAsset`` interface has been made simpler. The ``Commands`` grouping interface
that included the ``Move``, ``Issue`` and ``Exit`` interfaces have all been removed, while the ``move`` function has
been renamed to ``withNewOwnerAndAmount`` to be consistent with the ``withNewOwner`` function of the ``OwnableState``.
* ``FungibleAsset`` interface simplification:
The following errors may be reported:
@ -595,20 +598,20 @@ Miscellaneous
* ``args[0].parseNetworkHostAndPort()`` becomes ``NetworkHostAndPort.parse(args[0])``
* There is no longer a ``NodeInfo.advertisedServices`` property.
* There is no longer a ``NodeInfo.advertisedServices`` property
The concept of advertised services has been removed from Corda. This is because it was vaguely defined and real world
apps would not typically select random, unknown counterparties from the network map based on self-declared capabilities.
We will introduce a replacement for this functionality, business networks, in a future release.
For now, your should retrieve the service by legal name using ``NetworkMapCache.getNodeByLegalName``.
* The concept of advertised services has been removed from Corda. This is because it was vaguely defined and
real-world apps would not typically select random, unknown counterparties from the network map based on
self-declared capabilities
* We will introduce a replacement for this functionality, business networks, in a future release
* For now, services should be retrieved by legal name using ``NetworkMapCache.getNodeByLegalName``
Gotchas
^^^^^^^
* Beware to use the correct identity when issuing cash:
* Be sure to use the correct identity when issuing cash:
The 3rd parameter to ``CashIssueFlow`` should be the ** notary ** (not the ** node identity **)
* The third parameter to ``CashIssueFlow`` should be the *notary* (and not the *node identity*)
:ref:`From Milestone 13 <changelog_m13>`
@ -617,67 +620,71 @@ Gotchas
Core data structures
^^^^^^^^^^^^^^^^^^^^
* ``TransactionBuilder`` changes.
* ``TransactionBuilder`` changes:
Use convenience class ``StateAndContract`` instead of ``TransactionBuilder.withItems()`` for passing
around a state and its contract.
* Use convenience class ``StateAndContract`` instead of ``TransactionBuilder.withItems`` for passing
around a state and its contract.
* Transaction building DSL changes:
* Transaction builder DSL changes:
* now need to explicitly pass the ContractClassName into all inputs and outputs.
* ``ContractClassName`` refers to the class containing the “verifier” method.
* Contract verify method signature change.
* ``ContractClassName`` is the name of the ``Contract`` subclass used to verify the transaction
``override fun verify(tx: TransactionForContract)`` becomes ``override fun verify(tx: LedgerTransaction)``
* Contract verify method signature change:
* No longer need to override Contract ``contract()`` function.
* ``override fun verify(tx: TransactionForContract)`` becomes ``override fun verify(tx: LedgerTransaction)``
* You no longer need to override ``ContractState.contract`` function
Node services (ServiceHub)
^^^^^^^^^^^^^^^^^^^^^^^^^^
* ServiceHub API method changes.
* ServiceHub API method changes:
``services.networkMapUpdates().justSnapshot`` becomes ``services.networkMapSnapshot()``
* ``services.networkMapUpdates().justSnapshot`` becomes ``services.networkMapSnapshot()``
Configuration
^^^^^^^^^^^^^
* No longer need to define ``CordaPluginRegistry`` and configure ``requiredSchemas``
* No longer need to define ``CordaPluginRegistry`` and configure ``requiredSchemas``:
Custom contract schemas are automatically detected at startup time by class path scanning.
For testing purposes, use the ``SchemaService`` method to register new custom schemas:
eg. ``services.schemaService.registerCustomSchemas(setOf(YoSchemaV1))``
* Custom contract schemas are automatically detected at startup time by class path scanning
* For testing purposes, use the ``SchemaService`` method to register new custom schemas (e.g.
``services.schemaService.registerCustomSchemas(setOf(YoSchemaV1))``)
Identity
^^^^^^^^
* Party names are now ``CordaX500Name``, not ``X500Name``
* Party names are now ``CordaX500Name``, not ``X500Name``:
``CordaX500Name`` specifies a predefined set of mandatory (organisation, locality, country)
and optional fields (commonName, organisationUnit, state) with validation checking.
Use new builder CordaX500Name.build(X500Name(target)) or, preferably, explicitly define X500Name parameters using
``CordaX500Name`` constructor.
* ``CordaX500Name`` specifies a predefined set of mandatory (organisation, locality, country) and optional fields
(common name, organisation unit, state) with validation checking
* Use new builder ``CordaX500Name.build(X500Name(target))`` or explicitly define the X500Name parameters using the
``CordaX500Name`` constructors
Testing
^^^^^^^
* MockNetwork Testing.
* MockNetwork testing:
Mock nodes in node tests are now of type ``StartedNode<MockNode>``, rather than ``MockNode``
MockNetwork now returns a BasketOf(<StartedNode<MockNode>>)
Must call internals on StartedNode to get MockNode:
a = nodes.partyNodes[0].internals
b = nodes.partyNodes[1].internals
* Mock nodes in node tests are now of type ``StartedNode<MockNode>``, rather than ``MockNode``
* Host and Port change.
* ``MockNetwork`` now returns a ``BasketOf(<StartedNode<MockNode>>)``
Use string helper function ``parseNetworkHostAndPort()`` to parse a URL on startup.
eg. ``val hostAndPort = args[0].parseNetworkHostAndPort()``
* You must call internals on ``StartedNode`` to get ``MockNode`` (e.g. ``a = nodes.partyNodes[0].internals``)
* The node driver parameters for starting a node have been reordered, and the nodes name needs to be given as an
``CordaX500Name``, instead of using ``getX509Name``
* Host and port changes:
* Use string helper function ``parseNetworkHostAndPort`` to parse a URL on startup (e.g.
``val hostAndPort = args[0].parseNetworkHostAndPort()``)
* Node driver parameter changes:
* The node driver parameters for starting a node have been reordered
* The nodes name needs to be given as an ``CordaX500Name``, instead of using ``getX509Name``
:ref:`From Milestone 12 (First Public Beta) <changelog_m12>`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -685,17 +692,17 @@ Testing
Core data structures
^^^^^^^^^^^^^^^^^^^^
* Transaction building
* Transaction building:
You no longer need to specify the type of a ``TransactionBuilder`` as ``TransactionType.General``
``TransactionType.General.Builder(notary)`` becomes ``TransactionBuilder(notary)``
* You no longer need to specify the type of a ``TransactionBuilder`` as ``TransactionType.General``
* ``TransactionType.General.Builder(notary)`` becomes ``TransactionBuilder(notary)``
Build
^^^^^
* Gradle dependency reference changes.
* Gradle dependency reference changes:
Module name has changed to include ``corda`` in the artifacts jar name:
* Module names have changed to include ``corda`` in the artifacts' JAR names:
.. sourcecode:: shell
@ -708,25 +715,32 @@ Build
Node services (ServiceHub)
^^^^^^^^^^^^^^^^^^^^^^^^^^
* ServiceHub API changes.
* ``ServiceHub`` API changes:
``services.networkMapUpdates()`` becomes ``services.networkMapFeed()``
``services.getCashBalances()`` becomes a helper method within the **finance** module contracts package: ``net.corda.finance.contracts.getCashBalances``
* ``services.networkMapUpdates`` becomes ``services.networkMapFeed``
* ``services.getCashBalances`` becomes a helper method in the *finance* module contracts package
(``net.corda.finance.contracts.getCashBalances``)
Finance
^^^^^^^
* Financial asset contracts (Cash, CommercialPaper, Obligations) are now a standalone CorDapp within the **finance** module.
* Financial asset contracts (``Cash``, ``CommercialPaper``, ``Obligations``) are now a standalone CorDapp within the
``finance`` module:
Need to import from respective package within ``finance`` module:
eg. ``net.corda.finance.contracts.asset.Cash``
* You need to import them from their respective packages within the ``finance`` module (e.g.
``net.corda.finance.contracts.asset.Cash``)
Likewise, need to import associated asset flows from respective package within ``finance`` module:
eg. ``net.corda.finance.flows.CashIssueFlow``
``net.corda.finance.flows.CashIssueAndPaymentFlow``
``net.corda.finance.flows.CashExitFlow``
* You need to import the associated asset flows from their respective packages within ``finance`` module. For
example:
* Moved ``finance`` gradle project files into a ``net.corda.finance`` package namespace.
* ``net.corda.finance.flows.CashIssueFlow``
* ``net.corda.finance.flows.CashIssueAndPaymentFlow``
* ``net.corda.finance.flows.CashExitFlow``
This may require adjusting imports of Cash flow references and also of ``StartFlow`` permission in ``gradle.build`` files.
Associated flows (``Cash*Flow``, ``TwoPartyTradeFlow``, ``TwoPartyDealFlow``) must now be imported from this package.
* Adjust imports of Cash flow references
* Adjust the ``StartFlow`` permission in ``gradle.build`` files
* Adjust imports of the associated flows (``Cash*Flow``, ``TwoPartyTradeFlow``, ``TwoPartyDealFlow``)

View File

@ -8,7 +8,9 @@ import net.corda.finance.contracts.FixOf
import net.corda.finance.contracts.Frequency
import net.corda.finance.contracts.Tenor
import net.corda.node.services.api.IdentityServiceInternal
import net.corda.testing.*
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity
import net.corda.testing.dsl.EnforceVerifyOrFail
import net.corda.testing.dsl.TransactionDSL
import net.corda.testing.dsl.TransactionDSLInterpreter

View File

@ -2,7 +2,7 @@ package net.corda.finance.contracts.universal
import net.corda.finance.contracts.FixOf
import net.corda.finance.contracts.Tenor
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test

View File

@ -2,7 +2,7 @@ package net.corda.finance.contracts.universal
import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.CordaX500Name
import net.corda.testing.TestIdentity
import net.corda.testing.core.TestIdentity
import org.junit.Test
import java.util.*
import kotlin.test.assertEquals

View File

@ -1,6 +1,6 @@
package net.corda.finance.contracts.universal
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test

View File

@ -1,6 +1,6 @@
package net.corda.finance.contracts.universal
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test

View File

@ -3,7 +3,7 @@ package net.corda.finance.contracts.universal
import net.corda.finance.contracts.FixOf
import net.corda.finance.contracts.Frequency
import net.corda.finance.contracts.Tenor
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test

View File

@ -1,7 +1,7 @@
package net.corda.finance.contracts.universal
import net.corda.finance.contracts.Frequency
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Rule
import org.junit.Test
import java.time.Instant

View File

@ -2,7 +2,7 @@ package net.corda.finance.contracts.universal
import net.corda.finance.contracts.Frequency
import net.corda.finance.contracts.Tenor
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test

View File

@ -1,6 +1,6 @@
package net.corda.finance.contracts.universal
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Rule
import org.junit.Test
import java.time.Instant

View File

@ -37,6 +37,7 @@ object CommercialPaperSchemaV1 : MappedSchema(
var issuancePartyHash: String,
@Column(name = "issuance_ref")
@Type(type = "corda-wrapper-binary")
var issuanceRef: ByteArray,
@Column(name = "owner_key_hash", length = MAX_HASH_HEX_SIZE)

View File

@ -5,9 +5,9 @@ import net.corda.core.identity.AnonymousParty;
import net.corda.core.identity.CordaX500Name;
import net.corda.core.identity.Party;
import net.corda.node.services.api.IdentityServiceInternal;
import net.corda.testing.DummyCommandData;
import net.corda.testing.SerializationEnvironmentRule;
import net.corda.testing.TestIdentity;
import net.corda.testing.core.DummyCommandData;
import net.corda.testing.core.SerializationEnvironmentRule;
import net.corda.testing.core.TestIdentity;
import net.corda.testing.node.MockServices;
import org.junit.Rule;
import org.junit.Test;
@ -17,7 +17,7 @@ import static net.corda.finance.Currencies.DOLLARS;
import static net.corda.finance.Currencies.issuedBy;
import static net.corda.testing.node.NodeTestUtils.transaction;
import static net.corda.testing.internal.InternalTestUtilsKt.rigorousMock;
import static net.corda.testing.TestConstants.DUMMY_NOTARY_NAME;
import static net.corda.testing.core.TestConstants.DUMMY_NOTARY_NAME;
import static org.mockito.Mockito.doReturn;
/**

View File

@ -1,14 +1,10 @@
package net.corda.finance.contracts
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.*
import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.node.services.Vault
import net.corda.core.node.services.VaultService
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.days
@ -20,10 +16,13 @@ import net.corda.finance.contracts.asset.Cash
import net.corda.finance.contracts.asset.STATE
import net.corda.node.services.api.IdentityServiceInternal
import net.corda.testing.*
import net.corda.finance.contracts.asset.CASH
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.contracts.asset.STATE
import net.corda.testing.core.*
import net.corda.testing.dsl.EnforceVerifyOrFail
import net.corda.testing.dsl.TransactionDSL
import net.corda.testing.dsl.TransactionDSLInterpreter
import net.corda.testing.internal.rigorousMock
import net.corda.testing.internal.vault.VaultFiller
import net.corda.testing.node.MockServices
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
@ -50,15 +49,12 @@ interface ICommercialPaperTestTemplate {
}
private val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB"))
private val MEGA_CORP get() = megaCorp.party
private val MEGA_CORP_IDENTITY get() = megaCorp.identity
private val MEGA_CORP_PUBKEY get() = megaCorp.publicKey
class JavaCommercialPaperTest : ICommercialPaperTestTemplate {
override fun getPaper(): ICommercialPaperState = JavaCommercialPaper.State(
MEGA_CORP.ref(123),
MEGA_CORP,
1000.DOLLARS `issued by` MEGA_CORP.ref(123),
megaCorp.ref(123),
megaCorp.party,
1000.DOLLARS `issued by` megaCorp.ref(123),
TEST_TX_TIME + 7.days
)
@ -70,9 +66,9 @@ class JavaCommercialPaperTest : ICommercialPaperTestTemplate {
class KotlinCommercialPaperTest : ICommercialPaperTestTemplate {
override fun getPaper(): ICommercialPaperState = CommercialPaper.State(
issuance = MEGA_CORP.ref(123),
owner = MEGA_CORP,
faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
issuance = megaCorp.ref(123),
owner = megaCorp.party,
faceValue = 1000.DOLLARS `issued by` megaCorp.ref(123),
maturityDate = TEST_TX_TIME + 7.days
)
@ -84,9 +80,9 @@ class KotlinCommercialPaperTest : ICommercialPaperTestTemplate {
class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate {
override fun getPaper(): ICommercialPaperState = CommercialPaper.State(
issuance = MEGA_CORP.ref(123),
owner = MEGA_CORP,
faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
issuance = megaCorp.ref(123),
owner = megaCorp.party,
faceValue = 1000.DOLLARS `issued by` megaCorp.ref(123),
maturityDate = TEST_TX_TIME + 7.days
)
@ -104,49 +100,36 @@ class CommercialPaperTestsGeneric {
fun data() = listOf(JavaCommercialPaperTest(), KotlinCommercialPaperTest(), KotlinCommercialPaperLegacyTest())
private val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10)
private val DUMMY_CASH_ISSUER_IDENTITY get() = dummyCashIssuer.identity
private val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1)
private val alice = TestIdentity(ALICE_NAME, 70)
private val BIG_CORP_KEY = generateKeyPair()
private val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20)
private val alice = TestIdentity(ALICE_NAME, 70)
private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB"))
private val ALICE get() = alice.party
private val ALICE_KEY get() = alice.keyPair
private val ALICE_PUBKEY get() = alice.publicKey
private val DUMMY_NOTARY get() = dummyNotary.party
private val DUMMY_NOTARY_IDENTITY get() = dummyNotary.identity
private val MINI_CORP get() = miniCorp.party
private val MINI_CORP_IDENTITY get() = miniCorp.identity
private val MINI_CORP_PUBKEY get() = miniCorp.publicKey
}
@Parameterized.Parameter
lateinit var thisTest: ICommercialPaperTestTemplate
@Rule
@JvmField
val testSerialization = SerializationEnvironmentRule()
val issuer = MEGA_CORP.ref(123)
private val ledgerServices = MockServices(emptyList(), rigorousMock<IdentityServiceInternal>().also {
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY)
doReturn(MINI_CORP).whenever(it).partyFromKey(MINI_CORP_PUBKEY)
doReturn(null).whenever(it).partyFromKey(ALICE_PUBKEY)
}, MEGA_CORP.name)
private val megaCorpRef = megaCorp.ref(123)
private val ledgerServices = MockServices(megaCorp, miniCorp)
@Test
fun `trade lifecycle test`() {
val someProfits = 1200.DOLLARS `issued by` issuer
ledgerServices.ledger(DUMMY_NOTARY) {
val someProfits = 1200.DOLLARS `issued by` megaCorpRef
ledgerServices.ledger(dummyNotary.party) {
unverifiedTransaction {
attachment(Cash.PROGRAM_ID)
output(Cash.PROGRAM_ID, "alice's $900", 900.DOLLARS.CASH issuedBy issuer ownedBy ALICE)
output(Cash.PROGRAM_ID, "some profits", someProfits.STATE ownedBy MEGA_CORP)
output(Cash.PROGRAM_ID, "alice's $900", 900.DOLLARS.CASH issuedBy megaCorpRef ownedBy alice.party)
output(Cash.PROGRAM_ID, "some profits", someProfits.STATE ownedBy megaCorp.party)
}
// Some CP is issued onto the ledger by MegaCorp.
transaction("Issuance") {
attachments(CP_PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID)
output(thisTest.getContract(), "paper", thisTest.getPaper())
command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
command(megaCorp.publicKey, thisTest.getIssueCommand(dummyNotary.party))
timeWindow(TEST_TX_TIME)
this.verifies()
}
@ -157,10 +140,10 @@ class CommercialPaperTestsGeneric {
attachments(Cash.PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID)
input("paper")
input("alice's $900")
output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP)
output(thisTest.getContract(), "alice's paper", "paper".output<ICommercialPaperState>().withOwner(ALICE))
command(ALICE_PUBKEY, Cash.Commands.Move())
command(MEGA_CORP_PUBKEY, thisTest.getMoveCommand())
output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy megaCorpRef ownedBy megaCorp.party)
output(thisTest.getContract(), "alice's paper", "paper".output<ICommercialPaperState>().withOwner(alice.party))
command(alice.publicKey, Cash.Commands.Move())
command(megaCorp.publicKey, thisTest.getMoveCommand())
this.verifies()
}
@ -172,17 +155,17 @@ class CommercialPaperTestsGeneric {
input("some profits")
fun TransactionDSL<TransactionDSLInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
output(Cash.PROGRAM_ID, "Alice's profit", aliceGetsBack.STATE ownedBy ALICE)
output(Cash.PROGRAM_ID, "Change", (someProfits - aliceGetsBack).STATE ownedBy MEGA_CORP)
output(Cash.PROGRAM_ID, "Alice's profit", aliceGetsBack.STATE ownedBy alice.party)
output(Cash.PROGRAM_ID, "Change", (someProfits - aliceGetsBack).STATE ownedBy megaCorp.party)
}
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
command(ALICE_PUBKEY, thisTest.getRedeemCommand(DUMMY_NOTARY))
command(megaCorp.publicKey, Cash.Commands.Move())
command(alice.publicKey, thisTest.getRedeemCommand(dummyNotary.party))
tweak {
outputs(700.DOLLARS `issued by` issuer)
outputs(700.DOLLARS `issued by` megaCorpRef)
timeWindow(TEST_TX_TIME + 8.days)
this `fails with` "received amount equals the face value"
}
outputs(1000.DOLLARS `issued by` issuer)
outputs(1000.DOLLARS `issued by` megaCorpRef)
tweak {
@ -202,7 +185,7 @@ class CommercialPaperTestsGeneric {
}
private fun transaction(script: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail) = run {
ledgerServices.transaction(DUMMY_NOTARY, script)
ledgerServices.transaction(dummyNotary.party, script)
}
@Test
@ -211,7 +194,7 @@ class CommercialPaperTestsGeneric {
attachment(CP_PROGRAM_ID)
attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
output(thisTest.getContract(), thisTest.getPaper())
command(MINI_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
command(miniCorp.publicKey, thisTest.getIssueCommand(dummyNotary.party))
timeWindow(TEST_TX_TIME)
this `fails with` "output states are issued by a command signer"
}
@ -222,8 +205,8 @@ class CommercialPaperTestsGeneric {
transaction {
attachment(CP_PROGRAM_ID)
attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
output(thisTest.getContract(), thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer))
command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
output(thisTest.getContract(), thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` megaCorpRef))
command(megaCorp.publicKey, thisTest.getIssueCommand(dummyNotary.party))
timeWindow(TEST_TX_TIME)
this `fails with` "output values sum to more than the inputs"
}
@ -235,7 +218,7 @@ class CommercialPaperTestsGeneric {
attachment(CP_PROGRAM_ID)
attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
output(thisTest.getContract(), thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days))
command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
command(megaCorp.publicKey, thisTest.getIssueCommand(dummyNotary.party))
timeWindow(TEST_TX_TIME)
this `fails with` "maturity date is not in the past"
}
@ -248,95 +231,83 @@ class CommercialPaperTestsGeneric {
attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
input(thisTest.getContract(), thisTest.getPaper())
output(thisTest.getContract(), thisTest.getPaper())
command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
command(megaCorp.publicKey, thisTest.getIssueCommand(dummyNotary.party))
timeWindow(TEST_TX_TIME)
this `fails with` "output values sum to more than the inputs"
}
}
/**
* Unit test requires two separate Database instances to represent each of the two
* transaction participants (enforces uniqueness of vault content in lieu of partipant identity)
*/
private lateinit var bigCorpServices: MockServices
private lateinit var bigCorpVault: Vault<ContractState>
private lateinit var bigCorpVaultService: VaultService
private lateinit var aliceServices: MockServices
private lateinit var aliceVaultService: VaultService
private lateinit var alicesVault: Vault<ContractState>
private val notaryServices = MockServices(emptyList(), rigorousMock(), MEGA_CORP.name, dummyNotary.keyPair)
private val issuerServices = MockServices(listOf("net.corda.finance.contracts", "net.corda.finance.schemas"), rigorousMock(), MEGA_CORP.name, dummyCashIssuer.keyPair)
private lateinit var moveTX: SignedTransaction
@Test
fun `issue move and then redeem`() {
val aliceDatabaseAndServices = makeTestDatabaseAndMockServices(
listOf("net.corda.finance.contracts", "net.corda.finance.schemas"),
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY),
TestIdentity(MEGA_CORP.name, ALICE_KEY))
val databaseAlice = aliceDatabaseAndServices.first
aliceServices = aliceDatabaseAndServices.second
aliceVaultService = aliceServices.vaultService
// Set up a test environment with 4 parties:
// 1. The notary
// 2. A dummy cash issuer e.g. central bank
// 3. Alice
// 4. MegaCorp
//
// MegaCorp will issue some commercial paper and Alice will buy it, using cash issued to her in the name
// of the dummy cash issuer.
databaseAlice.transaction {
alicesVault = VaultFiller(aliceServices, dummyNotary, rngFactory = ::Random).fillWithSomeTestCash(9000.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER)
aliceVaultService = aliceServices.vaultService
val allIdentities = arrayOf(megaCorp.identity, miniCorp.identity, dummyCashIssuer.identity, dummyNotary.identity)
val notaryServices = MockServices(dummyNotary)
val issuerServices = MockServices(dummyCashIssuer, dummyNotary)
val (aliceDatabase, aliceServices) = makeTestDatabaseAndMockServices(
listOf("net.corda.finance.contracts", "net.corda.finance.schemas"),
makeTestIdentityService(*allIdentities),
alice
)
val aliceCash: Vault<Cash.State> = aliceDatabase.transaction {
VaultFiller(aliceServices, dummyNotary).fillWithSomeTestCash(9000.DOLLARS, issuerServices, 1, dummyCashIssuer.ref(1))
}
val bigCorpDatabaseAndServices = makeTestDatabaseAndMockServices(
listOf("net.corda.finance.contracts", "net.corda.finance.schemas"),
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY),
TestIdentity(MEGA_CORP.name, BIG_CORP_KEY))
val databaseBigCorp = bigCorpDatabaseAndServices.first
bigCorpServices = bigCorpDatabaseAndServices.second
bigCorpVaultService = bigCorpServices.vaultService
databaseBigCorp.transaction {
bigCorpVault = VaultFiller(bigCorpServices, dummyNotary, rngFactory = ::Random).fillWithSomeTestCash(13000.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER)
bigCorpVaultService = bigCorpServices.vaultService
val (megaCorpDatabase, megaCorpServices) = makeTestDatabaseAndMockServices(
listOf("net.corda.finance.contracts", "net.corda.finance.schemas"),
makeTestIdentityService(*allIdentities),
megaCorp
)
val bigCorpCash: Vault<Cash.State> = megaCorpDatabase.transaction {
VaultFiller(megaCorpServices, dummyNotary).fillWithSomeTestCash(13000.DOLLARS, issuerServices, 1, dummyCashIssuer.ref(1))
}
// Propagate the cash transactions to each side.
aliceServices.recordTransactions(bigCorpVault.states.map { bigCorpServices.validatedTransactions.getTransaction(it.ref.txhash)!! })
bigCorpServices.recordTransactions(alicesVault.states.map { aliceServices.validatedTransactions.getTransaction(it.ref.txhash)!! })
aliceServices.recordTransactions(bigCorpCash.states.map { megaCorpServices.validatedTransactions.getTransaction(it.ref.txhash)!! })
megaCorpServices.recordTransactions(aliceCash.states.map { aliceServices.validatedTransactions.getTransaction(it.ref.txhash)!! })
// BigCorp™ issues $10,000 of commercial paper, to mature in 30 days, owned initially by itself.
val faceValue = 10000.DOLLARS `issued by` DUMMY_CASH_ISSUER
val issuance = bigCorpServices.myInfo.chooseIdentity().ref(1)
val issueBuilder = CommercialPaper().generateIssue(issuance, faceValue, TEST_TX_TIME + 30.days, DUMMY_NOTARY)
// MegaCorp™ issues $10,000 of commercial paper, to mature in 30 days, owned initially by itself.
val faceValue = 10000.DOLLARS `issued by` dummyCashIssuer.ref(1)
val issuance = megaCorpServices.myInfo.chooseIdentity().ref(1)
val issueBuilder = CommercialPaper().generateIssue(issuance, faceValue, TEST_TX_TIME + 30.days, dummyNotary.party)
issueBuilder.setTimeWindow(TEST_TX_TIME, 30.seconds)
val issuePtx = bigCorpServices.signInitialTransaction(issueBuilder)
val issuePtx = megaCorpServices.signInitialTransaction(issueBuilder)
val issueTx = notaryServices.addSignature(issuePtx)
databaseAlice.transaction {
val moveTX = aliceDatabase.transaction {
// Alice pays $9000 to BigCorp to own some of their debt.
moveTX = run {
val builder = TransactionBuilder(DUMMY_NOTARY)
Cash.generateSpend(aliceServices, builder, 9000.DOLLARS, AnonymousParty(BIG_CORP_KEY.public))
CommercialPaper().generateMove(builder, issueTx.tx.outRef(0), AnonymousParty(ALICE_KEY.public))
val ptx = aliceServices.signInitialTransaction(builder)
val ptx2 = bigCorpServices.addSignature(ptx)
val stx = notaryServices.addSignature(ptx2)
stx
}
val builder = TransactionBuilder(dummyNotary.party)
Cash.generateSpend(aliceServices, builder, 9000.DOLLARS, alice.identity, AnonymousParty(megaCorp.publicKey))
CommercialPaper().generateMove(builder, issueTx.tx.outRef(0), AnonymousParty(alice.keyPair.public))
val ptx = aliceServices.signInitialTransaction(builder)
val ptx2 = megaCorpServices.addSignature(ptx)
val stx = notaryServices.addSignature(ptx2)
stx
}
databaseBigCorp.transaction {
megaCorpDatabase.transaction {
// Verify the txns are valid and insert into both sides.
listOf(issueTx, moveTX).forEach {
it.toLedgerTransaction(aliceServices).verify()
aliceServices.recordTransactions(it)
bigCorpServices.recordTransactions(it)
for (tx in listOf(issueTx, moveTX)) {
tx.toLedgerTransaction(aliceServices).verify()
aliceServices.recordTransactions(tx)
megaCorpServices.recordTransactions(tx)
}
}
databaseBigCorp.transaction {
megaCorpDatabase.transaction {
fun makeRedeemTX(time: Instant): Pair<SignedTransaction, UUID> {
val builder = TransactionBuilder(DUMMY_NOTARY)
val builder = TransactionBuilder(dummyNotary.party)
builder.setTimeWindow(time, 30.seconds)
CommercialPaper().generateRedeem(builder, moveTX.tx.outRef(1), bigCorpServices, bigCorpServices.myInfo.chooseIdentityAndCert())
CommercialPaper().generateRedeem(builder, moveTX.tx.outRef(1), megaCorpServices, megaCorpServices.myInfo.chooseIdentityAndCert())
val ptx = aliceServices.signInitialTransaction(builder)
val ptx2 = bigCorpServices.addSignature(ptx)
val ptx2 = megaCorpServices.addSignature(ptx)
val stx = notaryServices.addSignature(ptx2)
return Pair(stx, builder.lockId)
}
@ -349,7 +320,7 @@ class CommercialPaperTestsGeneric {
}
// manually release locks held by this failing transaction
aliceServices.vaultService.softLockRelease(tooEarlyRedemptionLockId)
assertTrue(e.cause!!.message!!.contains("paper must have matured"))
assertTrue("paper must have matured" in e.cause!!.message!!)
val validRedemption = makeRedeemTX(TEST_TX_TIME + 31.days).first
validRedemption.toLedgerTransaction(aliceServices).verify()

View File

@ -21,16 +21,15 @@ import net.corda.finance.utils.sumCash
import net.corda.finance.utils.sumCashBy
import net.corda.finance.utils.sumCashOrNull
import net.corda.finance.utils.sumCashOrZero
import net.corda.node.services.api.IdentityServiceInternal
import net.corda.node.services.vault.NodeVaultService
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.testing.*
import net.corda.testing.contracts.DummyState
import net.corda.testing.core.*
import net.corda.testing.dsl.EnforceVerifyOrFail
import net.corda.testing.dsl.TransactionDSL
import net.corda.testing.dsl.TransactionDSLInterpreter
import net.corda.testing.internal.LogHelper
import net.corda.testing.internal.rigorousMock
import net.corda.testing.internal.LogHelper
import net.corda.testing.internal.vault.VaultFiller
import net.corda.testing.node.MockServices
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
@ -47,41 +46,25 @@ import kotlin.test.*
class CashTests {
private companion object {
val alice = TestIdentity(ALICE_NAME, 70)
val BOB_PUBKEY = TestIdentity(BOB_NAME, 80).publicKey
val bob = TestIdentity(BOB_NAME, 80)
val charlie = TestIdentity(CHARLIE_NAME, 90)
val DUMMY_CASH_ISSUER_IDENTITY = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).identity
val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10)
val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20)
val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB"))
val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB"))
val ALICE get() = alice.party
val ALICE_PUBKEY get() = alice.publicKey
val CHARLIE get() = charlie.party
val CHARLIE_IDENTITY get() = charlie.identity
val DUMMY_NOTARY get() = dummyNotary.party
val DUMMY_NOTARY_IDENTITY get() = dummyNotary.identity
val DUMMY_NOTARY_KEY get() = dummyNotary.keyPair
val MEGA_CORP get() = megaCorp.party
val MEGA_CORP_IDENTITY get() = megaCorp.identity
val MEGA_CORP_KEY get() = megaCorp.keyPair
val MEGA_CORP_PUBKEY get() = megaCorp.publicKey
val MINI_CORP get() = miniCorp.party
val MINI_CORP_IDENTITY get() = miniCorp.identity
val MINI_CORP_KEY get() = miniCorp.keyPair
val MINI_CORP_PUBKEY get() = miniCorp.publicKey
}
@Rule
@JvmField
val testSerialization = SerializationEnvironmentRule()
private val defaultRef = OpaqueBytes.of(1)
private val defaultIssuer = MEGA_CORP.ref(defaultRef)
private val defaultIssuer = megaCorp.ref(1)
private val inState = Cash.State(
amount = 1000.DOLLARS `issued by` defaultIssuer,
owner = AnonymousParty(ALICE_PUBKEY)
owner = AnonymousParty(alice.publicKey)
)
// Input state held by the issuer
private val issuerInState = inState.copy(owner = defaultIssuer.party)
private val outState = issuerInState.copy(owner = AnonymousParty(BOB_PUBKEY))
private val outState = issuerInState.copy(owner = AnonymousParty(bob.publicKey))
private fun Cash.State.editDepositRef(ref: Byte) = copy(
amount = Amount(amount.quantity, token = amount.token.copy(amount.token.issuer.copy(reference = OpaqueBytes.of(ref))))
@ -96,48 +79,42 @@ class CashTests {
private lateinit var ourIdentity: AbstractParty
private lateinit var miniCorpAnonymised: AnonymousParty
private val CHARLIE_ANONYMISED = CHARLIE_IDENTITY.party.anonymise()
private lateinit var WALLET: List<StateAndRef<Cash.State>>
private lateinit var cashStates: List<StateAndRef<Cash.State>>
// TODO: Optimise this so that we don't throw away and rebuild state that can be shared across tests.
@Before
fun setUp() {
LogHelper.setLevel(NodeVaultService::class)
megaCorpServices = MockServices(listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas"), rigorousMock(), MEGA_CORP.name, MEGA_CORP_KEY)
miniCorpServices = MockServices(listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas"), rigorousMock<IdentityServiceInternal>().also {
doNothing().whenever(it).justVerifyAndRegisterIdentity(argThat { name == MINI_CORP.name })
}, MINI_CORP.name, MINI_CORP_KEY)
val notaryServices = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
megaCorpServices = MockServices(megaCorp)
miniCorpServices = MockServices(miniCorp)
val databaseAndServices = makeTestDatabaseAndMockServices(
listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas"),
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY),
TestIdentity(CordaX500Name("Me", "London", "GB")))
makeTestIdentityService(megaCorp.identity, miniCorp.identity, dummyCashIssuer.identity, dummyNotary.identity),
TestIdentity(CordaX500Name("Me", "London", "GB"))
)
database = databaseAndServices.first
ourServices = databaseAndServices.second
// Set up and register identities
ourIdentity = ourServices.myInfo.singleIdentity()
miniCorpAnonymised = miniCorpServices.myInfo.singleIdentityAndCert().party.anonymise()
(miniCorpServices.myInfo.legalIdentitiesAndCerts + megaCorpServices.myInfo.legalIdentitiesAndCerts + notaryServices.myInfo.legalIdentitiesAndCerts).forEach { identity ->
ourServices.identityService.verifyAndRegisterIdentity(identity) // TODO: Configure a mock identity service instead.
}
// Create some cash. Any attempt to spend >$500 will require multiple issuers to be involved.
database.transaction {
val vaultFiller = VaultFiller(ourServices, dummyNotary, rngFactory = ::Random)
vaultFiller.fillWithSomeTestCash(100.DOLLARS, megaCorpServices, 1, MEGA_CORP.ref(1), ourIdentity)
vaultFiller.fillWithSomeTestCash(400.DOLLARS, megaCorpServices, 1, MEGA_CORP.ref(1), ourIdentity)
vaultFiller.fillWithSomeTestCash(80.DOLLARS, miniCorpServices, 1, MINI_CORP.ref(1), ourIdentity)
vaultFiller.fillWithSomeTestCash(80.SWISS_FRANCS, miniCorpServices, 1, MINI_CORP.ref(1), ourIdentity)
val vaultFiller = VaultFiller(ourServices, dummyNotary)
vaultFiller.fillWithSomeTestCash(100.DOLLARS, megaCorpServices, 1, megaCorp.ref(1), ourIdentity)
vaultFiller.fillWithSomeTestCash(400.DOLLARS, megaCorpServices, 1, megaCorp.ref(1), ourIdentity)
vaultFiller.fillWithSomeTestCash(80.DOLLARS, miniCorpServices, 1, miniCorp.ref(1), ourIdentity)
vaultFiller.fillWithSomeTestCash(80.SWISS_FRANCS, miniCorpServices, 1, miniCorp.ref(1), ourIdentity)
}
database.transaction {
vaultStatesUnconsumed = ourServices.vaultService.queryBy<Cash.State>().states
}
WALLET = listOf(
makeCash(100.DOLLARS, MEGA_CORP),
makeCash(400.DOLLARS, MEGA_CORP),
makeCash(80.DOLLARS, MINI_CORP),
makeCash(80.SWISS_FRANCS, MINI_CORP, 2)
cashStates = listOf(
makeCash(100.DOLLARS, megaCorp.party),
makeCash(400.DOLLARS, megaCorp.party),
makeCash(80.DOLLARS, miniCorp.party),
makeCash(80.SWISS_FRANCS, miniCorp.party, 2)
)
}
@ -146,13 +123,8 @@ class CashTests {
database.close()
}
private fun transaction(script: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail) = run {
MockServices(emptyList(), rigorousMock<IdentityServiceInternal>().also {
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY)
doReturn(MINI_CORP).whenever(it).partyFromKey(MINI_CORP_PUBKEY)
doReturn(null).whenever(it).partyFromKey(ALICE_PUBKEY)
doReturn(null).whenever(it).partyFromKey(BOB_PUBKEY)
}, MEGA_CORP.name).transaction(DUMMY_NOTARY, script)
private fun transaction(script: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail) {
MockServices(megaCorp).transaction(dummyNotary.party, script)
}
@Test
@ -162,30 +134,30 @@ class CashTests {
input(Cash.PROGRAM_ID, inState)
tweak {
output(Cash.PROGRAM_ID, outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer))
command(ALICE_PUBKEY, Cash.Commands.Move())
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "the amounts balance"
}
tweak {
output(Cash.PROGRAM_ID, outState)
command(ALICE_PUBKEY, DummyCommandData)
command(alice.publicKey, DummyCommandData)
// Invalid command
this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command"
}
tweak {
output(Cash.PROGRAM_ID, outState)
command(BOB_PUBKEY, Cash.Commands.Move())
command(bob.publicKey, Cash.Commands.Move())
this `fails with` "the owning keys are a subset of the signing keys"
}
tweak {
output(Cash.PROGRAM_ID, outState)
output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP)
command(ALICE_PUBKEY, Cash.Commands.Move())
output(Cash.PROGRAM_ID, outState issuedBy miniCorp.party)
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "at least one cash input"
}
// Simple reallocation works.
tweak {
output(Cash.PROGRAM_ID, outState)
command(ALICE_PUBKEY, Cash.Commands.Move())
command(alice.publicKey, Cash.Commands.Move())
this.verifies()
}
}
@ -198,7 +170,7 @@ class CashTests {
attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID, DummyState())
output(Cash.PROGRAM_ID, outState)
command(MINI_CORP_PUBKEY, Cash.Commands.Move())
command(miniCorp.publicKey, Cash.Commands.Move())
this `fails with` "there is at least one cash input for this group"
}
}
@ -210,16 +182,16 @@ class CashTests {
transaction {
attachment(Cash.PROGRAM_ID)
output(Cash.PROGRAM_ID, outState)
command(ALICE_PUBKEY, Cash.Commands.Issue())
command(alice.publicKey, Cash.Commands.Issue())
this `fails with` "output states are issued by a command signer"
}
transaction {
attachment(Cash.PROGRAM_ID)
output(Cash.PROGRAM_ID,
Cash.State(
amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34),
owner = AnonymousParty(ALICE_PUBKEY)))
command(MINI_CORP_PUBKEY, Cash.Commands.Issue())
amount = 1000.DOLLARS `issued by` miniCorp.ref(12, 34),
owner = AnonymousParty(alice.publicKey)))
command(miniCorp.publicKey, Cash.Commands.Issue())
this.verifies()
}
}
@ -228,23 +200,23 @@ class CashTests {
fun generateIssueRaw() {
// Test generation works.
val tx: WireTransaction = TransactionBuilder(notary = null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(ALICE_PUBKEY), notary = DUMMY_NOTARY)
Cash().generateIssue(this, 100.DOLLARS `issued by` miniCorp.ref(12, 34), owner = AnonymousParty(alice.publicKey), notary = dummyNotary.party)
}.toWireTransaction(miniCorpServices)
assertTrue(tx.inputs.isEmpty())
val s = tx.outputsOfType<Cash.State>().single()
assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount)
assertEquals(MINI_CORP as AbstractParty, s.amount.token.issuer.party)
assertEquals(AnonymousParty(ALICE_PUBKEY), s.owner)
assertEquals(100.DOLLARS `issued by` miniCorp.ref(12, 34), s.amount)
assertEquals(miniCorp.party as AbstractParty, s.amount.token.issuer.party)
assertEquals(AnonymousParty(alice.publicKey), s.owner)
assertTrue(tx.commands[0].value is Cash.Commands.Issue)
assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0])
assertEquals(miniCorp.publicKey, tx.commands[0].signers[0])
}
@Test
fun generateIssueFromAmount() {
// Test issuance from an issued amount
val amount = 100.DOLLARS `issued by` MINI_CORP.ref(12, 34)
val amount = 100.DOLLARS `issued by` miniCorp.ref(12, 34)
val tx: WireTransaction = TransactionBuilder(notary = null).apply {
Cash().generateIssue(this, amount, owner = AnonymousParty(ALICE_PUBKEY), notary = DUMMY_NOTARY)
Cash().generateIssue(this, amount, owner = AnonymousParty(alice.publicKey), notary = dummyNotary.party)
}.toWireTransaction(miniCorpServices)
assertTrue(tx.inputs.isEmpty())
assertEquals(tx.outputs[0], tx.outputs[0])
@ -259,13 +231,13 @@ class CashTests {
output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2))
// Move fails: not allowed to summon money.
tweak {
command(ALICE_PUBKEY, Cash.Commands.Move())
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "the amounts balance"
}
// Issue works.
tweak {
command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
command(megaCorp.publicKey, Cash.Commands.Issue())
this.verifies()
}
}
@ -275,7 +247,7 @@ class CashTests {
attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount.splitEvenly(2).first()))
command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
command(megaCorp.publicKey, Cash.Commands.Issue())
this `fails with` "output values sum to more than the inputs"
}
@ -284,7 +256,7 @@ class CashTests {
attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID, inState)
command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
command(megaCorp.publicKey, Cash.Commands.Issue())
this `fails with` "output values sum to more than the inputs"
}
@ -293,9 +265,9 @@ class CashTests {
attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2))
command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
command(megaCorp.publicKey, Cash.Commands.Issue())
tweak {
command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
command(megaCorp.publicKey, Cash.Commands.Issue())
this `fails with` "there is only a single issue command"
}
this.verifies()
@ -309,15 +281,15 @@ class CashTests {
@Test(expected = IllegalStateException::class)
fun `reject issuance with inputs`() {
// Issue some cash
var ptx = TransactionBuilder(DUMMY_NOTARY)
var ptx = TransactionBuilder(dummyNotary.party)
Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP, notary = DUMMY_NOTARY)
Cash().generateIssue(ptx, 100.DOLLARS `issued by` miniCorp.ref(12, 34), owner = miniCorp.party, notary = dummyNotary.party)
val tx = miniCorpServices.signInitialTransaction(ptx)
// Include the previously issued cash in a new issuance command
ptx = TransactionBuilder(DUMMY_NOTARY)
ptx = TransactionBuilder(dummyNotary.party)
ptx.addInputState(tx.tx.outRef<Cash.State>(0))
Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP, notary = DUMMY_NOTARY)
Cash().generateIssue(ptx, 100.DOLLARS `issued by` miniCorp.ref(12, 34), owner = miniCorp.party, notary = dummyNotary.party)
}
@Test
@ -325,7 +297,7 @@ class CashTests {
// Splitting value works.
transaction {
attachment(Cash.PROGRAM_ID)
command(ALICE_PUBKEY, Cash.Commands.Move())
command(alice.publicKey, Cash.Commands.Move())
tweak {
input(Cash.PROGRAM_ID, inState)
val splits4 = inState.amount.splitEvenly(4)
@ -356,7 +328,7 @@ class CashTests {
attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID, inState)
input(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer))
command(ALICE_PUBKEY, Cash.Commands.Move())
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "zero sized inputs"
}
transaction {
@ -364,7 +336,7 @@ class CashTests {
input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer))
command(ALICE_PUBKEY, Cash.Commands.Move())
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "zero sized outputs"
}
}
@ -375,8 +347,8 @@ class CashTests {
transaction {
attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP)
command(ALICE_PUBKEY, Cash.Commands.Move())
output(Cash.PROGRAM_ID, outState issuedBy miniCorp.party)
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "the amounts balance"
}
// Can't change deposit reference when splitting.
@ -385,7 +357,7 @@ class CashTests {
val splits2 = inState.amount.splitEvenly(2)
input(Cash.PROGRAM_ID, inState)
for (i in 0..1) output(Cash.PROGRAM_ID, outState.copy(amount = splits2[i]).editDepositRef(i.toByte()))
command(ALICE_PUBKEY, Cash.Commands.Move())
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "the amounts balance"
}
// Can't mix currencies.
@ -394,7 +366,7 @@ class CashTests {
input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID, outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer))
output(Cash.PROGRAM_ID, outState.copy(amount = 200.POUNDS `issued by` defaultIssuer))
command(ALICE_PUBKEY, Cash.Commands.Move())
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "the amounts balance"
}
transaction {
@ -403,18 +375,18 @@ class CashTests {
input(Cash.PROGRAM_ID,
inState.copy(
amount = 150.POUNDS `issued by` defaultIssuer,
owner = AnonymousParty(BOB_PUBKEY)))
owner = AnonymousParty(bob.publicKey)))
output(Cash.PROGRAM_ID, outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer))
command(ALICE_PUBKEY, Cash.Commands.Move())
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "the amounts balance"
}
// Can't have superfluous input states from different issuers.
transaction {
attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID, inState)
input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP)
input(Cash.PROGRAM_ID, inState issuedBy miniCorp.party)
output(Cash.PROGRAM_ID, outState)
command(ALICE_PUBKEY, Cash.Commands.Move())
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "the amounts balance"
}
// Can't combine two different deposits at the same issuer.
@ -423,7 +395,7 @@ class CashTests {
input(Cash.PROGRAM_ID, inState)
input(Cash.PROGRAM_ID, inState.editDepositRef(3))
output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount * 2).editDepositRef(3))
command(ALICE_PUBKEY, Cash.Commands.Move())
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "for reference [01]"
}
}
@ -436,17 +408,17 @@ class CashTests {
input(Cash.PROGRAM_ID, issuerInState)
output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)))
tweak {
command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer))
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
command(megaCorp.publicKey, Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer))
command(megaCorp.publicKey, Cash.Commands.Move())
this `fails with` "the amounts balance"
}
tweak {
command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer))
command(megaCorp.publicKey, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer))
this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command"
tweak {
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
command(megaCorp.publicKey, Cash.Commands.Move())
this.verifies()
}
}
@ -459,14 +431,14 @@ class CashTests {
transaction {
attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID, issuerInState)
input(Cash.PROGRAM_ID, issuerInState.copy(owner = MINI_CORP) issuedBy MINI_CORP)
output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) issuedBy MINI_CORP)
output(Cash.PROGRAM_ID, issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)))
command(listOf(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY), Cash.Commands.Move())
input(Cash.PROGRAM_ID, issuerInState.copy(owner = miniCorp.party) issuedBy miniCorp.party)
output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) issuedBy miniCorp.party)
output(Cash.PROGRAM_ID, issuerInState.copy(owner = miniCorp.party, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)))
command(listOf(megaCorp.publicKey, miniCorp.publicKey), Cash.Commands.Move())
this `fails with` "the amounts balance"
command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer))
command(megaCorp.publicKey, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer))
this `fails with` "the amounts balance"
command(MINI_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` MINI_CORP.ref(defaultRef)))
command(miniCorp.publicKey, Cash.Commands.Exit(200.DOLLARS `issued by` miniCorp.ref(1)))
this.verifies()
}
}
@ -478,8 +450,8 @@ class CashTests {
attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)))
command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer))
command(ALICE_PUBKEY, Cash.Commands.Move())
command(megaCorp.publicKey, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer))
command(alice.publicKey, Cash.Commands.Move())
this `fails with` "the amounts balance"
}
}
@ -490,23 +462,23 @@ class CashTests {
attachment(Cash.PROGRAM_ID)
// Gather 2000 dollars from two different issuers.
input(Cash.PROGRAM_ID, inState)
input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP)
command(ALICE_PUBKEY, Cash.Commands.Move())
input(Cash.PROGRAM_ID, inState issuedBy miniCorp.party)
command(alice.publicKey, Cash.Commands.Move())
// Can't merge them together.
tweak {
output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer))
output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(bob.publicKey), amount = 2000.DOLLARS `issued by` defaultIssuer))
this `fails with` "the amounts balance"
}
// Missing MiniCorp deposit
tweak {
output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY)))
output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY)))
output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(bob.publicKey)))
output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(bob.publicKey)))
this `fails with` "the amounts balance"
}
// This works.
output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY)))
output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY)) issuedBy MINI_CORP)
output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(bob.publicKey)))
output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(bob.publicKey)) issuedBy miniCorp.party)
this.verifies()
}
}
@ -516,12 +488,12 @@ class CashTests {
// Check we can do an atomic currency trade tx.
transaction {
attachment(Cash.PROGRAM_ID)
val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(BOB_PUBKEY))
input(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(ALICE_PUBKEY))
val pounds = Cash.State(658.POUNDS `issued by` miniCorp.ref(3, 4, 5), AnonymousParty(bob.publicKey))
input(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(alice.publicKey))
input(Cash.PROGRAM_ID, pounds)
output(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(BOB_PUBKEY))
output(Cash.PROGRAM_ID, pounds ownedBy AnonymousParty(ALICE_PUBKEY))
command(listOf(ALICE_PUBKEY, BOB_PUBKEY), Cash.Commands.Move())
output(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(bob.publicKey))
output(Cash.PROGRAM_ID, pounds ownedBy AnonymousParty(alice.publicKey))
command(listOf(alice.publicKey, bob.publicKey), Cash.Commands.Move())
this.verifies()
}
}
@ -532,7 +504,7 @@ class CashTests {
private fun makeCash(amount: Amount<Currency>, issuer: AbstractParty, depositRef: Byte = 1) =
StateAndRef(
TransactionState(Cash.State(amount `issued by` issuer.ref(depositRef), ourIdentity), Cash.PROGRAM_ID, DUMMY_NOTARY),
TransactionState(Cash.State(amount `issued by` issuer.ref(depositRef), ourIdentity), Cash.PROGRAM_ID, dummyNotary.party),
StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
)
@ -540,15 +512,15 @@ class CashTests {
* Generate an exit transaction, removing some amount of cash from the ledger.
*/
private fun makeExit(serviceHub: ServiceHub, amount: Amount<Currency>, issuer: Party, depositRef: Byte = 1): WireTransaction {
val tx = TransactionBuilder(DUMMY_NOTARY)
val payChangeTo = serviceHub.keyManagementService.freshKeyAndCert(MINI_CORP_IDENTITY, false).party
Cash().generateExit(tx, Amount(amount.quantity, Issued(issuer.ref(depositRef), amount.token)), WALLET, payChangeTo)
val tx = TransactionBuilder(dummyNotary.party)
val payChangeTo = serviceHub.keyManagementService.freshKeyAndCert(miniCorp.identity, false).party
Cash().generateExit(tx, Amount(amount.quantity, Issued(issuer.ref(depositRef), amount.token)), cashStates, payChangeTo)
return tx.toWireTransaction(serviceHub)
}
private fun makeSpend(services: ServiceHub, amount: Amount<Currency>, dest: AbstractParty): WireTransaction {
val ourIdentity = services.myInfo.singleIdentityAndCert()
val tx = TransactionBuilder(DUMMY_NOTARY)
val tx = TransactionBuilder(dummyNotary.party)
database.transaction {
Cash.generateSpend(services, tx, amount, ourIdentity, dest)
}
@ -560,12 +532,12 @@ class CashTests {
*/
@Test
fun generateSimpleExit() {
val wtx = makeExit(miniCorpServices, 100.DOLLARS, MEGA_CORP, 1)
assertEquals(WALLET[0].ref, wtx.inputs[0])
val wtx = makeExit(miniCorpServices, 100.DOLLARS, megaCorp.party, 1)
assertEquals(cashStates[0].ref, wtx.inputs[0])
assertEquals(0, wtx.outputs.size)
val expectedMove = Cash.Commands.Move()
val expectedExit = Cash.Commands.Exit(Amount(10000, Issued(MEGA_CORP.ref(1), USD)))
val expectedExit = Cash.Commands.Exit(Amount(10000, Issued(megaCorp.ref(1), USD)))
assertEquals(listOf(expectedMove, expectedExit), wtx.commands.map { it.value })
}
@ -575,15 +547,15 @@ class CashTests {
*/
@Test
fun generatePartialExit() {
val wtx = makeExit(miniCorpServices, 50.DOLLARS, MEGA_CORP, 1)
val wtx = makeExit(miniCorpServices, 50.DOLLARS, megaCorp.party, 1)
val actualInput = wtx.inputs.single()
// Filter the available inputs and confirm exactly one has been used
val expectedInputs = WALLET.filter { it.ref == actualInput }
val expectedInputs = cashStates.filter { it.ref == actualInput }
assertEquals(1, expectedInputs.size)
val inputState = expectedInputs.single()
val actualChange = wtx.outputs.single().data as Cash.State
val expectedChangeAmount = inputState.state.data.amount.quantity - 50.DOLLARS.quantity
val expectedChange = WALLET[0].state.data.copy(amount = WALLET[0].state.data.amount.copy(quantity = expectedChangeAmount), owner = actualChange.owner)
val expectedChange = cashStates[0].state.data.copy(amount = cashStates[0].state.data.amount.copy(quantity = expectedChangeAmount), owner = actualChange.owner)
assertEquals(expectedChange, wtx.getOutput(0))
}
@ -592,7 +564,7 @@ class CashTests {
*/
@Test
fun generateAbsentExit() {
assertFailsWith<InsufficientBalanceException> { makeExit(miniCorpServices, 100.POUNDS, MEGA_CORP, 1) }
assertFailsWith<InsufficientBalanceException> { makeExit(miniCorpServices, 100.POUNDS, megaCorp.party, 1) }
}
/**
@ -600,7 +572,7 @@ class CashTests {
*/
@Test
fun generateInvalidReferenceExit() {
assertFailsWith<InsufficientBalanceException> { makeExit(miniCorpServices, 100.POUNDS, MEGA_CORP, 2) }
assertFailsWith<InsufficientBalanceException> { makeExit(miniCorpServices, 100.POUNDS, megaCorp.party, 2) }
}
/**
@ -608,7 +580,7 @@ class CashTests {
*/
@Test
fun generateInsufficientExit() {
assertFailsWith<InsufficientBalanceException> { makeExit(miniCorpServices, 1000.DOLLARS, MEGA_CORP, 1) }
assertFailsWith<InsufficientBalanceException> { makeExit(miniCorpServices, 1000.DOLLARS, megaCorp.party, 1) }
}
/**
@ -616,7 +588,7 @@ class CashTests {
*/
@Test
fun generateOwnerWithNoStatesExit() {
assertFailsWith<InsufficientBalanceException> { makeExit(miniCorpServices, 100.POUNDS, CHARLIE, 1) }
assertFailsWith<InsufficientBalanceException> { makeExit(miniCorpServices, 100.POUNDS, charlie.party, 1) }
}
/**
@ -625,8 +597,8 @@ class CashTests {
@Test
fun generateExitWithEmptyVault() {
assertFailsWith<IllegalArgumentException> {
val tx = TransactionBuilder(DUMMY_NOTARY)
Cash().generateExit(tx, Amount(100, Issued(CHARLIE.ref(1), GBP)), emptyList(), ourIdentity)
val tx = TransactionBuilder(dummyNotary.party)
Cash().generateExit(tx, Amount(100, Issued(charlie.ref(1), GBP)), emptyList(), ourIdentity)
}
}
@ -647,9 +619,8 @@ class CashTests {
@Test
fun generateSimpleSpendWithParties() {
database.transaction {
val tx = TransactionBuilder(DUMMY_NOTARY)
Cash.generateSpend(ourServices, tx, 80.DOLLARS, ourServices.myInfo.singleIdentityAndCert(), ALICE, setOf(MINI_CORP))
val tx = TransactionBuilder(dummyNotary.party)
Cash.generateSpend(ourServices, tx, 80.DOLLARS, ourServices.myInfo.singleIdentityAndCert(), alice.party, setOf(miniCorp.party))
assertEquals(vaultStatesUnconsumed.elementAt(2).ref, tx.inputStates()[0])
}
@ -737,9 +708,9 @@ class CashTests {
*/
@Test
fun aggregation() {
val fiveThousandDollarsFromMega = Cash.State(5000.DOLLARS `issued by` MEGA_CORP.ref(2), MEGA_CORP)
val twoThousandDollarsFromMega = Cash.State(2000.DOLLARS `issued by` MEGA_CORP.ref(2), MINI_CORP)
val oneThousandDollarsFromMini = Cash.State(1000.DOLLARS `issued by` MINI_CORP.ref(3), MEGA_CORP)
val fiveThousandDollarsFromMega = Cash.State(5000.DOLLARS `issued by` megaCorp.ref(2), megaCorp.party)
val twoThousandDollarsFromMega = Cash.State(2000.DOLLARS `issued by` megaCorp.ref(2), miniCorp.party)
val oneThousandDollarsFromMini = Cash.State(1000.DOLLARS `issued by` miniCorp.ref(3), megaCorp.party)
// Obviously it must be possible to aggregate states with themselves
assertEquals(fiveThousandDollarsFromMega.amount.token, fiveThousandDollarsFromMega.amount.token)
@ -753,7 +724,7 @@ class CashTests {
// States cannot be aggregated if the currency differs
assertNotEquals(oneThousandDollarsFromMini.amount.token,
Cash.State(1000.POUNDS `issued by` MINI_CORP.ref(3), MEGA_CORP).amount.token)
Cash.State(1000.POUNDS `issued by` miniCorp.ref(3), megaCorp.party).amount.token)
// States cannot be aggregated if the reference differs
assertNotEquals(fiveThousandDollarsFromMega.amount.token, (fiveThousandDollarsFromMega withDeposit defaultIssuer).amount.token)
@ -763,20 +734,20 @@ class CashTests {
@Test
fun `summing by owner`() {
val states = listOf(
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MINI_CORP),
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP)
Cash.State(1000.DOLLARS `issued by` defaultIssuer, miniCorp.party),
Cash.State(2000.DOLLARS `issued by` defaultIssuer, megaCorp.party),
Cash.State(4000.DOLLARS `issued by` defaultIssuer, megaCorp.party)
)
assertEquals(6000.DOLLARS `issued by` defaultIssuer, states.sumCashBy(MEGA_CORP))
assertEquals(6000.DOLLARS `issued by` defaultIssuer, states.sumCashBy(megaCorp.party))
}
@Test(expected = UnsupportedOperationException::class)
fun `summing by owner throws`() {
val states = listOf(
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP)
Cash.State(2000.DOLLARS `issued by` defaultIssuer, megaCorp.party),
Cash.State(4000.DOLLARS `issued by` defaultIssuer, megaCorp.party)
)
states.sumCashBy(MINI_CORP)
states.sumCashBy(miniCorp.party)
}
@Test
@ -795,9 +766,9 @@ class CashTests {
@Test
fun `summing a single currency`() {
val states = listOf(
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP)
Cash.State(1000.DOLLARS `issued by` defaultIssuer, megaCorp.party),
Cash.State(2000.DOLLARS `issued by` defaultIssuer, megaCorp.party),
Cash.State(4000.DOLLARS `issued by` defaultIssuer, megaCorp.party)
)
// Test that summing everything produces the total number of dollars
val expected = 7000.DOLLARS `issued by` defaultIssuer
@ -808,8 +779,8 @@ class CashTests {
@Test(expected = IllegalArgumentException::class)
fun `summing multiple currencies`() {
val states = listOf(
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
Cash.State(4000.POUNDS `issued by` defaultIssuer, MEGA_CORP)
Cash.State(1000.DOLLARS `issued by` defaultIssuer, megaCorp.party),
Cash.State(4000.POUNDS `issued by` defaultIssuer, megaCorp.party)
)
// Test that summing everything fails because we're mixing units
states.sumCash()
@ -818,23 +789,20 @@ class CashTests {
// Double spend.
@Test
fun chainCashDoubleSpendFailsWith() {
val mockService = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock<IdentityServiceInternal>().also {
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY)
}, MEGA_CORP.name, MEGA_CORP_KEY)
mockService.ledger(DUMMY_NOTARY) {
MockServices(megaCorp).ledger(dummyNotary.party) {
unverifiedTransaction {
attachment(Cash.PROGRAM_ID)
output(Cash.PROGRAM_ID, "MEGA_CORP cash",
Cash.State(
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MEGA_CORP))
amount = 1000.DOLLARS `issued by` megaCorp.ref(1, 1),
owner = megaCorp.party))
}
transaction {
attachment(Cash.PROGRAM_ID)
input("MEGA_CORP cash")
output(Cash.PROGRAM_ID, "MEGA_CORP cash 2", "MEGA_CORP cash".output<Cash.State>().copy(owner = AnonymousParty(ALICE_PUBKEY)))
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
output(Cash.PROGRAM_ID, "MEGA_CORP cash 2", "MEGA_CORP cash".output<Cash.State>().copy(owner = AnonymousParty(alice.publicKey)))
command(megaCorp.publicKey, Cash.Commands.Move())
this.verifies()
}
@ -843,8 +811,8 @@ class CashTests {
attachment(Cash.PROGRAM_ID)
input("MEGA_CORP cash")
// We send it to another pubkey so that the transaction is not identical to the previous one
output(Cash.PROGRAM_ID, "MEGA_CORP cash 3", "MEGA_CORP cash".output<Cash.State>().copy(owner = ALICE))
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
output(Cash.PROGRAM_ID, "MEGA_CORP cash 3", "MEGA_CORP cash".output<Cash.State>().copy(owner = alice.party))
command(megaCorp.publicKey, Cash.Commands.Move())
this.verifies()
}
this.fails()
@ -856,11 +824,11 @@ class CashTests {
@Test
fun multiSpend() {
val tx = TransactionBuilder(DUMMY_NOTARY)
val tx = TransactionBuilder(dummyNotary.party)
database.transaction {
val payments = listOf(
PartyAndAmount(miniCorpAnonymised, 400.DOLLARS),
PartyAndAmount(CHARLIE_ANONYMISED, 150.DOLLARS)
PartyAndAmount(charlie.party.anonymise(), 150.DOLLARS)
)
Cash.generateSpend(ourServices, tx, payments, ourServices.myInfo.singleIdentityAndCert())
}
@ -871,9 +839,9 @@ class CashTests {
assertEquals(320.DOLLARS, out(1).amount.withoutIssuer())
assertEquals(150.DOLLARS, out(2).amount.withoutIssuer())
assertEquals(30.DOLLARS, out(3).amount.withoutIssuer())
assertEquals(MINI_CORP, out(0).amount.token.issuer.party)
assertEquals(MEGA_CORP, out(1).amount.token.issuer.party)
assertEquals(MEGA_CORP, out(2).amount.token.issuer.party)
assertEquals(MEGA_CORP, out(3).amount.token.issuer.party)
assertEquals(miniCorp.party, out(0).amount.token.issuer.party)
assertEquals(megaCorp.party, out(1).amount.token.issuer.party)
assertEquals(megaCorp.party, out(2).amount.token.issuer.party)
assertEquals(megaCorp.party, out(3).amount.token.issuer.party)
}
}

View File

@ -19,9 +19,9 @@ import net.corda.finance.contracts.Commodity
import net.corda.finance.contracts.NetType
import net.corda.finance.contracts.asset.Obligation.Lifecycle
import net.corda.node.services.api.IdentityServiceInternal
import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState
import net.corda.testing.core.*
import net.corda.testing.dsl.*
import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices

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