2017-06-05 12:37:23 +00:00
.. highlight :: kotlin
.. raw :: html
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script>
API: Contracts
==============
.. note :: Before reading this page, you should be familiar with the key concepts of :doc: `key-concepts-contracts` .
All Corda contracts are JVM classes that implement `` net.corda.core.contracts.Contract `` .
The `` Contract `` interface is defined as follows:
.. container :: codeset
.. literalinclude :: ../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt
:language: kotlin
:start-after: DOCSTART 5
:end-before: DOCEND 5
Where:
2017-07-19 12:06:43 +00:00
* `` verify(tx: LedgerTransaction) `` determines whether transactions involving states which reference this
2017-06-05 12:37:23 +00:00
contract type are valid
* `` legalContractReference `` is the hash of the legal prose contract that `` verify `` seeks to express in code
verify()
--------
2017-07-19 12:06:43 +00:00
`` verify() `` is a method that doesn't return anything and takes a `` LedgerTransaction `` as a parameter. It
2017-06-05 12:37:23 +00:00
either throws an exception if the transaction is considered invalid, or returns normally if the transaction is
considered valid.
`` verify() `` is executed in a sandbox. It does not have access to the enclosing scope, and is not able to access
2017-07-19 12:06:43 +00:00
the network or perform any other I/O. It only has access to the properties defined on `` LedgerTransaction `` when
2017-06-05 12:37:23 +00:00
establishing whether a transaction is valid.
The two simplest `` verify `` functions are the one that accepts all transactions, and the one that rejects all
transactions.
Here is the `` verify `` that accepts all transactions:
.. container :: codeset
.. sourcecode :: kotlin
2017-07-19 12:06:43 +00:00
override fun verify(tx: LedgerTransaction) {
2017-06-05 12:37:23 +00:00
// Always accepts!
}
.. sourcecode :: java
@Override
2017-07-19 12:06:43 +00:00
public void verify(LedgerTransaction tx) {
2017-06-05 12:37:23 +00:00
// Always accepts!
}
And here is the `` verify `` that rejects all transactions:
.. container :: codeset
.. sourcecode :: kotlin
2017-07-19 12:06:43 +00:00
override fun verify(tx: LedgerTransaction) {
2017-06-05 12:37:23 +00:00
throw IllegalArgumentException("Always rejects!")
}
.. sourcecode :: java
@Override
2017-07-19 12:06:43 +00:00
public void verify(LedgerTransaction tx) {
2017-06-05 12:37:23 +00:00
throw new IllegalArgumentException("Always rejects!");
}
2017-07-19 12:06:43 +00:00
LedgerTransaction
2017-06-05 12:37:23 +00:00
^^^^^^^^^^^^^^^^^^^^^^
2017-07-19 12:06:43 +00:00
The `` LedgerTransaction `` object passed into `` verify() `` represents the full set of information available to
2017-06-05 12:37:23 +00:00
`` verify() `` when deciding whether to accept or reject the transaction. It has the following properties:
.. container :: codeset
2017-07-19 12:06:43 +00:00
.. literalinclude :: ../../core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt
2017-06-05 12:37:23 +00:00
:language: kotlin
:start-after: DOCSTART 1
:end-before: DOCEND 1
Where:
2017-07-19 12:06:43 +00:00
* `` inputs `` is a list of the transaction's inputs'
* `` outputs `` is a list of the transaction's outputs'
* `` attachments `` is a list of the transaction's attachments'
* `` commands `` is a list of the transaction's commands, and their associated signatures'
* `` id `` is the transaction's merkle root hash'
* `` notary `` is the transaction's notary. If there are inputs these must have the same notary on their source transactions.
* `` timeWindow `` is the transaction's timestamp and defines the acceptable delay for notarisation.
* `` type `` is the class of Transaction. Normal ledger data transactions are `` TransactionType.General `` , but migration of states to a new notary uses `` TransactionType.NotaryChange `` .
2017-06-05 12:37:23 +00:00
requireThat()
^^^^^^^^^^^^^
Instead of throwing exceptions manually to reject a transaction, we can use the `` requireThat `` DSL:
.. container :: codeset
.. sourcecode :: kotlin
requireThat {
"No inputs should be consumed when issuing an X." using (tx.inputs.isEmpty())
"Only one output state should be created." using (tx.outputs.size == 1)
val out = tx.outputs.single() as XState
"The sender and the recipient cannot be the same entity." using (out.sender != out.recipient)
"All of the participants must be signers." using (command.signers.containsAll(out.participants))
"The X's value must be non-negative." using (out.x.value > 0)
}
.. sourcecode :: java
requireThat(require -> {
require.using("No inputs should be consumed when issuing an X.", tx.getInputs().isEmpty());
require.using("Only one output state should be created.", tx.getOutputs().size() == 1);
final XState out = (XState) tx.getOutputs().get(0);
require.using("The sender and the recipient cannot be the same entity.", out.getSender() != out.getRecipient());
require.using("All of the participants must be signers.", command.getSigners().containsAll(out.getParticipants()));
require.using("The X's value must be non-negative.", out.getX().getValue() > 0);
return null;
});
For each <`` String `` , `` Boolean `` > pair within `` requireThat `` , if the boolean condition is false, an
`` IllegalArgumentException `` is thrown with the corresponding string as the exception message. In turn, this
exception will cause the transaction to be rejected.
Commands
^^^^^^^^
2017-07-19 12:06:43 +00:00
`` LedgerTransaction `` contains the commands as a list of `` AuthenticatedObject `` instances.
2017-06-05 12:37:23 +00:00
`` AuthenticatedObject `` pairs an object with a list of signers. In this case, `` AuthenticatedObject `` pairs a command
2017-06-07 15:14:01 +00:00
with a list of the entities that are required to sign a transaction where this command is present:
2017-06-05 12:37:23 +00:00
.. container :: codeset
.. literalinclude :: ../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt
:language: kotlin
:start-after: DOCSTART 6
:end-before: DOCEND 6
Where:
* `` signers `` is the list of each signer's `` PublicKey ``
* `` signingParties `` is the list of the signer's identities, if known
* `` value `` is the object being signed (a command, in this case)
Extracting commands
~~~~~~~~~~~~~~~~~~~
You can use the `` requireSingleCommand() `` helper method to extract commands.
`` <C : CommandData> Collection<AuthenticatedObject<CommandData>>.requireSingleCommand(klass: Class<C>) `` asserts that
the transaction contains exactly one command of type `` T `` , and returns it. If there is not exactly one command of this
type in the transaction, an exception is thrown, rejecting the transaction.
For `` requireSingleCommand `` to work, all the commands that we wish to match against must be grouped using the same
marker interface.
Here is an example of using `` requireSingleCommand `` to extract a transaction's command and using it to fork the
execution of `` verify() `` :
.. container :: codeset
.. sourcecode :: kotlin
class XContract : Contract {
interface Commands : CommandData {
class Issue : TypeOnlyCommandData(), Commands
class Transfer : TypeOnlyCommandData(), Commands
}
2017-07-19 12:06:43 +00:00
override fun verify(tx: LedgerTransaction) {
2017-06-05 12:37:23 +00:00
val command = tx.commands.requireSingleCommand<Commands>()
when (command.value) {
is Commands.Issue -> {
// Issuance verification logic.
}
is Commands.Transfer -> {
// Transfer verification logic.
}
}
}
override val legalContractReference: SecureHash = SecureHash.sha256("X contract hash")
}
.. sourcecode :: java
public class XContract implements Contract {
public interface Commands extends CommandData {
class Issue extends TypeOnlyCommandData implements Commands {}
class Transfer extends TypeOnlyCommandData implements Commands {}
}
@Override
2017-07-19 12:06:43 +00:00
public void verify(LedgerTransaction tx) {
2017-06-05 12:37:23 +00:00
final AuthenticatedObject<Commands> command = requireSingleCommand(tx.getCommands(), Commands.class);
if (command.getValue() instanceof Commands.Issue) {
// Issuance verification logic.
} else if (command.getValue() instanceof Commands.Transfer) {
// Transfer verification logic.
}
}
private final SecureHash legalContractReference = SecureHash.sha256("X contract hash");
@Override public final SecureHash getLegalContractReference() { return legalContractReference; }
}
Grouping states
---------------
Suppose we have the following transaction, where 15 USD is being exchanged for 10 GBP:
.. image :: resources/ungrouped-tx.png
:scale: 20
:align: center
We can imagine that we would like to verify the USD states and the GBP states separately:
.. image :: resources/grouped-tx.png
:scale: 20
:align: center
2017-07-19 12:06:43 +00:00
`` LedgerTransaction `` provides a `` groupStates `` method to allow you to group states in this way:
2017-06-05 12:37:23 +00:00
.. container :: codeset
.. literalinclude :: ../../core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt
:language: kotlin
:start-after: DOCSTART 2
:end-before: DOCEND 2
Where `` InOutGroup `` is defined as:
.. container :: codeset
.. literalinclude :: ../../core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt
:language: kotlin
:start-after: DOCSTART 3
:end-before: DOCEND 3
For example, we could group the states in the transaction above by currency (i.e. by `` amount.token `` ):
.. container :: codeset
.. sourcecode :: kotlin
val groups: List<InOutGroup<Cash.State, Issued<Currency>>> = tx.groupStates(Cash.State::class.java) {
it -> it.amount.token
}
.. sourcecode :: java
final List<InOutGroup<Cash.State, Issued<Currency>>> groups = tx.groupStates(
Cash.State.class,
it -> it.getAmount().getToken()
);
This would produce the following InOutGroups:
.. image :: resources/in-out-groups.png
We can now verify these groups individually:
.. container :: codeset
.. sourcecode :: kotlin
for ((in_, out, key) in groups) {
when (key) {
is GBP -> {
// GBP verification logic.
}
is USD -> {
// USD verification logic.
}
}
}
.. sourcecode :: java
for (InOutGroup group : groups) {
if (group.getGroupingKey() == USD) {
// USD verification logic.
} else if (group.getGroupingKey() == GBP) {
// GBP verification logic.
}
}
Legal prose
-----------
Current, `` legalContractReference `` is simply the SHA-256 hash of a contract:
.. container :: codeset
.. literalinclude :: ../../finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt
:language: kotlin
:start-after: DOCSTART 2
:end-before: DOCEND 2
In the future, a contract's legal prose will be included as an attachment instead.