mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
Merge branch 'release/os/4.1' into tudor_merge_4.1_to_4.3
# Conflicts: # node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt
This commit is contained in:
commit
ea73dcfb22
@ -34,8 +34,8 @@ couple of resources.
|
||||
|
||||
.. code-block:: 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:
|
||||
|
@ -719,3 +719,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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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<LinearState> or queryBy<FungibleState<*>>
|
||||
*/
|
||||
private val contractStateTypeMappings = mutableMapOf<String, MutableSet<String>>().toSynchronised()
|
||||
@VisibleForTesting
|
||||
internal val contractStateTypeMappings = ConcurrentHashMap<String, MutableSet<String>>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -207,6 +210,9 @@ class NodeVaultService(
|
||||
override val updates: Observable<Vault.Update<ContractState>>
|
||||
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<CoreTransaction>, previouslySeenTxns: Iterable<CoreTransaction>) {
|
||||
if (statesToRecord == StatesToRecord.NONE || (!txns.any() && !previouslySeenTxns.any())) return
|
||||
@ -738,7 +744,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)
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
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
|
||||
@ -22,6 +23,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
|
||||
@ -951,4 +953,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<Currency>, 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<StateAndRef<ContractState>>(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()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user