Skip to content

Commit 2090bcc

Browse files
Nicholas Clarkenc6
authored andcommitted
Add section on transaction validity.
1 parent 29c986e commit 2090bcc

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Transaction Validity
2+
3+
What does it mean for a transaction to be valid? The ledger specs define it
4+
quite simply: a transaction is valid if it may be applied to a valid ledger
5+
state to return another valid ledger state. We say that it is valid with regard
6+
to the initial ledger state.
7+
8+
More prosaically, for a transaction to be valid with regards to a ledger state
9+
entails the things we would expect: its inputs must exist, the spender must have
10+
the right to spend those inputs, the transaction must balance etc. In the
11+
ledger specifications, these are all written as _predicates_ - assertions that
12+
one thing equals another, for example. A failing predicate means that the
13+
transition is invalid and hence that the transaction is invalid with regard
14+
to that ledger state.
15+
16+
## Multi-phase Validity
17+
18+
Invalid transactions do not end up on the chain. Consequently, invalid
19+
transactions do not pay fees. A trivial attack on a block producing node would
20+
be to bombard it with invalid transactions. The node must verify that each
21+
transaction is invalid, but gains no benefit for performing this work.
22+
23+
In order that this not become an asymmetric resource attack, the work which
24+
must be done to validate a transaction needs be bounded. The introduction of
25+
Plutus in the Alonzo era, however, complicated this situation. Plutus scripts
26+
must necessarily be capable of performing a significant amount of work. Should
27+
this work result in the transaction being deemed invalid, that work would be
28+
uncompensated - an attacker could use relatively small amounts of their own
29+
resource (crafting a looping Plutus script is, after all, relatively easy) to
30+
force significantly larger resource expenditure from the network.
31+
32+
In order to combat this, Alonzo introduced the concept of 2-phase validity:
33+
34+
1. The first phase involves the regular checks of things such as transaction
35+
size, fee suitability, input validity etc. These checks are assumed to have
36+
bounded work. A failure in phase 1 indicates that the transaction will
37+
not be placed on chain.
38+
2. Phase 2 checks are only run if phase 1 succeeds. Phase 2 checks involve
39+
running Plutus scripts and validating that inputs locked by those scripts
40+
can be spent. A transaction failing a phase 2 check can still be put on
41+
chain. In this case, a special input called the 'collateral' is spent and
42+
donated to the fee pot. The collateral must be locked by a phase-1
43+
verifiable input - i.e. an input locked by a VKey or native script.
44+
45+
An important consideration is that phase-2 checks are _static_ (see below).
46+
Phase-2 checks are run always in the context only of the transaction and its
47+
resolved inputs (which, since we have a UTxO system, are determinstic if they
48+
exist). As such, a diligent transaction submitter should have no risk of their
49+
collateral being taken - they can validate that their script passes before
50+
submitting the transaction and then be assured that it will either pass when
51+
the transaction is included, or that the transaction will fail during phase 1
52+
(for example, if an input has been spent).[^1]
53+
54+
## Static vs Dynamic Checks
55+
56+
Since transaction validity is defined with regard to a ledger state, a change
57+
to the ledger state may result in previously valid transactions now becoming
58+
invalid. For example, the time may have moved past the transaction's validity
59+
window, or one of the inputs may have been spent.
60+
61+
Consequently, as the ledger state evolves due to new blocks being accepted,
62+
nodes need to revalidate transactions in their mempool against the new state.
63+
However, not everything needs to be revalidated. Cryptographic signatures, for
64+
example, are guaranteed to remain valid regardless of the ledger state.
65+
66+
Formally, we call a check _static_ if it can be evaluated with regard only to
67+
the contents of the transaction and its resolved inputs. Examples
68+
(non-exhaustive) of static checks include:
69+
70+
- Cryptographic signature checks
71+
- Native (multisig/timelock) scripts
72+
- Phase 2 checks (Plutus scripts)
73+
74+
_Dynamic checks_, on the other hand, require access to the UTxO or other aspects
75+
of the ledger state to compute. As such, they need to be re-evaluated each time
76+
the ledger state is updated. Obvious examples of dynamic checks include
77+
verifying that inputs exist, checking that the transaction still sits within its
78+
validity window, and validating block transaction size against the protocol
79+
parameters.
80+
81+
# Block Validity
82+
83+
> This section is currently a stub
84+
85+
# Relevance for the node developer
86+
87+
The above is mostly relevant for node developers in that it is useful to be able
88+
to run the ledger transitions with fine-grained control over which checks are
89+
computed.
90+
91+
There are four main scenarios which come into consideration:
92+
93+
1. Validating a transaction as it enters the mempool. In this case all checks
94+
must be computed.
95+
2. Re-validating a transaction after a new block has been adopted. In this case,
96+
we care only about re-running _dynamic_ checks.
97+
3. Validating a new block body downloaded from a peer. In this case all checks
98+
must be computed.
99+
4. Re-applying a block from our local storage in order to reconstruct the ledger
100+
state. Since local blocks are assumed to be trusted, we need run _no_ checks
101+
here and only apply the transition.
102+
103+
Node developers should bear these scenarios in mind when considering how to
104+
structure their node transition function.
105+
106+
[^1]: Note that there is a small addendum to this story. While theoretically
107+
anyone may validate their own Plutus scripts, many users do not run their own
108+
node and as such trust a third party to validate those scripts on their behalf.
109+
These users were concerned about accidentally losing collateral. Since
110+
collateral must be a single address, users in such a situation either had to
111+
assign precisely the 'minCollateral' to an address or put up another UTxO as
112+
collateral and risk losing more than the minimum. To assuage the fears of such
113+
folks, Babbage introduced a 'collateral return address' to which collateral in
114+
excess of the minimum required would be returned in the case of a failing
115+
script.

0 commit comments

Comments
 (0)