From d1dcbd979339bed6a9cca193341bc17ca9d18bcb Mon Sep 17 00:00:00 2001 From: Ed Prosser Date: Thu, 22 Aug 2019 15:05:59 +0100 Subject: [PATCH 1/3] Fixed code block links Signed-off-by: Ed Prosser --- docs/source/testnet-explorer-corda.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/testnet-explorer-corda.rst b/docs/source/testnet-explorer-corda.rst index 56f7ea47d3..b25da0e5a9 100644 --- a/docs/source/testnet-explorer-corda.rst +++ b/docs/source/testnet-explorer-corda.rst @@ -34,8 +34,8 @@ couple of resources. .. code:: bash - wget https://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda/corda-finance-contracts-|corda_version|-corda/corda-finance-contracts-|corda_version|-corda.jar - wget https://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda/corda-finance-workflows-|corda_version|-corda/corda-finance-workflows-|corda_version|-corda.jar + wget https://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda/corda-finance-contracts/|corda_version|/corda-finance-contracts-|corda_version|.jar + wget https://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda/corda-finance-workflows/|corda_version|/corda-finance-workflows-|corda_version|.jar This is required to run some flows to check your connections, and to issue/transfer cash to counterparties. Copy it to the Corda installation location: From f09bff9c0fb8ec393197106f32426e04d08b7bc8 Mon Sep 17 00:00:00 2001 From: josecoll Date: Thu, 29 Aug 2019 09:55:21 +0100 Subject: [PATCH 2/3] Make concurrent updates to contractStateTypeMappings thread safe. (#5410) --- .../node/services/vault/NodeVaultService.kt | 12 +++++-- .../services/vault/NodeVaultServiceTest.kt | 34 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index 5480c0264b..309553bb17 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -28,6 +28,8 @@ import java.security.PublicKey import java.time.Clock import java.time.Instant import java.util.* +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.CopyOnWriteArraySet import javax.persistence.Tuple import javax.persistence.criteria.CriteriaBuilder import javax.persistence.criteria.CriteriaUpdate @@ -91,7 +93,8 @@ class NodeVaultService( * Maintain a list of contract state interfaces to concrete types stored in the vault * for usage in generic queries of type queryBy or queryBy> */ - private val contractStateTypeMappings = mutableMapOf>().toSynchronised() + @VisibleForTesting + internal val contractStateTypeMappings = ConcurrentHashMap>() override fun start() { bootstrapContractStateTypes() @@ -103,7 +106,7 @@ class NodeVaultService( if (!seen) { val contractTypes = deriveContractTypes(concreteType) contractTypes.map { - val contractStateType = contractStateTypeMappings.getOrPut(it.name) { mutableSetOf() } + val contractStateType = contractStateTypeMappings.getOrPut(it.name) { CopyOnWriteArraySet() } contractStateType.add(concreteType.name) } } @@ -203,6 +206,9 @@ class NodeVaultService( override val updates: Observable> get() = mutex.locked { _updatesInDbTx } + @VisibleForTesting + internal val publishUpdates get() = mutex.locked { updatesPublisher } + /** Groups adjacent transactions into batches to generate separate net updates per transaction type. */ override fun notifyAll(statesToRecord: StatesToRecord, txns: Iterable) { if (statesToRecord == StatesToRecord.NONE || !txns.any()) return @@ -716,7 +722,7 @@ class NodeVaultService( concreteType?.let { val contractTypes = deriveContractTypes(it) contractTypes.map { - val contractStateType = contractStateTypeMappings.getOrPut(it.name) { mutableSetOf() } + val contractStateType = contractStateTypeMappings.getOrPut(it.name) { CopyOnWriteArraySet() } contractStateType.add(concreteType.name) } } diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index a9c50a3ede..1da96f4213 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -4,6 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import com.nhaarman.mockito_kotlin.* import net.corda.core.contracts.* import net.corda.core.crypto.NullKeys +import net.corda.core.crypto.SecureHash import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.* import net.corda.core.internal.NotaryChangeTransactionBuilder @@ -19,6 +20,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.toNonEmptySet import net.corda.finance.* import net.corda.finance.contracts.asset.Cash @@ -945,4 +947,36 @@ class NodeVaultServiceTest { assertTrue(it) } } + + @Test + fun `test concurrent update of contract state type mappings`() { + // no registered contract state types at start-up. + assertEquals(0, vaultService.contractStateTypeMappings.size) + + fun makeCash(amount: Amount, issuer: AbstractParty, depositRef: Byte = 1) = + StateAndRef( + TransactionState(Cash.State(amount `issued by` issuer.ref(depositRef), identity.party), Cash.PROGRAM_ID, DUMMY_NOTARY, constraint = AlwaysAcceptAttachmentConstraint), + StateRef(SecureHash.randomSHA256(), Random().nextInt(32)) + ) + + val cashIssued = setOf>(makeCash(100.DOLLARS, dummyCashIssuer.party)) + val cashUpdate = Vault.Update(emptySet(), cashIssued) + + val service = Executors.newFixedThreadPool(10) + (1..100).map { + service.submit { + database.transaction { + vaultService.publishUpdates.onNext(cashUpdate) + } + } + }.forEach { it.getOrThrow() } + + vaultService.contractStateTypeMappings.forEach { + println("${it.key} = ${it.value}") + } + // Cash.State and its superclasses and interfaces: FungibleAsset, FungibleState, OwnableState, QueryableState + assertEquals(4, vaultService.contractStateTypeMappings.size) + + service.shutdown() + } } From 80d64a957410f409d749a272e3b0f80836e260cb Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Mon, 9 Sep 2019 10:26:37 +0100 Subject: [PATCH 3/3] ENT-3916 - document testing CorDapp upgrades. (#5421) --- docs/source/upgrading-cordapps.rst | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/source/upgrading-cordapps.rst b/docs/source/upgrading-cordapps.rst index 6b28ec1fd9..5e519a20d3 100644 --- a/docs/source/upgrading-cordapps.rst +++ b/docs/source/upgrading-cordapps.rst @@ -641,3 +641,29 @@ Although not strictly related to versioning, AMQP serialisation dictates that we wildcard * Any superclass must adhere to the same rules, but can be abstract * Object graph cycles are not supported, so an object cannot refer to itself, directly or indirectly + + +Testing CorDapp upgrades +------------------------ + +At the time of this writing there is no platform support to test CorDapp upgrades. There are plans to add support in a future version. +This means that it is not possible to write automated tests using just the provided tooling. + +To test an implicit upgrade, you must simulate a network that initially has only nodes with the old version of the CorDapp, and then nodes gradually transition to the new version. +Typically, in such a complex upgrade scenario, there must be a deadline by which time all nodes that want to continue to use the CorDapp must upgrade. +To achieve this, this deadline must be configured in the flow logic which must only use new features afterwards. + +This can be simulated with a scenario like this: + +1. Write and individually test the new version of the state and contract. +2. Setup a network of nodes with the previous version. In the simplest form, `deployNodes` can be used for this purpose. +3. Run some transactions between nodes. +4. Upgrade a couple of nodes to the new version of the CorDapp. +5. Continue running transactions between various combinations of versions. Also make sure transactions that were created between nodes with the new version +are being successfully read by nodes with the old CorDapp. +6. Upgrade all nodes and simulate the deadline expiration. +7. Make sure old transactions can be consumed, and new features are successfully used in new transactions. + + + +