.. highlight:: kotlin .. raw:: html API: Transactions ================= .. note:: Before reading this page, you should be familiar with the key concepts of :doc:`key-concepts-transactions`. Transaction types ----------------- There are two types of transaction in Corda: * ``TransactionType.NotaryChange``, used to change the notary for a set of states * ``TransactionType.General``, for transactions other than notary-change transactions Notary-change transactions ^^^^^^^^^^^^^^^^^^^^^^^^^^ A single Corda network will usually have multiple notary services. To commit a transaction, we require a signature from the notary service associated with each input state. If we tried to commit a transaction where the input states were associated with different notary services, the transaction would require a signature from multiple notary services, creating a complicated multi-phase commit scenario. To prevent this, every input state in a transaction must be associated the same notary. However, we will often need to create a transaction involving input states associated with different notaries. Before we can create this transaction, we will need to change the notary service associated with each state by: * Deciding which notary service we want to notarise the transaction * For each set of inputs states that point to the same notary service that isn't the desired notary service, creating a ``TransactionType.NotaryChange`` transaction that: * Consumes the input states pointing to the old notary * Outputs the same states, but that now point to the new notary * Using the outputs of the notary-change transactions as inputs to a standard ``TransactionType.General`` transaction In practice, this process is handled automatically by a built-in flow called ``NotaryChangeFlow``. See :doc:`api-flows` for more details. Transaction workflow -------------------- There are four states the transaction can occupy: * ``TransactionBuilder``, a mutable transaction-in-construction * ``WireTransaction``, an immutable transaction * ``SignedTransaction``, a ``WireTransaction`` with 1+ associated signatures * ``LedgerTransaction``, a resolved ``WireTransaction`` that can be checked for contract validity Here are the possible transitions between transaction states: .. image:: resources/transaction-flow.png TransactionBuilder ------------------ Creating a builder ^^^^^^^^^^^^^^^^^^ The first step when building a transaction is to create a ``TransactionBuilder``: .. container:: codeset .. sourcecode:: kotlin // A general transaction builder. val generalTxBuilder = TransactionType.General.Builder() // A notary-change transaction builder. val notaryChangeTxBuilder = TransactionType.NotaryChange.Builder() .. sourcecode:: java // A general transaction builder. final TransactionBuilder generalTxBuilder = new TransactionType.General.Builder(); // A notary-change transaction builder. final TransactionBuilder notaryChangeTxBuilder = new TransactionType.NotaryChange.Builder(); Adding items ^^^^^^^^^^^^ The transaction builder is mutable. We add items to it using the ``TransactionBuilder.withItems`` method: .. container:: codeset .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 ``withItems`` takes a ``vararg`` of objects and adds them to the builder based on their type: * ``StateAndRef`` objects are added as input states * ``TransactionState`` and ``ContractState`` objects are added as output states * ``Command`` objects are added as commands Passing in objects of any other type will cause an ``IllegalArgumentException`` to be thrown. You can also add the following items to the transaction: * ``TimeWindow`` objects, using ``TransactionBuilder.setTime`` * ``SecureHash`` objects referencing the hash of an attachment stored on the node, using ``TransactionBuilder.addAttachment`` Input states ~~~~~~~~~~~~ Input states are added to a transaction as ``StateAndRef`` instances, rather than as ``ContractState`` instances. A ``StateAndRef`` combines a ``ContractState`` with a pointer to the transaction that created it. This series of pointers from the input states back to the transactions that created them is what allows a node to work backwards and verify the entirety of the transaction chain. It is defined as: .. container:: codeset .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt :language: kotlin :start-after: DOCSTART 7 :end-before: DOCEND 7 Where ``StateRef`` is defined as: .. container:: codeset .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt :language: kotlin :start-after: DOCSTART 8 :end-before: DOCEND 8 ``StateRef.index`` is the state's position in the outputs of the transaction that created it. In this way, a ``StateRef`` allows a notary service to uniquely identify the existing states that a transaction is marking as historic. Output states ~~~~~~~~~~~~~ Since a transaction's output states do not exist until the transaction is committed, they cannot be referenced as the outputs of previous transactions. Instead, we create the desired output states as ``ContractState`` instances, and add them to the transaction. Commands ~~~~~~~~ Commands are added to the transaction as ``Command`` instances. ``Command`` combines a ``CommandData`` instance representing the type of the command with a list of the command's required signers. It is defined as: .. container:: codeset .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt :language: kotlin :start-after: DOCSTART 9 :end-before: DOCEND 9 Signing the builder ^^^^^^^^^^^^^^^^^^^ Once the builder is ready, we finalize it by signing it and converting it into a ``SignedTransaction``: .. container:: codeset .. sourcecode:: kotlin // Finalizes the builder by signing it with our primary signing key. val signedTx1 = serviceHub.signInitialTransaction(unsignedTx) // Finalizes the builder by signing it with a different key. val signedTx2 = serviceHub.signInitialTransaction(unsignedTx, otherKey) // Finalizes the builder by signing it with a set of keys. val signedTx3 = serviceHub.signInitialTransaction(unsignedTx, otherKeys) .. sourcecode:: java // Finalizes the builder by signing it with our primary signing key. final SignedTransaction signedTx1 = getServiceHub().signInitialTransaction(unsignedTx); // Finalizes the builder by signing it with a different key. final SignedTransaction signedTx2 = getServiceHub().signInitialTransaction(unsignedTx, otherKey); // Finalizes the builder by signing it with a set of keys. final SignedTransaction signedTx3 = getServiceHub().signInitialTransaction(unsignedTx, otherKeys); SignedTransaction ----------------- A ``SignedTransaction`` is a combination of an immutable ``WireTransaction`` and a list of signatures over that transaction: .. container:: codeset .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 Verifying the signatures ^^^^^^^^^^^^^^^^^^^^^^^^ The signatures on a ``SignedTransaction`` have not necessarily been checked for validity. We check them using ``SignedTransaction.verifySignatures``: .. container:: codeset .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 ``verifySignatures`` takes a ``vararg`` of the public keys for which the signatures are allowed to be missing. If the transaction is missing any signatures without the corresponding public keys being passed in, a ``SignaturesMissingException`` is thrown. Verifying the transaction ^^^^^^^^^^^^^^^^^^^^^^^^^ Verifying a transaction is a multi-step process: * We check the transaction's signatures: .. container:: codeset .. sourcecode:: kotlin subFlow(ResolveTransactionsFlow(transactionToVerify, partyWithTheFullChain)) .. sourcecode:: java subFlow(new ResolveTransactionsFlow(transactionToVerify, partyWithTheFullChain)); * Before verifying the transaction, we need to retrieve from the proposer(s) of the transaction any parts of the transaction chain that our node doesn't currently have in its local storage: .. container:: codeset .. sourcecode:: kotlin subFlow(ResolveTransactionsFlow(transactionToVerify, partyWithTheFullChain)) .. sourcecode:: java subFlow(new ResolveTransactionsFlow(transactionToVerify, partyWithTheFullChain)); * To verify the transaction, we first need to resolve any state references and attachment hashes by converting the ``SignedTransaction`` into a ``LedgerTransaction``. We can then verify the fully-resolved transaction: .. container:: codeset .. sourcecode:: kotlin partSignedTx.tx.toLedgerTransaction(serviceHub).verify() .. sourcecode:: java partSignedTx.getTx().toLedgerTransaction(getServiceHub()).verify(); * We will generally also want to conduct some custom validation of the transaction, beyond what is provided for in the contract: .. container:: codeset .. sourcecode:: kotlin val ledgerTransaction = partSignedTx.tx.toLedgerTransaction(serviceHub) val inputStateAndRef = ledgerTransaction.inputs.single() val input = inputStateAndRef.state.data as MyState if (input.value > 1000000) { throw FlowException("Proposed input value too high!") } .. sourcecode:: java final LedgerTransaction ledgerTransaction = partSignedTx.getTx().toLedgerTransaction(getServiceHub()); final StateAndRef inputStateAndRef = ledgerTransaction.getInputs().get(0); final MyState input = (MyState) inputStateAndRef.getState().getData(); if (input.getValue() > 1000000) { throw new FlowException("Proposed input value too high!"); } Signing the transaction ^^^^^^^^^^^^^^^^^^^^^^^ We add an additional signature to an existing ``SignedTransaction`` using: .. container:: codeset .. sourcecode:: kotlin val fullySignedTx = serviceHub.addSignature(partSignedTx) .. sourcecode:: java SignedTransaction fullySignedTx = getServiceHub().addSignature(partSignedTx); We can also generate a signature over the transaction without adding it to the transaction directly by using: .. container:: codeset .. sourcecode:: kotlin val signature = serviceHub.createSignature(partSignedTx) .. sourcecode:: java DigitalSignature.WithKey signature = getServiceHub().createSignature(partSignedTx); Notarising and recording ^^^^^^^^^^^^^^^^^^^^^^^^ Notarising and recording a transaction is handled by a built-in flow called ``FinalityFlow``. See :doc:`api-flows` for more details.