Resources / cardano-sl / overall.md
You are browsing a mirror of a file hosted on GitHub. View original
Block processing can be described by presenting an algorithm to solve the following problem:
Given a sequence of blocks
B₀, B₁, B₂, …(where
B₀is the first genesis block) check whether these blocks are valid.
We describe block processing as a stateful algorithm:
- Initial state
S₀is derived from blockchain genesis data (see mainnet genesis JSON)
S₁, S₂, …are maintained as sequential application of blocks
B₀, B₁, B₂, …to state
- We maintain some state called GState which corresponds to the application of a sequence of blocks.
- State transition function. Given GState
Sand a block
Breturn either an error describing why
Bis invalid or new GState
verifyAndApplyGState :: GState -> Block -> Either BlockVerificationError GState -- ^ Note that function definition and types are different in code and are put here for reader's convenience
Note that in theory verification can be stateless (except genesis state
and be described without mentioning any additional state (apart from blocks
themselves). But in practice it would be very inefficient and inconvenient.
For sake of simplicity, we describe state transition function in two parts:
- Verification: given GState
Sand a block
B, check whether
Bis valid (and can be applied to
- Modification: given GState
Sand a block
B(successfully verified against
Block payload consists of:
TxPayload. It is checked by the transaction processing component and is described in Transaction processing.
UpdatePayload. It is checked by the update system component and is described in Update system consensus rules.
DlgPayload. It is checked by the delegation component and is described in TODO.
SscPayload. It is checked by the shared seed computation component and is described in TODO.
From the implementation viewpoint, we have a pattern where for all kinds of
payloads the corresponding components export essentially three functions which
deal with these payloads:
When we need to process a new sequence on blocks, we first
payloads component-wise using functions from corresponding systems (
Update). A side effect of verification is that we
Undos, which are pieces of data needed to undo the application of each
block. We bind together blocks and
Undos into pairs we call
Blunds, which we
apply. Each component is responsible for persisting its
Blund in the DB
and updating the GState. Occasionally, we
rollback some blocks too, which is
also performed component-wise.
One somewhat exceptional case is
Slog, which does not correspond to any kind
of payload and represents a collection of checks and things that can be undone
that do not fit in
ssc. For more information on
See also comments in
for extra details.
Unknown data handling
Many types in Cardano-SL are designed in an extensible way. They can be extended via softfork in future (e. g. new data can be attached or semantics can be changed). Examples are:
TxInhas two constructors
TxInUnknown. The former is just a reference to an unspent output, it’s the only
TxIntype we use currently.
TxInUnknowncontains arbitrary bytestring as payload. Later we may introduce another type of transaction inputs (e. g. to redeem fees). Then old software will parse these inputs as
TxInUnknown, but new software will parse them as new type of input (and process them appropriately).
Attributesis basically a map from 1-byte integers to arbitrary values. Some values can be parsed into known data types, other values are treated as unparsed fields.
Attributesare part of various data types, e. g.
Tx. In version 0
Txalways has empty attributes. But in version 1 we may add more data to
Tx(put it into
Attributes). In this case new software will parse this data and old software will treat it as unparsed fields.
- Unknown address type. Each valid address in Cardano-SL has a type. It can be one of fixed address types or unknown address type which can’t be interpreted by current version of software.
There are few more examples, but these three should demonstrate the general idea. If we encounter unknown data (unparsed fields, unknown tx input type, unknown address type, etc.) in a block, there are two possible behaviours: 1. Consider such block invalid. 2. Do as many checks as we can and ignore checks which can’t be done because data is unknown. This behaviour depends on which type of data we are processing.
The behaviour depends on two protocol versions: version used by this software and last adopted version. We verify that data in blocks is known if protocol version used by this software is greater than or equal to the adopted version. That’s because in this case:
- Authors of this software are aware of the adopted version.
- Each issued block must be formed with respect to adopted version.
Comparison of software protocol version and last adopted one is quite tricky here. Table below demonstrates it. The last column stands for whether we check data in block is known.
(major, minor) of our version is greater than of adopted one, then check is
certainly done. If it’s equal, then check is done only if
alt component is the
same as adopted one. In other cases (i. e. when our
(major, minor) is less
than from adopted version) check is not done.
Note: when we say that attributes must be known, it means that unparsed fields must be empty.
Let’s assume that version comparison is done as described above. Let’s define
verifyAllIsKnown and assign it value:
Trueif check is to be done, i.e. only known data should be considered valid (unknown data is prohibitied)