From 37b0b18c8209ea2a4ed9def286749296b981870f Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Mon, 15 Jan 2018 10:57:22 +0000 Subject: [PATCH] Updates the documentation on versioning. --- docs/source/building-a-cordapp-index.rst | 1 + docs/source/resources/flow-interface.png | Bin 0 -> 35805 bytes docs/source/upgrade-notes.rst | 4 +- docs/source/upgrading-cordapps.rst | 417 +++++++++++++++++++++++ docs/source/versioning.rst | 46 --- 5 files changed, 420 insertions(+), 48 deletions(-) create mode 100644 docs/source/resources/flow-interface.png create mode 100644 docs/source/upgrading-cordapps.rst diff --git a/docs/source/building-a-cordapp-index.rst b/docs/source/building-a-cordapp-index.rst index c8c376bd29..3a0e5d8d46 100644 --- a/docs/source/building-a-cordapp-index.rst +++ b/docs/source/building-a-cordapp-index.rst @@ -7,6 +7,7 @@ CorDapps cordapp-overview writing-a-cordapp upgrade-notes + upgrading-cordapps cordapp-build-systems building-against-master api-index diff --git a/docs/source/resources/flow-interface.png b/docs/source/resources/flow-interface.png new file mode 100644 index 0000000000000000000000000000000000000000..ab38ad3d2b88313a57b14c120fc6756957c265d8 GIT binary patch literal 35805 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 literal 0 HcmV?d00001 diff --git a/docs/source/upgrade-notes.rst b/docs/source/upgrade-notes.rst index a6b5f829e3..f716dda97c 100644 --- a/docs/source/upgrade-notes.rst +++ b/docs/source/upgrade-notes.rst @@ -1,5 +1,5 @@ -Upgrading a CorDapp to a new version -==================================== +Upgrading a CorDapp to a new platform version +============================================= These notes provide instructions for upgrading your CorDapps from previous versions, starting with the upgrade from our first public Beta (:ref:`Milestone 12 `), to :ref:`V1.0 `. diff --git a/docs/source/upgrading-cordapps.rst b/docs/source/upgrading-cordapps.rst new file mode 100644 index 0000000000..f7f8a6b038 --- /dev/null +++ b/docs/source/upgrading-cordapps.rst @@ -0,0 +1,417 @@ +Upgrading a CorDapp (outside of platform version upgrades) +========================================================== + +.. note:: This document only concerns the upgrading of CorDapps and not the Corda platform itself (wire format, node + database schemas, etc.). + +.. contents:: + +CorDapp versioning +------------------ +The Corda platform does not mandate a version number on a per-CorDapp basis. Different elements of a CorDapp are +allowed to evolve separately: + +* States +* Contracts +* Services +* Flows +* Utilities and library functions +* All, or a subset, of the above + +Sometimes, however, a change to one element will require changes to other elements. For example, changing a shared data +structure may require flow changes that are not backwards-compatible. + +Areas of consideration +---------------------- +This document will consider the following types of versioning: + +* Flow versioning +* State and contract versioning +* State and state schema versioning +* Serialisation of custom types + +Flow versioning +--------------- +Any flow that initiates other flows must be annotated with the ``@InitiatingFlow`` annotation, which is defined as: + +.. sourcecode:: kotlin + + annotation class InitiatingFlow(val version: Int = 1) + +The ``version`` property, which defaults to 1, specifies the flow's version. This integer value should be incremented +whenever there is a release of a flow which has changes that are not backwards-compatible. A non-backwards compatible +change is one that changes the interface of the flow. + +Currently, CorDapp developers have to explicitly write logic to handle these flow version numbers. In the future, +however, the platform will use prescribed rules for handling versions. + +What defines the interface of a flow? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The flow interface is defined by the sequence of ``send`` and ``receive`` calls between an ``InitiatingFlow`` and an +``InitiatedBy`` flow, including the types of the data sent and received. We can picture a flow's interface as follows: + +.. image:: resources/flow-interface.png + :scale: 50% + :align: center + +In the diagram above, the ``InitiatingFlow``: + +* Sends an ``Int`` +* Receives a ``String`` +* Sends a ``String`` +* Receives a ``CustomType`` + +The ``InitiatedBy`` flow does the opposite: + +* Receives an ``Int`` +* Sends a ``String`` +* Receives a ``String`` +* Sends a ``CustomType`` + +As long as both the ``IntiatingFlow`` and the ``InitiatedBy`` flows conform to the sequence of actions, the flows can +be implemented in any way you see fit (including adding proprietary business logic that is not shared with other +parties). + +What constitutes a non-backwards compatible flow change? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A flow can become backwards-incompatible in two main ways: + +* The sequence of ``send`` and ``receive`` calls changes: + + * A ``send`` or ``receive`` is added or removed from either the ``InitatingFlow`` or ``InitiatedBy`` flow + * The sequence of ``send`` and ``receive`` calls changes + +* The types of the ``send`` and ``receive`` calls changes + +What happens when running flows with incompatible versions? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Pairs of ``InitiatingFlow`` flows and ``InitiatedBy`` flows that have incompatible interfaces are likely to exhibit the +following behaviour: + +* The flows hang indefinitely and never terminate, usually because a flow expects a response which is never sent from + the other side +* One of the flow ends with an exception: "Expected Type X but Received Type Y", because the ``send`` or ``receive`` + types are incorrect +* One of the flows ends with an exception: "Counterparty flow terminated early on the other side", because one flow + sends some data to another flow, but the latter flow has already ended + +How do I upgrade my flows? +~~~~~~~~~~~~~~~~~~~~~~~~~~ +For flag-day upgrades, the process is simple. + +Assumptions +^^^^^^^^^^^ + +* All nodes in the business network can be shut down for a period of time +* All nodes retire the old flows and adopt the new flows at the same time + +Process +^^^^^^^ + +1. Update the flow and test the changes. Increment the flow version number in the ``InitiatingFlow`` annotation +2. Ensure that all versions of the existing flow have finished running and there are no pending ``SchedulableFlows`` on + any of the nodes on the business network +3. Shut down all the nodes +4. Replace the existing CorDapp JAR with the CorDapp JAR containing the new flow +5. Start the nodes + +From this point onwards, all the nodes will be using the updated flows. + +In situations where some nodes may still be using previous versions of a flow, the updated flows need to be +backwards-compatible. + +How do I ensure flow backwards-compatibility? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``InitiatingFlow`` version number is included in the flow session handshake and exposed to both parties via the +``FlowLogic.getFlowContext`` method. This method takes a ``Party`` and returns a ``FlowContext`` object which describes +the flow running on the other side. In particular, it has a ``flowVersion`` property which can be used to +programmatically evolve flows across versions. For example: + +.. sourcecode:: kotlin + + @Suspendable + override fun call() { + val otherFlowVersion = otherSession.getCounterpartyFlowInfo().flowVersion + val receivedString = if (otherFlowVersion == 1) { + receive(otherParty).unwrap { it.toString() } + } else { + receive(otherParty).unwrap { it } + } + } + +This code shows a flow that in its first version expected to receive an Int, but in subsequent versions was modified to +expect a String. This flow is still able to communicate with parties that are running the older CorDapp containing +the older flow. + +How do I deal with interface changes to inlined subflows? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Here is an example of an in-lined subflow: + +.. sourcecode:: kotlin + + @StartableByRPC + @InitiatingFlow + class FlowA(val recipient: Party) : FlowLogic() { + @Suspendable + override fun call() { + subFlow(FlowB(recipient)) + } + } + + @InitiatedBy(FlowA::class) + class FlowC(val otherSession: FlowSession) : FlowLogic() { + // Omitted. + } + + // Note: No annotations. This is used as an inlined subflow. + class FlowB(val recipient: Party) : FlowLogic() { + @Suspendable + override fun call() { + val message = "I'm an inlined subflow, so I inherit the @InitiatingFlow's session ID and type." + initiateFlow(recipient).send(message) + } + } + +Inlined subflows are treated as being the flow that invoked them when initiating a new flow session with a counterparty. +Suppose flow ``A`` calls inlined subflow B, which, in turn, initiates a session with a counterparty. The ``FlowLogic`` +type used by the counterparty to determine which counter-flow to invoke is determined by ``A``, and not by ``B``. This +means that the response logic for the inlined flow must be implemented explicitly in the ``InitiatedBy`` flow. This can +be done either by calling a matching inlined counter-flow, or by implementing the other side explicitly in the +initiated parent flow. Inlined subflows also inherit the session IDs of their parent flow. + +As such, an interface change to an inlined subflow must be considered a change to the parent flow interfaces. + +An example of an inlined subflow is ``CollectSignaturesFlow``. It has a response flow called ``SignTransactionFlow`` +that isn’t annotated with ``InitiatedBy``. This is because both of these flows are inlined. How these flows speak to +one another is defined by the parent flows that call ``CollectSignaturesFlow`` and ``SignTransactionFlow``. + +In code, inlined subflows appear as regular ``FlowLogic`` instances without either an ``InitiatingFlow`` or an +``InitiatedBy`` annotation. + +Inlined flows are not versioned, as they inherit the version of their parent ``InitiatingFlow`` or ``InitiatedBy`` +flow. + +Are there any other considerations? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Suspended flows +^^^^^^^^^^^^^^^ +Currently, serialised flow state machines persisted in the node's database cannot be updated. All flows must finish +before the updated flow classes are added to the node's plugins folder. + +Flows that don't create sessions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Flows which are not an ``InitiatingFlow`` or ``InitiatedBy`` flow, or inlined subflows that are not called from an +``InitiatingFlow`` or ``InitiatedBy`` flow, can be updated without consideration of backwards-compatibility. Flows of +this type include utility flows for querying the vault and flows for reaching out to external systems. + +Contract and state versioning +----------------------------- +Contracts and states can be upgraded if and only if all of the state's participants agree to the proposed upgrade. The +following combinations of upgrades are possible: + +* A contract is upgraded while the state definition remains the same +* A state is upgraded while the contract stays the same +* The state and the contract are updated simultaneously + +The procedure for updating a state or a contract using a flag-day approach is quite simple: + +* Update and test the state or contract +* Stop all the nodes on the business network +* Produce a new CorDapp JAR file and distribute it to all the relevant parties +* Start all nodes on the network +* Run the contract upgrade authorisation flow for each state that requires updating on every node +* For each state, one node should run the contract upgrade initiation flow + +Update Process +~~~~~~~~~~~~~~ + +Writing the new state and contract definitions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Start by updating the contract and/or state definitions. There are no restrictions on how states are updated. However, +upgraded contracts must implement the ``UpgradedContract`` interface. This interface is defined as: + +.. sourcecode:: kotlin + + interface UpgradedContract : Contract { + val legacyContract: ContractClassName + fun upgrade(state: OldState): NewState + } + +The ``upgrade`` method describes how the old state type is upgraded to the new state type. When the state isn't being +upgraded, the same state type can be used for both the old and new state type parameters. + +Authorising the upgrade +^^^^^^^^^^^^^^^^^^^^^^^ +Once the new states and contracts are on the classpath for all the relevant nodes, the next step is for all nodes to +run the ``ContractUpgradeFlow.Authorise`` flow. This flow takes a ``StateAndRef`` of the state to update as well as a +reference to the new contract, which must implement the ``UpgradedContract`` interface. + +At any point, a node administrator may de-authorise a contract upgrade by running the +``ContractUpgradeFlow.Deauthorise`` flow. + +Performing the upgrade +^^^^^^^^^^^^^^^^^^^^^^ +Once all nodes have performed the authorisation process, a participant must be chosen to initiate the upgrade via the +``ContractUpgradeFlow.Initiate`` flow for each state object. This flow has the following signature: + +.. sourcecode:: kotlin + + class Initiate( + originalState: StateAndRef, + newContractClass: Class> + ) : AbstractStateReplacementFlow.Instigator>>(originalState, newContractClass) + +This flow sub-classes ``AbstractStateReplacementFlow``, which can be used to upgrade state objects that do not need a +contract upgrade. + +One the flow ends successfully, all the participants of the old state object should have the upgraded state object +which references the new contract code. + +Points to note +~~~~~~~~~~~~~~ + +Capabilities of the contract upgrade flows +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* Despite its name, the ``ContractUpgradeFlow`` also handles the update of state object definitions +* The state can completely change as part of an upgrade! For example, it is possible to transmute a ``Cat`` state into + a ``Dog`` state, provided that all participants in the ``Cat`` state agree to the change +* Equally, the state doesn't have to change at all +* If a node has not yet run the contract upgrade authorisation flow, they will not be able to upgrade the contract + and/or state objects +* Upgrade authorisations can subsequently be deauthorised +* Upgrades do not have to happen immediately. For a period, the two parties can use the old states and contracts + side-by-side +* State schema changes are handled separately + +Writing new states and contracts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* If a property is removed from a state, any references to it must be removed from the contract code. Otherwise, you + will not be able to compile your contract code. It is generally not advisable to remove properties from states. Mark + them as deprecated instead +* When adding properties to a state, consider how the new properties will affect transaction validation involving this + state. If the contract is not updated to add constraints over the new properties, they will be able to take on any + value +* Updated state objects can use the old contract code as long as there is no requirement to update it + +Dealing with old contract code JAR files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* Currently, all parties **must** keep the old state and contract definitions on their node's classpath as they will + always be required to verify transactions involving previous versions of the state using previous versions of the + contract + + * This will change when the contract code as an attachment feature has been fully implemented. + +Permissioning +^^^^^^^^^^^^^ +* Only node administrators are able to run the contract upgrade authorisation and deauthorisation flows + +Logistics +^^^^^^^^^ +* All nodes need to run the contract upgrade authorisation flow +* Only one node should run the contract upgrade initiation flow. If multiple nodes run it for the same ``StateRef``, a + double-spend will occur for all but the first completed upgrade +* The supplied upgrade flows upgrade one state object at a time + +Serialisation +------------- + +Currently, the serialisation format for everything except flow checkpoints (which uses a Kryo-based format) is based +upon AMQP 1.0, a self-describing and controllable serialisation format. AMQP is desirable because it allows us to have +a schema describing what has been serialized alongside the data itself. This assists with versioning and deserialising +long-ago archived data, among other things. + +Writing classes +~~~~~~~~~~~~~~~ +Although not strictly related to versioning, AMQP serialisation dictates that we must write our classes in a particular way: + +* Your class must have a constructor that takes all the properties that you wish to record in the serialized form. This + is required in order for the serialization framework to reconstruct an instance of your class +* If more than one constructor is provided, the serialization framework needs to know which one to use. The + ``@ConstructorForDeserialization`` annotation can be used to indicate the chosen constructor. For a Kotlin class + without the ``@ConstructorForDeserialization`` annotation, the primary constructor is selected +* The class must be compiled with parameter names in the .class file. This is the default in Kotlin but must be turned + on in Java (using the ``-parameters`` command line option to ``javac``) +* Your class must provide a Java Bean getter for each of the properties in the constructor, with a matching name. For + example, if a class has the constructor parameter ``foo``, there must be a getter called ``getFoo()``. If ``foo`` is + a boolean, the getter may optionally be called ``isFoo()``. This is why the class must be compiled with parameter + names turned on +* The class must be annotated with ``@CordaSerializable`` +* The declared types of constructor arguments/getters must be supported, and where generics are used the generic + parameter must be a supported type, an open wildcard (*), or a bounded wildcard which is currently widened to an open + 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 + +Writing enums +~~~~~~~~~~~~~ +Elements cannot be added to enums in a new version of the code. Hence, enums are only a good fit for genuinely static +data that will never change (e.g. days of the week). A ``Buy`` or ``Sell`` flag is another. However, something like +``Trade Type`` or ``Currency Code`` will likely change. For those, it is preferable to choose another representation, +such as a string. + +State schemas +------------- +By default, all state objects are serialised to the database as a string of bytes and referenced by their ``StateRef``. +However, it is also possible to define custom schemas for serialising particular properties or combinations of +properties, so that they can be queried from a source other than the Corda Vault. This is done by implementing the +``QueryableState`` interface and creating a custom object relational mapper for the state. See :doc:`api-persistence` +for details. + +For backwards compatible changes such as adding columns, the procedure for upgrading a state schema is to extend the +existing object relational mapper. For example, we can update: + +.. sourcecode:: kotlin + + object ObligationSchemaV1 : MappedSchema(Obligation::class.java, 1, listOf(ObligationEntity::class.java)) { + @Entity @Table(name = "obligations") + class ObligationEntity(obligation: Obligation) : PersistentState() { + @Column var currency: String = obligation.amount.token.toString() + @Column var amount: Long = obligation.amount.quantity + @Column @Lob var lender: ByteArray = obligation.lender.owningKey.encoded + @Column @Lob var borrower: ByteArray = obligation.borrower.owningKey.encoded + @Column var linear_id: String = obligation.linearId.id.toString() + } + } + +To: + +.. sourcecode:: kotlin + + object ObligationSchemaV1 : MappedSchema(Obligation::class.java, 1, listOf(ObligationEntity::class.java)) { + @Entity @Table(name = "obligations") + class ObligationEntity(obligation: Obligation) : PersistentState() { + @Column var currency: String = obligation.amount.token.toString() + @Column var amount: Long = obligation.amount.quantity + @Column @Lob var lender: ByteArray = obligation.lender.owningKey.encoded + @Column @Lob var borrower: ByteArray = obligation.borrower.owningKey.encoded + @Column var linear_id: String = obligation.linearId.id.toString() + @Column var defaulted: Bool = obligation.amount.inDefault // NEW COLUNM! + } + } + +Thus adding a new column with a default value. + +To make a non-backwards compatible change, the ``ContractUpgradeFlow`` or ``AbstractStateReplacementFlow`` must be +used, as changes to the state are required. To make a backwards-incompatible change such as deleting a column (e.g. +because a property was removed from a state object), the procedure is to define another object relational mapper, then +add it to the ``supportedSchemas`` property of your ``QueryableState``, like so: + +.. sourcecode:: kotlin + + override fun supportedSchemas(): Iterable = listOf(ExampleSchemaV1, ExampleSchemaV2) + +Then, in ``generateMappedObject``, add support for the new schema: + +.. sourcecode:: kotlin + + override fun generateMappedObject(schema: MappedSchema): PersistentState { + return when (schema) { + is DummyLinearStateSchemaV1 -> // Omitted. + is DummyLinearStateSchemaV2 -> // Omitted. + else -> throw IllegalArgumentException("Unrecognised schema $schema") + } + } + +With this approach, whenever the state object is stored in the vault, a representation of it will be stored in two +separate database tables where possible - one for each supported schema. \ No newline at end of file diff --git a/docs/source/versioning.rst b/docs/source/versioning.rst index 9f5967e1ef..b6c47d0217 100644 --- a/docs/source/versioning.rst +++ b/docs/source/versioning.rst @@ -8,9 +8,6 @@ friendly for a developer working on the platform. It first has to be parsed and which to determine API differences. The release version is still useful and every MQ message the node sends attaches it to the ``release-version`` header property for debugging purposes. -Platform Version ----------------- - It is much easier to use a single incrementing integer value to represent the API version of the Corda platform, which is called the Platform Version. It is similar to Android's `API Level `_. It starts at 1 and will increment by exactly 1 for each release which changes any of the publicly exposed APIs in the @@ -27,46 +24,3 @@ for the network. .. note:: A future release may introduce the concept of a target platform version, which would be similar to Android's ``targetSdkVersion``, and would provide a means of maintaining behavioural compatibility for the cases where the platform's behaviour has changed. - -Flow versioning ---------------- - -In addition to the evolution of the platform, flows that run on top of the platform can also evolve. It may be that the -flow protocol between an initiating flow and it's intiated flow changes from one CorDapp release to the next in such as -way to be backwards incompatible with existing flows. For example, if a sequence of sends and receives needs to change -or if the semantics of a particular receive changes. - -The ``InitiatingFlow`` annotation (see :doc:`flow-state-machine` for more information on the flow annotations) has a ``version`` -property, which if not specified defaults to 1. This flow version is included in the flow session handshake and exposed -to both parties in the communication via ``FlowLogic.getFlowContext``. This takes in a ``Party`` and will return a -``FlowContext`` object which describes the flow running on the other side. In particular it has the ``flowVersion`` property -which can be used to programmatically evolve flows across versions. - -.. container:: codeset - - .. sourcecode:: kotlin - - @Suspendable - override fun call() { - val flowVersionOfOtherParty = getFlowContext(otherParty).flowVersion - val receivedString = if (flowVersionOfOtherParty == 1) { - receive(otherParty).unwrap { it.toString() } - } else { - receive(otherParty).unwrap { it } - } - } - -The above shows an example evolution of a flow which in the first version was expecting to receive an Int, but then -in subsequent versions was relaxed to receive a String. This flow is still able to communicate with parties which are -running the older flow (or rather older CorDapps containing the older flow). - -.. warning:: It's important that ``InitiatingFlow.version`` be incremented each time the flow protocol changes in an - incompatible way. - -``FlowContext`` also has ``appName`` which is the name of the CorDapp hosting the flow. This can be used to determine -implementation details of the CorDapp. See :doc:`cordapp-build-systems` for more information on the CorDapp filename. - -.. note:: Currently changing any of the properties of a ``CordaSerializable`` type is also backwards incompatible and - requires incrementing of ``InitiatingFlow.version``. This will be relaxed somewhat once the AMQP wire serialisation - format is implemented as it will automatically handle a lot of the data type migration cases. -