Ledger Rule - All inputs must be Valid #
See MidgardSpec -- Section 5.1.1.
This section is a proof-of-concept trying to verify the logic behind fraudproofs and their descomposition.
The Valid Inputs Property #
The property we are checking in this file is:
ValidInput
= ∀ t ∈ Ledger, ∀ i ∈ spend_inputs(t)
: (∃ t_1 ∈ Ledger, t ≠ t_1 ∧ i ∈ outputs(t_1))
∧ (∄ t_2 ∈ Ledger, t ≠ t_2 ∧ i ∈ spend_inputs(t_2))
Claim of the spec:
ValidInput ↔
( NOInput
∧ InputNoIdx
∧ WithdrawnInput
∧ DoubleSpend
∧ DoubleWithdraw
)
To prove the above claim, we need to have a definition of the Ledger, i.e. a Ledger Spec. However, once we have the spec implemented, we can prove the above statement in isolation.
What we can try is to modify the ValidInput statement so we only use local
data: e.g. each transaction consumes utxos and produce new ones. So we do not
need to look at the whole Ledger.
Let's propose the following valid input statement:
Let's txs be the current list of transactions forming a block and
utxo_prev the current set of utxos alive.
ValidInput
= ∀ t ∈ txs, ∀ i ∈ spend_inputs(t)
: i ∈ utxo_prev
∧ (∄ t_2 ∈ txs, t ≠ t_2 ∧ i ∈ spend_inputs(t_2))
In words, inputs exist before spending them and only one transaction gets to spend them.
Simple Translation into Lean #
This is how we would write it in lean. In the spec there is no clear definition of what they consider a /Ledger/, I assume it is just a list of blocks.
In the spec, it is not clear how the different components are composed to /verify/ the All Valid Property.
Equations
- One or more equations did not get rendered due to their size.
Instances For
All valid Property from the spec. There are no mention of withdrawals here, maybe we should add something so it matches the other properties.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Methodology #
In this first PoC, I assume I have access to all the required data. Lateron, we will replace:
- List membership by MPT membership.
- List non-membership by MPT (Sorted Lists or Tries) non-membership.
Data retieval may be a bit different. We get some hashes from the State Queue, or recieve external data and verify it correct using hashes.
When it comes to the logic of /Optimistic properties/, we follow the same ideas stated in the Operators Directory module. We define a way to observe the property, to create a witness, and then, a way to /correct/ the state. Technically, we are not in a bad (/bad/ in the old ways) state, we allow such states to be reached, but we penalize the agents generating it.
NoInput Violation #
There is a non-membership proof that may involve several others.
Definition
Spec:
∃ t ∈ txs, ∃ i ∈ spend_inputs(t)
: (i ∉ utxos_prev)
∧ (∄ t_1 ∈ txs, t ≠ t_1 → tx_hash(t_1) = tx_hash(i))
Replaced by
∃ t ∈ txs, ∃ i ∈ spend_inputs(t)
: (i ∉ utxos_prev)
∧ (hash(i) ∉ txs)
Equations
Instances For
The following property is noncomputable because flat_tx_hash is not
computable.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Computation #
Computation Finds a Witness #
Computation finding one violation. Hashing is axiomatized, so we cannot run it.
TODO: Missing witnesses. We need to reconstruct the path so we can build the
fraudproof. See findWitness?
Computation Returns None #
If Computation returns none, it means we cannot build valid witnesses. If we cannot build a valid witness, it should mean the transaction is valid.
Therefore, all inputs are correct?
Since i ∈ t.inputs, hash t cannot be the same as hash i.
Computation Thread #
- i : OutputRef
- txs : List FlattenTransaction
- txs_sorted : List.Sorted (fun (x1 x2 : Hash_64) => x1 < x2) (List.map TransactionHash.flat_tx_hash self.txs)
- utxo_prev_sorted : List.Sorted (fun (x1 x2 : TxId) => x1 < x2) self.utxo_prev
Instances For
- Mem : ListSimulation.Path → Input
- NonMem : ListSimulation.NonMemProof TxId → Input
- Skip : Input
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
- block : BlockBody
- txs_sorted : List.Sorted (fun (x1 x2 : Hash_64) => x1 < x2) (List.map (fun (x : TxOrderId × MidgardTx) => TransactionHash.flat_tx_hash x.2) self.block.transactions)
- utxo_sorted : List.Sorted (fun (x1 x2 : Hash_64) => x1 < x2) (List.map (fun (x : OutputRef × Output) => TransactionHash.output_hash x.2) self.block.utxos)
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- ⋯ = ⋯
Instances For
Equations
- ⋯ = ⋯
Instances For
No Input IDX Violation #
Definition
∃ t ∈ txs, ∃ i ∈ spend_inputs(t), ∃ t_1 ∈ txs
: tx_hash(t_1) = tx_hash(i)
∧ i ∉ outputs(t_1)
Equations
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Computation #
Equations
- One or more equations did not get rendered due to their size.
- AllInputsValid.InputNOIDXViolation.Computation [] acc = none
Instances For
Computation Thread #
- t : Transaction
- i : Transaction
- t1 : Transaction
- spend_inputs : List Transaction
- txs : List Transaction
- output_t1 : List Transaction
- i_idx : ℕ
Instances For
- Mem : ListSimulation.Path → Input
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
WithdrawnInput Violation #
Equations
- AllInputsValid.WithdrawnInputViolation.WithdrawnInputProp txs withdrawals = ∃ t ∈ txs, ∃ i ∈ t.inputs, ∃ w ∈ withdrawals, i = w.2.l2_outref
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Lean Computation function. #
Equations
- One or more equations did not get rendered due to their size.
Instances For
Computation Thread #
- i : OutputRef
- w : OutputRef
- txs : List FlattenTransaction
- wtxs : Withdrawals
Instances For
- Mem : ListSimulation.Path → Input
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
DoubleSpend Violation #
Property:
∃ t ∈ txs,
∃ i ∈ spend_inputs(t),
∃ t_1 ∈ txs
: ¬ t = t_1
∧ i ∈ spend_inputs(t_1)
Equations
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Computation #
Equations
- One or more equations did not get rendered due to their size.
Instances For
Computation Thread #
- i : OutputRef
- t' : FlattenTransaction
- txs : List FlattenTransaction
Instances For
- Mem : ListSimulation.Path → Input
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
DoubleWithdraw Violation #
Property:
∃ w, w_1 ∈ withdrawals
: ¬ w = w_1
∧ l2_outref(w) = l2_outref(w_1)
Equations
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Computation #
Equations
- One or more equations did not get rendered due to their size.
Instances For
Computation Thread #
- w : Withdrawal
- w' : Withdrawal
- wts : Withdrawals
Instances For
- Mem : ListSimulation.Path → Input
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Lemmas #
We need to prove that our Computation Threads are checking the property we want.
Verification #
We define a property AllValidProp and we claim that we can split it into
different fraudproofs:
NoInputPropInputNoIdxPropWithdrawnPropDoubleSpendPropDoubleWithdrawProp
However, these props are at different levels.
The main prop AllValidProp is at the ledger level and the other ones are at
the block level.
Fraud Proof #
In this section, we define the scripts and the fraudproofs as inductive types. We are forgetting about the different scripts steps and if they are proving the original statement or not.
In other words, this is a quick fix to have a mechanized implementation of the FraudProof mechanisim.
Scripts interface.
- NoInput (t : FlattenTransaction) (i : OutputRef) : AllInputsScript
- InputNoIdx : FlattenTransaction → OutputRef → FlattenTransaction → AllInputsScript
- WithdrawnInput : FlattenTransaction → OutputRef → Withdrawal → AllInputsScript
- DoubleWithdraw : Withdrawal → Withdrawal → AllInputsScript
Instances For
- NoInputs {data : FPData} (t : FlattenTransaction) (i : OutputRef) : data.txs.contains (TransactionHash.flat_tx_hash t) = true → t.inputs.contains i = true → data.utxos_prev.contains i = false → data.txs.contains i.id = false → AllInputs data (AllInputsScript.NoInput t i)
- InpudNoIdx {data : FPData} (t : FlattenTransaction) (i : OutputRef) (t' : FlattenTransaction) : data.txs.contains (TransactionHash.flat_tx_hash t) = true → t.inputs.contains i = true → data.txs.contains (TransactionHash.flat_tx_hash t') = true → (TransactionHash.flat_tx_hash t' == i.id) = true → t'.outputs.length < i.index → AllInputs data (AllInputsScript.InputNoIdx t i t')
- Withdrawn {data : FPData} (t : FlattenTransaction) (i : OutputRef) (w : Withdrawal) : data.txs.contains (TransactionHash.flat_tx_hash t) = true → t.inputs.contains i = true → (List.contains data.wts w = true → (w.2.l2_outref == i) = true) = (true = true) → AllInputs data (AllInputsScript.WithdrawnInput t i w)
- DoubleWithdraw {data : FPData} (w w' : Withdrawal) : List.contains data.wts w = true → List.contains data.wts w' = true → ((!decide ((w == w') = true)) = true → (w.2.l2_outref == w'.2.l2_outref) = true) = (true = true) → AllInputs data (AllInputsScript.DoubleWithdraw w w')