Multi-Script Hashes
Abstract
This is a (draft) CIP to introduce a simple way to statically identify a
collection of scripts that depend on each other, so that each script can
detect if a transaction tries to substitute an outside script address for an
expected one.
Motivation
Most non-trivial DApps will be distributed not only in the usual sense, but
will also have their on-chain logic spread across multiple validator
scripts. Some types of transactions will then be expected to consume and
create UTxOs belonging to different scripts.
To take an abstracted example, consider a DApp with only two validators
scripts, Sā and Sā. There are transactions Tā that deal only with the
first validator script, transactions Tā that deal only with the second, and
transactions Tāā that involve both. A Tāā might for example simply move
some funds from the first script to the second. The question is, how can the
Sā ensure that the recipient is in fact Sā?
The simplest solution is that we first compile Sā, then supply its
ValidatorHash or its Address as a parameter to Sā, which can trivially
compare it to the recipientās address.
If, however, there is also a valid transaction Tāā that moves funds from
Sā to Sā, we canāt apply the same solution: there is currently no way to
resolve a mutual dependency between scripts.
There are workarounds to guard against the substitution. We can mint a limited
amount of a token, for example, supply its CurrencySymbol as a parameter to
all scripts, and then ensure that every UTxO locked by any script in our DApp
carries one of these tokens as an identification badge. This however
introduces unnecessary complexity and increases all transaction costs. It
would be better to solve the root issue of mutual dependency.
Specification
The minimum required functionality can be provided with just two additional
functions in Plutus API, and a new field in TxInfo:
validatorHashes :: [Validator] -> [ValidatorHash]
allScripts :: [Validator] -> ScriptLookups Any
txInfoValidators :: TxInfo -> [ValidatorHash]
The new validatorHashes function is a generalization of the existing
validatorHash :: Validator -> ValidatorHash
The new function would hash the entire list of validators, together with
[1..n] for each result hash. The length of the result list would equal the
length of the argument, and the value of every single ValidatorHash result
list item would depend on every Validator item in the argument list. In
other words, a change to any single Validator would affect every
ValidatorHash.
The same explanation applies to the new allScripts function, which is a
generalization of the existing
otherScript :: Validator -> ScriptLookups Any
The [ValidatorHash] list calculated by allScripts would be made available
as the txInfoValidators accessor so each validator triggered by the
transation could check it against the addresses of transactionās UTxOs.
Furthermore, if allScripts is invoked with argument vs, every TxOut
consumed by the transaction must satisfy all (`elem` validatorHashes vs) . toValidatorHash . txOutAddress. The transaction otherwise fails in phase 1
of validation.
Rationale
Assume the off-chain portion of our example DApp consistently specifies the
allScripts [Sā, Sā] lookup with every transaction and that both validators
use txInfoValidators to verify both the ownInput address and the
destinations of both Tāā and Tāā. If an attacker attempts to submit a
transaction substituting a third address Sā, the validators would reject
it. Adding otherScript Sā or allScripts [Sā, Sā] as a lookup would not
work: the former would not populate txInfoValidators and the latter would
provide the wrong address for Sā.
Backwards Compatibility
The addition of two new functions validatorHashes and allScripts would be
a backward-compatible extension of the API. The addition of txInfoValidators
is more problematic. Perhaps it can be done without a hard fork, but Iām not
sure.
Copyright
Apache-2.0