Question: can we make a blanket double satisfaction attack?
VictorCMiraldo opened this issue · comments
A common attack to most scripts is to rely on a double satisfaction vulnerability: the combination of consuming two utxos that would cause the script to validate but in some slightly off manner. For example, when auditing a DEX we always try to execute the refunding of an order, but consume two order UTxOs instead and pay the difference to an attacker.
Can we make this into a blanket attack in cooked, just like we have with datum hijacking and token duplication?
I understand your view of the double satisfaction attack like this: It consists in adding an input to an otherwise normal transaction and paying the excess after the transaction to the attacker. Since it's always about fooling (validator) scripts, my idea would be to write an attack of a type like
doubleSatAttack ::
(
... -- some constraints on 'a' and its 'RedeemerType' and 'DatumType', probably
) =>
(TypedValidator a -> RedeemerType a -> (SpendableOut, DatumType a) -> Maybe MiscConstraint) ->
Wallet ->
Attack
Here, the first argument of doubleSatAttack
looks at all the SpendsScript
constraints in the transaction to be modified (note that its arguments are exactly the arguments of the SpendsScript
constructor), and returns Just
the additional transaction input if the constraint matches, and Nothing
otherwise. The second argument would be the wallet of the attacker.
My high-level question is this: What if there is more than one possible way to try double satisfaction (specified as above or differently)? In that case, wouldn't we like an attack that can return several modifications of the same transaction, so
type Attack = TxSkel -> [TxSkel]
where the empty list signals that the modification has failed? This would open up many possible other attacks (for example: try all permutations of the outputs). I'll ask @mmontin what he thinks about this.
This is how the folks from plutus-contract
do this: https://github.com/input-output-hk/plutus-apps/blob/a55dcf29047a46f77c5e679aca4ce1021cf250ec/plutus-contract/src/Plutus/Contract/Test/ContractModel/DoubleSatisfaction.hs
This is a relatively unordered brain dump, but I still think it's worthwhile to record it somewhere. In particular, we're after a justification of the Attack
type anyway.
Since we're already talking about generalisations, another reason why type Attack = TxSkel -> Maybe TxSkel
might not be appropriate for the double satisfaction attack: The core of that attack is adding a script input to a transaction, so we have to have a way to obtain that input. A doubleSatAttack
that always has to be preceded by at least a call to utxosSuchThat
would not be useful in combination with the LTL framework. That is: The return value of Attack
s has to depend on the state of the blockchain.
Here are a few idea-germs:
- Something like
type Attack = TxSkel -> Maybe (StagedMockchain TxId)
would mean that we swap onevalidateTxSkel
instruction for a whole sub-trace that ends invalidateTxSkel
. (I'm not sure ifStagedMockChain
is appropriate here or it we sould make it polymorphic on anym
withMonadModalMockChain m
.) - As a slight restriction,
type Attack = SomeContext -> TxSkel -> Maybe TxSkel
, whereSomeContext
is a suitable type that let's us query (a relevant part of) the state of the blockchain. I don't know what that type should be, though, without breaking the interface ofMonadBlockChain
. - A riff on @mmontin's "modifications form a monad" idea: Maybe the monad of modifications has methods that allow for queries of the blockchain state.
type Attack = TxSkel -> Maybe (SomeContext -> TxSkel)
With all of these, one problem that I see immediately is "How should conjunction (LtlAnd
) work? Furthermore, the question about Maybe
remains, lists also seem sensible.