6.3 KiB
API: States
Note
Before reading this page, you should be familiar with the key
concepts of key-concepts-states
.
ContractState
In Corda, states are classes that implement
ContractState
. The ContractState
interface is
defined as follows:
../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt
Where:
contract
is theContract
class defining the constraints on transactions involving states of this typeparticipants
is aList
of theAbstractParty
who are considered to have a stake in the state. For example, all theparticipants
will:- Need to sign a notary-change transaction for this state
- Receive any committed transactions involving this state as part of
FinalityFlow
The vault
Each node has a vault, where it stores the states that are "relevant" to the node's owner. Whenever the node sees a new transaction, it performs a relevancy check to decide whether to add each of the transaction's output states to its vault. The default vault implementation decides whether a state is relevant as follows:
- The vault will store any state for which it is one of the
participants
- This behavior is overridden for states that implement
LinearState
orOwnableState
(see below)
If a state is not considered relevant, the node will still store the transaction in its local storage, but it will not track the transaction's states in its vault.
ContractState sub-interfaces
There are two common optional sub-interfaces of
ContractState
:
LinearState
, which helps represent objects that have a constant identity over timeOwnableState
, which helps represent fungible assets
For example, a cash is an OwnableState
- you don't have
a specific piece of cash you are tracking over time, but rather a total
amount of cash that you can combine and divide at will. A contract, on
the other hand, cannot be merged with other contracts of the same type -
it has a unique separate identity over time.
We can picture the hierarchy as follows:
LinearState
LinearState
models facts that have a constant identity
over time. Remember that in Corda, states are immutable and can't be
updated directly. Instead, we represent an evolving fact as a sequence
of states where every state is a LinearState
that shares
the same linearId
. Each sequence of linear states
represents the lifecycle of a given fact up to the current point in
time. It represents the historic audit trail of how the fact evolved
over time to its current "state".
The LinearState
interface is defined as follows:
../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt
Where:
linearId
is aUniqueIdentifier
that:- Allows the successive versions of the fact to be linked over time
- Provides an
externalId
for referencing the state in external systems
isRelevant(ourKeys: Set<PublicKey>)
overrides the default vault implementation's relevancy check. You would generally override it to check whetherourKeys
is relevant to the state at hand in some way.
The vault tracks the head (i.e. the most recent version) of each
LinearState
chain (i.e. each sequence of states all sharing
a linearId
). To create a transaction updating a
LinearState
, we retrieve the state from the vault using its
linearId
.
UniqueIdentifier
UniqueIdentifier
is a combination of a (Java)
UUID
representing a globally unique 128 bit random number,
and an arbitrary string which can be paired with it. For instance the
string may represent an existing "weak" (not guaranteed unique)
identifier for convenience purposes.
OwnableState
OwnableState
models fungible assets. Fungible assets are
assets for which it's the total amount held that is important, rather
than the actual units held. US dollars are an example of a fungible
asset - we do not track the individual dollar bills held, but rather the
total amount of dollars.
The OwnableState
interface is defined as follows:
../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt
Where:
owner
is thePublicKey
of the asset's ownerOwnableState
also override the default behavior of the vault's relevancy check. The default vault implementation will track anyOwnableState
of which it is the owner.
withNewOwner(newOwner: PublicKey)
creates an identical copy of the state, only with a new owner
Other interfaces
ContractState
has several more sub-interfaces that can
optionally be implemented:
QueryableState
, which allows the state to be queried in the node's database using SQL (seepersistence
)SchedulableState
, which allows us to schedule future actions for the state (e.g. a coupon on a bond) (seeevent-scheduling
)
User-defined fields
Beyond implementing LinearState
or
OwnableState
, the definition of the state is up to the
CorDapp developer. You can define any additional class fields and
methods you see fit.
For example, here is a relatively complex state definition, for a state representing cash:
../../finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt
TransactionState
When a ContractState
is added to a
TransactionBuilder
, it is wrapped in a
TransactionState
:
../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt
Where:
data
is the state to be stored on-ledgernotary
is the notary service for this stateencumbrance
points to another state that must also appear as an input to any transaction consuming this state