16 KiB
Upgrading a CorDapp to a new platform version
These notes provide instructions for upgrading your CorDapps from previous versions to V3.0 Developer Preview <changelog_r3_v3>
of R3 Corda (Enterprise Blockchain).
General rules
Always remember to update the version identifiers in your project gradle file. For example, Corda V3.0 uses:
ext.corda_release_version = 'corda-3.0'
ext.corda_release_distribution = 'net.corda'
ext.corda_gradle_plugins_version = '3.0.9'
It may be necessary to update the version of major dependencies. This will be clearly stated in the upgrade notes for a particular version. For example, Corda V3.0 uses:
ext.kotlin_version = '1.1.60'
ext.quasar_version = '0.7.9'
Please consult the relevant release notes of the release in question. If not specified, you may assume the versions you are currently using are still in force.
We also strongly recommend cross referencing with the changelog
to confirm changes.
Upgrading to R3 Corda V3.0 Developer Preview
A prerequisite to upgrade to R3 Corda V3.0 is to ensure your CorDapp is upgraded to Open Source Corda V3.x. Please follow the instructions in the "Upgrading to V3.x" section to complete this initial step.
Upgrading to R3 Corda is now a simple task of updating the version identifiers as follows:
ext.corda_release_distribution = 'com.r3.corda' // R3 Corda
ext.corda_release_version = 'R3.CORDA-3.0.0-DEV-PREVIEW-3' // R3 Corda
ext.corda_gradle_plugins_version = '4.0.9'
and specifying an additional repository entry to point to the location of the R3 Corda distribution:
repositories {
maven {
credentials {
username "r3-corda-dev-preview"
password "XXXXX"
}
url 'https://ci-artifactory.corda.r3cev.com/artifactory/r3-corda-releases'
}
}
Upgrading to V3.x
Please refer to:
Build
- Update the version identifiers in your project gradle file(s):
ext.corda_release_version = 'corda-3.0' // Corda (Open Source)
ext.corda_gradle_plugins_version = '4.0.9'
ext.kotlin_version = '1.2.20'
- Add a new release identifier to specify the corda distribution type (Open Source or R3 Corda):
ext.corda_release_distribution = 'net.corda' // Corda (Open Source)
- Corda plugins have been modularised further so the following additional gradle entries are necessary:
dependencies {
classpath "net.corda.plugins:cordapp:$corda_gradle_plugins_version"
}
apply plugin: 'net.corda.plugins.cordapp'
The plugin needs to be applied in all gradle build files where there is a dependency on Corda using any of:
cordaCompile, cordaRuntime, cordapp
- If you use the Corda quasar-utils plugin (required for testing Corda flows), it is necessary to specify the following identifier information in addition to the dependencies and apply directives: (note, this relates to Developer Preview 3 only and will be resolved in the GA release)
ext.quasar_group = 'com.github.corda.quasar'
ext.quasar_version = '7629695563deae6cc95adcfbebcbc8322fd0241a'
in addition to:
dependencies {
classpath "net.corda.plugins:quasar-utils:$corda_gradle_plugins_version"
}
apply plugin: 'net.corda.plugins.quasar-utils'
- Corda Gradle plugins require Gradle version 4.1 or above
- All gradle compile, test, and run-time dependencies (except gradle plugins) to Corda artifacts should now use the
corda_release_distribution
variable (was previously hardcoded to usenet.corda
):
dependencies {
// Corda integration dependencies
cordaCompile "$corda_release_distribution:corda-core:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-finance:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-jackson:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-rpc:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-node-api:$corda_release_version"
cordaCompile "$corda_release_distribution:corda-webserver-impl:$corda_release_version"
cordaRuntime "$corda_release_distribution:corda:$corda_release_version"
cordaRuntime "$corda_release_distribution:corda-webserver:$corda_release_version"
testCompile "$corda_release_distribution:corda-node-driver:$corda_release_version"
}
- 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:
@Entity
@Table(name = "cash_states_v2",
"ccy_code_idx2", columnList = "ccy_code")))
indexes = arrayOf(Index(name = class PersistentCashState(
ElementCollection
@Column(name = "participants")
@name="cash_states_v2_participants", joinColumns = arrayOf(
@CollectionTable("output_index", referencedColumnName = "output_index"),
JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id")))
JoinColumn(name = override var participants: MutableSet<AbstractParty>? = null,
Configuration
Applies to both gradle deployNodes tasks and/or corda node configuration (node.conf).
- Remove any references to
networkMap
.
networkMap "O=Agent,L=Dallas,C=US"
- Remove any references to
advertisedServices
(including notaries).
advertisedServices = ["corda.notary.validating"]
- Add an explicit notary definition in the Notary node configuration only:
notary = [validating : true]
For existing contract ORM schemas that extend from
CommonSchemaV1.LinearState
orCommonSchemaV1.FungibleState
, you will need to explicitly map theparticipants
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:- Add the
override var participants: MutableSet<AbstractParty>? = null
field to your class, and - Add JPA mappings
For example:
@Entity @Table(name = "cash_states_v2", "ccy_code_idx2", columnList = "ccy_code"))) indexes = arrayOf(Index(name = class PersistentCashState( ElementCollection @Column(name = "participants") @name="cash_states_v2_participants", joinColumns = arrayOf( @CollectionTable("output_index", referencedColumnName = "output_index"), JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))) JoinColumn(name = override var participants: MutableSet<AbstractParty>? = null,
- Add the
Shell - to use Shell ensure
rpcSettings.address
andrpcSettings.adminAddress
settings are present.
Databases
Drivers
- Alternative JDBC drivers are not bundled as part of R3 Corda releases. If you are running a node on a database different from H2 you need to provide the associated driver as described in
node-database
.
Testing
Test Framework API stabilisation changes (introduced in Corda V3.0) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MockNetwork API usage has been greatly simplified.
All references to
StartedNode<MockNode>
orStartedNode<MockNetwork.MockNode>
now becomeStartedMockNode
Calling a flow on a MockNode now becomes
myMockNode.startFlow(myFlow)
MockNode transaction demarcation has been simplified.
All references to
myMockNode.database.transaction { ... }
now becomemyMockNode.transaction { ... }
Please also see API Testing
Contract tests
You must now create a
MockServices
object.MockServices
provides a mock identity, key and storage service.MockServices
takes as its first argument a list of the CorDapp packages to scan:private val ledgerServices = MockServices(listOf("net.corda.examples.obligation", "net.corda.testing.contracts"))
MockServices
replaces the use ofsetCordappPackages
andunsetCordappPackages
.ledger
is now defined as aMockServices
method. This means that:ledger {
Becomes:
ledgerServices.ledger {
Within a mock ledger transaction,
ContractState
instances are passed toinput
andoutput
as objects rather than lambdas. For example:ledgerServices.ledger { transaction { input(OBLIGATION_CONTRACT_ID, DummyState()) output(OBLIGATION_CONTRACT_ID, oneDollarObligation) } }
Within a mock ledger transaction,
CommandData
instances are passed toinput
andoutput
as objects rather than lambdas, and the public keys must be passed as a list if there is more than one. For example:ledgerServices.ledger { transaction { command(alice.publicKey, ObligationContract.Commands.Issue()) command(listOf(alice.publicKey, bob.publicKey), ObligationContract.Commands.Issue()) } }
The predefined test identities (e.g.
ALICE
andMINI_CORP
) have been removed.You must now define the test identities explicitly. For example:
val alice = TestIdentity(CordaX500Name(organisation = "Alice", locality = "TestLand", country = "GB"))
TestIdentity
exposes methods to get thename
,keyPair
,publicKey
,party
andidentity
of the underlyingTestIdentity
Explicit invocation of transaction transformation (ie. using
TransactionBuilder
) requires serialization engine to be initialized. In unit test this can be achieved by using the following jUnit rule:@Rule @JvmField val testSerialization = SerializationEnvironmentRule()
Flow tests
The registration mechanism for CorDapps in
MockNetwork
unit tests has changed:- CorDapp registration is now done via the
cordappPackages
constructor parameter of MockNetwork. This parameter is a list ofString
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
Creation of Notaries in
MockNetwork
unit tests has changed.Previously the API call
createNotaryNode(legalName = CordaX500ame(...))
would be used to create a notary:val notary = mockNetwork.createNotaryNode(legalName = CordaX500Name("Notary", "London", "UK"))
Notaries are now defined as part of
MockNetwork
creation using a newMockNetworkNotarySpec
class, as in the following example:"Notary","London","UK")))) mockNetwork = MockNetwork(notarySpecs = listOf(MockNetworkNotarySpec(CordaX500Name(
A notary is no longer specified when creating a standard node using the
createPartyNode
API call.Previously:
"Node", "Madrid", "ES")) mockNetwork.createPartyNode(notary.network.myAddress, CordaX500Name(
Becomes:
"Node", "Madrid", "ES")) mockNetwork.createPartyNode(CordaX500Name(
Utility node creation API method
createSomeNodes(...)
has been removed, and nodes must be created individually.Previously:
BasketOfNodes nodes = net.createSomeNodes(3); MockNetwork.getPartyNodes().get(0); nodeA = nodes.getPartyNodes().get(1); nodeB = nodes.getPartyNodes().get(2); nodeC = nodes.
Becomes:
createNode(new MockNodeParameters()); nodeA = net.createNode(new MockNodeParameters()); nodeB = net.createNode(new MockNodeParameters()); nodeC = net.List<StartedNode<MockNode>> nodes = Arrays.asList(nodeA, nodeB, nodeC);
Flow framework instantiation of a flow has a slight variation in start syntax:
Previously:
getServices().startFlow(flow).getResultFuture(); CordaFuture<SignedTransaction> future = nodeA.
Becomes:
startFlow(nodeA.getServices(), flow).getResultFuture(); CordaFuture<SignedTransaction> future =
StartedNodeServices.startFlow
must now be imported fromnet.corda.testing.node
Do not use
node.internals
to register flows:Previous code would often look as follows:
protected fun registerFlowsAndServices(node: StartedNode<MockNetwork.MockNode>) { val mockNode = node.internals class.java) mockNode.registerInitiatedFlow(MyCustomFlow:: }
Becomes:
protected fun registerFlowsAndServices(mockNode: StartedNode<MockNetwork.MockNode>) { class.java) mockNode.registerInitiatedFlow(MyCustomFlow:: }
Do not use
node.internals
to register Corda servicesPreviously:
class.java) node.internals.installCordaService(CustomService::
Becomes:
class.java) node.services.cordaService(CustomService::
Better yet, use node factory to organize both register flows and services, for example, create class as follows:
class PrimesOracleNode(args: MockNodeArgs) : MockNetwork.MockNode(args) { override fun start() = super.start().apply { class.java) registerInitiatedFlow(QueryHandler::SignHandler::class.java) registerInitiatedFlow(net.corda.examples.oracle.service.service.Oracle::class.java) services.cordaService( } }
and then pass it to
createNode
:val oracle = mockNet.createNode(MockNodeParameters(legalName = CordaX500Name("Oracle", "New York", "US")), ::PrimesOracleNode)
Node driver
Driver instantiation now uses a new
DriverParameters
data class to encapsulate all available driver options.For example, previously:
true, waitForAllNodesToFinish = true) { ... driver(isDebug =
Becomes:
true, waitForAllNodesToFinish = true)) { ... driver(DriverParameters(isDebug =
User
has been moved fromnet.corda.nodeapi.User
tonet.corda.nodeapi.internal.config.User
Notaries are defined by passing a list of
NotarySpec
objects todriver
using thenotarySpecs
argument, instead of being defined manually in the driver block.notarySpecs
defaults to providing a single validating notaryThe
waitForAllNodesToFinish
function has been removed. It has been replaced with awaitForAllNodesToFinish
argument todriver
No longer specify advertised services to the
DriverDSL
when starting nodes:Previously:
driver {"Controller", "London", "GB"), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))) startNode(providedName = CordaX500Name(
Becomes:
driver {"Controller", "London", "GB")), startNode(providedName = CordaX500Name(
Finance
CASH_PROGRAM_ID
has been moved toCash.PROGRAM_ID
, whereCash
is defined in theimport net.corda.finance.contracts.asset
package