This post was inspired by the paper “FairSwap: How to fairly exchange digital goods” by Dziembowski et. al. (1).
Many blockchain protocols suffer from an inherent non-attributable fault. Meaning a fault where the blame can not be attributed correctly.
For example if I want to buy a piece of data from a merchant, everyone else (outside of the transacting parties) can not distinguish between two failure cases:
- The merchant did not send the data
- I received the data but claimed that I didn’t
These issues can be solved by a mutually trusted intermediary that acts as an escrow service in order to facilitate the exchange.
We can also construct a protocol that solves these faults in specific cases using smart contracts.
Example construction
Suppose we have two players A
and B
.
A
has some data data
that they want to sell to B
.
B
only knows the hash = h(data)
of the data (e.g. because the hash was notarized by a trusted authority). In this example we proof the claim that the data hashes to hash
. We will see later that other claims about the data can be verified as well.
-
In the first step
A
encryptsdata
with keykey
:encData <- ENC(data, key)
and computes the hash of the encrypted data:hashEnc = h(encData)
and the hash of the key:hashKey = h(key)
NowA
sends{hash, hashEnc, hashKey}
to a smart contract and the encrypted dataencData
toB
. B
verifies thathashEnc
matches the encrypted data they received.hashEnc' = h(encData'); if hashEnc' != hashEnc : abort
B
sends a conditional payment to the smart contract with the following conditions:- If
A
fails to send keyk
that matcheshashKey
within a time period, the payment is reverted - If
B
provides a valid fault proof within a time period, the payment is reverted - Otherwise the payment is executed and send to
A
- If
-
A
sends the keykey'
to the smart contract. The contract verifies thath(key')
matcheshashKey
. B
now takes the keykey
and decrypts the data they received:data' = DEC(encData, key)
If the hash of the decrypted data matches the knownhash
, B terminates and the payment is executed. If the hash does not match,B
sendsdata'
as a fault proof to the smart contract. The smart contract verifies the fault proof and reverts the payment if the fault proof was correct.- It computes
encData'
= ENC(data’, key)` - and verifies that
h(encData') == hashEnc
andh(data') !=
h(data)`
- It computes
- If the smart contract did not receive a fault proof within a certain timeout, the transaction is executed and the funds are unlocked for
A
.
Extensions
This example construction is simplified. In practice data
can be quite large and publishing it on the blockchain in the case of a fault would be expensive. Thus the scheme can be easily extended to use merkle trees to keep the fault proof size low.
Non-Attributable Faults in MEV
There is an inherent non-attributable fault in the MEV supply chain. Block producers pay validators to include their block. But validators have to take the word of block producers, that the block is correctly constructed and matches the blinded header that they receive first. This fault gave rise to a trusted intermediary, the relay, which connects validators and block producers and verifies that the blocks are created correctly (simplified, they also act as block producers themselves, bundling transactions from searchers).
Construction
In the following construction, we shorten the validator to V
and the block producer to B
.
Right now the bidding process is kinda backwards in my opinion. Instead of validators buying blocks, they sell block space. But we can easily redefine it. This has some implications on how blocks are constructed, because any MEV now needs to go to the validator instead of the block producer, but they pay for it.
0.1 V
solicits bids by announcing that they are eligible to create the next block.
0.2 Block producers bid on the slot by announcing to V
that they have a block with hash hash
that pays the coinbase value
ether in extracted MEV. They are willing to sell the block to V
for cost
ether. Thus value - cost
is the profit for V
, cost
ist the profit for B
. V
chooses the block n
that maximizes their value - cost
.
V
wants to buy block with hashhash
fromB
and notifiesB
of its intentionB
sends{hashEnc, hashKey, encData}
toV
V
signs a transactiontx
that will sendcost
eth toB
iff- The preimage to
hashKey
was revealed within timeout - No fault proof was provided within timeout
V
sends the transaction{tx}
toB
- The preimage to
B
verifiestx
and sends{key}
toV
V
decrypts blockn
, signs it and sends it to the networkB
sendstx
to the network
Fault Proof Construction I
The fault proof works as follows:
B
sends the following data {txs, postState, difference}
to A
.
The fault proof contains {preState, txs, postState, difference}
All transactions are simulated in the smart contract. The smart contract verifies a proof to account of A
in the pre-state and the post-state.
The fault proof is valid, if the difference between pre and post-state are unequal to the difference claimed by B
.
Problems:
Simulating all transactions of a block within a smart contract is extremely costly and won’t work for big transaction bundles.
Improvements:
- Provide only the preStateHash and proofs to the preState.
- Provide only the postStateHash and proofs to the postState.
- Provide intermediate differences, so only one transaction needs to be simulated
Fault Proof Construction II
The fault proof contains {preStateHash, tx, postStateHash, difference}
`.
In this construction we only provide proofs to the preState, postState and one transaction.
B
needs to send the following data to V
: {preStateHash, tx_1, postStateHash_1, difference_1, ..., sumDifference}
.
The fault proofs now has two parts:
- if
B
claimed thatsumDifference
is higher/lower thansum(difference_x)
:V
provides merkle proofs for{difference_1, ..., sumDifference}
to the contract - if
B
claimed a wrong state transition of transaction with index i:V
provides merkle proofs for{postStateHash_i-1, tx_i, postStateHash_i}
to the contract.
Problems:
Simulating a big transaction is already costly and might cost more than a full block itself.
Conclusion
I think the only viable solution to proving transaction execution on chain is to merklelize the full transaction execution. So that every single operation is part of the proof only the state transition where B
cheated has to be put on chain in order to proof the fault.
I’m pretty sure that is exactly how fraud proofs for optimistic rollups work, so these schemes can probably be adapted to work with this.
The only big open problem I see is that the transaction tx
might not be valid at point 6 anymore, ideally it should be published beforehand. This would mean that the proposer would need to lock enough funds before his slot to pay out a (theoretically) unlimited bounty.
Even if we can not solve the trustless data exchange in the case of MEV with this protocol, its still a neat technique that can be used to fairly exchange digital assets and thus should be part of the toolbox of every developer.
(1) FairSwap: How to fairly exchange digital goods: https://eprint.iacr.org/2018/740.pdf