corda/docs/source/api-transactions.rst
2017-07-07 12:06:10 +01:00

21 KiB

API: Transactions

Note

Before reading this page, you should be familiar with the key concepts of 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 api-flows for more details.

Transaction workflow

There are four states the transaction can occupy:

  • TransactionBuilder, a builder for a transaction in construction
  • WireTransaction, an immutable transaction
  • SignedTransaction, an immutable transaction with 1+ associated signatures
  • LedgerTransaction, a 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 transaction is to instantiate a TransactionBuilder. We can create a builder for each transaction type 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 WireTransaction
  • 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 itself and its signatures.

Verifying the transaction

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 ResolveTransactionsFlow. See api-flows for more details.

When verifying a SignedTransaction, we don't verify the SignedTransaction per se, but rather the WireTransaction it contains. We extract this WireTransaction 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

However, this still isn't enough. The WireTransaction 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 WireTransaction 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 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 will generally also want to conduct some additional validation of the transaction, beyond what is provided for in the contract. Here's an example of how we might do this:

../../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 signatures

We also need to verify the signatures over the transaction to prevent tampering. We do this using SignedTransaction.verifySignatures:

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

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

Optionally, we can pass verifySignatures 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.