mirror of
https://github.com/corda/corda.git
synced 2025-01-14 08:49:47 +00:00
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:
commit
4b86362391
15
.idea/runConfigurations/Explorer___demo_nodes.xml
generated
15
.idea/runConfigurations/Explorer___demo_nodes.xml
generated
@ -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>
|
@ -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>
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -47,6 +47,7 @@ public class StandaloneCordaRPCJavaClientTest {
|
||||
port.getAndIncrement(),
|
||||
port.getAndIncrement(),
|
||||
port.getAndIncrement(),
|
||||
port.getAndIncrement(),
|
||||
true,
|
||||
Collections.singletonList(rpcUser),
|
||||
true,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
gradlePluginsVersion=3.0.3
|
||||
gradlePluginsVersion=3.0.4
|
||||
kotlinVersion=1.1.60
|
||||
platformVersion=3
|
||||
guavaVersion=21.0
|
||||
|
@ -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).
|
||||
|
@ -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}"
|
||||
}
|
||||
|
@ -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}")
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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. */
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()) }
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,8 +20,14 @@ 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 trustStore = X509KeyStore("password").apply {
|
||||
setCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
|
||||
}
|
||||
|
||||
val keyStore = X509KeyStore("password").apply {
|
||||
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val nodeCaCert = X509Utilities.createCertificate(
|
||||
CertificateType.NODE_CA,
|
||||
@ -29,12 +36,6 @@ class X509NameConstraintsTest {
|
||||
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 tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val tlsCert = X509Utilities.createCertificate(
|
||||
CertificateType.TLS,
|
||||
@ -42,14 +43,9 @@ class X509NameConstraintsTest {
|
||||
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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
||||
X509KeyStore.fromFile(keyStoreFile, storePassword, createNew = true).update {
|
||||
setCertificate(alias, original)
|
||||
}
|
||||
|
||||
// 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)
|
||||
val copy = X509KeyStore.fromFile(keyStoreFile, storePassword).getCertificate(alias)
|
||||
assertThat(copy).isEqualTo(original)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -5,40 +5,42 @@ 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")
|
||||
}
|
||||
|
||||
[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/")
|
||||
}
|
||||
@ -48,6 +50,13 @@ task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
|
||||
externalDocumentationLink {
|
||||
url = new URL("http://www.bouncycastle.org/docs/docs1.5on/")
|
||||
}
|
||||
internalPackagePrefixes.collect { packagePrefix ->
|
||||
packageOptions {
|
||||
prefix = packagePrefix
|
||||
suppress = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task buildDocs(dependsOn: ['apidocs', 'makeDocs'])
|
||||
|
@ -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.
|
||||
|
@ -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
72
docs/source/aws-vm.rst
Normal 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>`_
|
@ -11,6 +11,7 @@ CorDapps
|
||||
cordapp-build-systems
|
||||
building-against-master
|
||||
corda-api
|
||||
secure-coding-guidelines
|
||||
flow-cookbook
|
||||
cheat-sheet
|
||||
building-a-cordapp-samples
|
@ -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
|
||||
.. 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
|
||||
.. 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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
64
docs/source/contributing.rst
Normal file
64
docs/source/contributing.rst
Normal 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
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.*
|
||||
|
||||
|
@ -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 ] }
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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 ->
|
||||
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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
-------
|
||||
|
@ -4,8 +4,6 @@ Other
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
json
|
||||
secure-coding-guidelines
|
||||
corda-repo-layout
|
||||
building-the-docs
|
||||
codestyle
|
||||
json
|
@ -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/>
|
@ -6,5 +6,6 @@ Release process
|
||||
|
||||
release-notes
|
||||
changelog
|
||||
contributing
|
||||
codestyle
|
||||
testing
|
@ -8,4 +8,5 @@ Tools
|
||||
demobench
|
||||
node-explorer
|
||||
azure-vm
|
||||
aws-vm
|
||||
loadtesting
|
@ -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)),
|
||||
|
@ -140,13 +140,16 @@ 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
|
||||
|
||||
For example:
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Entity
|
||||
@Table(name = "cash_states_v2",
|
||||
@ -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,10 +400,10 @@ 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'
|
||||
@ -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"
|
||||
|
||||
.. 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
|
||||
* 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 node’s 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 node’s 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``)
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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 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 = bigCorpServices.addSignature(ptx)
|
||||
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()
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user