corda/docs/source/api-transactions.rst
2017-08-25 17:15:39 +01:00

20 KiB

API: Transactions

Note

Before reading this page, you should be familiar with the key concepts of key-concepts-transactions.

Transaction workflow

At any time, a transaction can occupy one of three states:

  • TransactionBuilder, a builder for an in-construction transaction
  • SignedTransaction, an immutable transaction with 1+ associated signatures
  • LedgerTransaction, an immutable transaction that can be checked for validity

Here are the possible transitions between transaction states:

image

TransactionBuilder

Creating a builder

The first step when creating a new transaction is to instantiate a TransactionBuilder. We create a builder for a transaction as follows:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Transaction components

Once we have a TransactionBuilder, we need to gather together the various transaction components the transaction will include.

Input states

Input states are added to a transaction as StateAndRef instances. A StateAndRef combines:

  • A ContractState representing the input state itself
  • A StateRef pointing to the input among the outputs of the transaction that created it

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

A StateRef uniquely identifies an input state, allowing the notary to mark it as historic. It is made up of:

  • The hash of the transaction that generated the state
  • The state's index in the outputs of that transaction

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

The StateRef create a chain of pointers from the input states back to the transactions that created them. This allows a node to work backwards and verify the entirety of the transaction chain.

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:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

In many cases (e.g. when we have a transaction that updates an existing state), we may want to create an output by copying from the input state:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Commands

Commands are added to the transaction as Command instances. Command combines:

  • A CommandData instance representing the type of the command
  • A list of the command's required signers

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Attachments

Attachments are identified by their hash. The attachment with the corresponding hash must have been uploaded ahead of time via the node's RPC interface:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Time-windows

Time windows represent the period of time during which the transaction must be notarised. They can have a start and an end time, or be open at either end:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

We can also define a time window as an Instant +/- a time tolerance (e.g. 30 seconds):

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Or as a start-time plus a duration:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Adding items

The transaction builder is mutable. We add items to it using the TransactionBuilder.withItems method:

../../core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt

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.

Here's an example usage of TransactionBuilder.withItems:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

You can also pass in objects one-by-one. This is the only way to add attachments:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

To set the transaction builder's time-window, we can either set a time-window directly:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Or define the time-window as a time plus a duration (e.g. 45 seconds):

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Signing the builder

Once the builder is ready, we finalize it by signing it and converting it into a SignedTransaction:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

This will sign the transaction with your legal identity key. You can also choose to use another one of your public keys:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Either way, the outcome of this process is to create a SignedTransaction, which can no longer be modified.

SignedTransaction

A SignedTransaction is a combination of:

  • An immutable transaction
  • A list of signatures over that transaction

../../core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt

Before adding our signature to the transaction, we'll want to verify both the transaction's contents and the transaction's signatures.

Verifying the transaction's contents

To verify a transaction, we need to retrieve any states in the transaction chain that our node doesn't currently have in its local storage from the proposer(s) of the transaction. This process is handled by a built-in flow called ReceiveTransactionFlow. See api-flows for more details.

We can now verify the transaction to ensure that it satisfies the contracts of all the transaction's input and output states:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

We can also conduct additional validation of the transaction, beyond what is performed by its contracts. However, the SignedTransaction holds its inputs as StateRef instances, and its attachments as hashes. These do not provide enough information to properly validate the transaction's contents. To resolve these into actual ContractState and Attachment instances, we need to use the ServiceHub to convert the SignedTransaction into a LedgerTransaction:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

We can now perform additional verification. Here's a simple example:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Verifying the transaction's signatures

We also need to verify that the transaction has all the required signatures, and that these signatures are valid, to prevent tampering. We do this using SignedTransaction.verifyRequiredSignatures:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Alternatively, we can use SignedTransaction.verifySignaturesExcept, which takes a vararg of the public keys for which the signatures are allowed to be missing:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

If the transaction is missing any signatures without the corresponding public keys being passed in, a SignaturesMissingException is thrown.

We can also choose to simply verify the signatures that are present:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

However, BE VERY CAREFUL - this function provides no guarantees that the signatures are correct, or that none are missing.

Signing the transaction

Once we are satisfied with the contents and existing signatures over the transaction, we can add our signature to the SignedTransaction using:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

As with the TransactionBuilder, we can also choose to sign using another one of our public keys:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

We can also generate a signature over the transaction without adding it to the transaction directly by using:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Or using another one of our public keys, as follows:

../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt

../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java

Notarising and recording

Notarising and recording a transaction is handled by a built-in flow called FinalityFlow. See api-flows for more details.

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 with 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
  • Creating a special NotaryChangeWireTransaction that consumes the input states pointing to the old notary and produces outputs which are identical but point to the new notary service
  • Using the outputs of the notary-change transactions as inputs to a standard transaction

In practice, this process is handled automatically by a built-in flow called NotaryChangeFlow. See api-flows for more details.