Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SCP-4107 MIP for universal validator #1

Closed
wants to merge 3 commits into from
Closed

SCP-4107 MIP for universal validator #1

wants to merge 3 commits into from

Conversation

bwbush
Copy link
Collaborator

@bwbush bwbush commented Jul 20, 2022

This document describes a modification to the token-minting process for Marlowe and the unification of the minting of role tokens and the operation of the Marlowe application or payout validators. Although it requires a change to the Language.Marlowe.Core.V1.Semantics.MarloweParams datatype that will eliminate some use cases for role tokens, it will enforce certain new guarantees on the role-token usage.

When used for minting role tokens, this "universal validator" must enforce the following constraints:
1. The validator is parameterized by a `TxOut` that it consumes when minting tokens.
2. The `Redeemer` for the validator is the list of roles (i.e., token names) that will be minted, along with the number of tokens minted on a role-by-role basis.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should allow the monetary policy script to burn tokens, without constraint. This would be a simple change to Alex's validator. This doesn't decrease the security of Marlowe contracts because owners are always at risk of losing their private keys to the wallet where the token resides, sending the token into oblivion, etc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that sounds better than not allowing burning at all. Maybe eventually we can think of a way of requiring the contract to be closed for burning

Copy link
Collaborator Author

@bwbush bwbush Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd need to leave the contract on the chain (just with Close as its continuation) and modify the monetary policy so that it could burn tokens when the contract is in this Close quiescent state.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess leaving the contract on-chain is pretty similar to not burning the tokens

Thus, having the `MarloweParams` parameterize the universal validator via that `TxOut` consumed in the minting process, rather than the `CurrencySymbol`, inexorably ties the minting validator to the spending validator. The present Marlowe validator's use of `CurrencySymbol` in its `MarloweParams` allows it to accept *any* currency symbol for role tokens, but this MIP's use of the `TxOut` in the `MarloweParams` for the *universal* validator ensures that the spending validator can only use tokens minted by minting validator because both these validators are *one and the same*.

Note that the MIP forbids three potentiallly important and presently possible use cases:
1. Customized monetary policies. *Some Marlowe dApps (e.g., DAOs or centrally administered applications) would benefit from supervisory control via a sophisticated minting policy.*
Copy link
Collaborator Author

@bwbush bwbush Jul 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my only reservation about this MIP. It will eliminate novel use of custom monetary policies in conjunction with Marlowe.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking the same thing, some of Marlowe's limitations due to its static nature could be overcome by using custom monetary policies.

At the same time, even if we only maintain/support one particular validator, it doesn't mean that someone could write/fork a validator that follows Marlowe's semantics with a custom monetary policy (although it would be much harder than using the official one)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a potential solution, though I think it's far more likely that we would do this ourselves as part of delivering a system to a large client. This would be expensive though, as we would need to get the forked validator audited and certified separately.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and if we make a version of the validator we will need a corresponding supporting version of the runtime, etc. From the point of view of the audit, I don't think it makes a difference, because the audit results will at best be subject to the monetary policy (independently of whether it is in or outside of the validator).

But from the security point of view, not having the monetary policy fixed by the version of the validator would open the door for attacks that would be pretty tricky to communicate to the user, since the monetary policy can be anything, the best we can do is tell the user it is not standard if its hash doesn't belong to a list (or something like this, I guess).

On the other hand, if we extend the validator to be cleverer and handle the monetary policy, we could manually add support in the future for specific behaviours like the ones you suggest, and then we can explain them to the user easily

Copy link
Collaborator Author

@bwbush bwbush Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, the increase in security is probably worth the loss of flexibility (no custom monetary policies or reuse of role tokens among multiple contracts).


## Path to Active

In implementation is proposed in [marlowe-cardano PR#117](https://github.com/input-output-hk/marlowe-cardano/pull/117).
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've merged the Language.Marlowe.Scripts from PR#117 into the SCP-4107 branch, which is derived from the sprint-61 branch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure but maybe it would be valuable to include some summary about the implementation here?

From my perspective it is worth mentioning that we are doing here a "unification" of miniting policy script and spending script which have completely different types! We should ask ourselves - is this proper usage of this technology which should provide as guarantees through types?

https://github.com/input-output-hk/marlowe-cardano/pull/117/files#diff-e96ad0912b23a6a2a412ad1447699ac9986a492161ee9a00e1407c01aadbc616R324

From my perspective this solution requires a lot of "low level hackery" and makes our on chain code pretty hard to understand but of course I'm not proficient plutus coder so my point may be invalid.

I wonder how spread this idiom is in the larger plutus community. Are there any other idioms which we can adopt?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Are we bypassing any checks by doing this?

One thing related to this to consider too is that we may be using an "unintended feature", so maybe in the future, a new version of Plutus or the ledger could prevent us from continuing doing this? We could have this as a temporary hack and liaise with Plutus to potentially get a less hacky way of doing this same thing, or at least making sure it doesn't get disabled

Copy link
Collaborator Author

@bwbush bwbush Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we bypassing any checks by doing this?

I carefully reviewed the proposed implementation, and it doesn't by bypass any checks. It may tie the validator to Plutus V1 or V2, because we don't know what Plutus V3+ will contain.


## Backwards Compatibility

This alteration to the validator is not backward compatible with the following Marlowe components:
Copy link
Collaborator Author

@bwbush bwbush Jul 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we both maintain backwards compatibility and support custom minting policies if we change MarloweParams to the sum type

data MarloweParams =
    MarloweParams             -- Previously.
    {
      rolePayoutValidatorHash :: ValidatorHash
    , rolesCurrency           :: CurrencySymbol
    }
  | MarloweUniversalParams    -- This MIP.
    {
      rolePayoutValidatorHash :: ValidatorHash
    , uniqueTxOutRef          :: (TxId, Integer)
    }

and make some minor adjustments where uniqueTxOutRef is used and where rolesCurrency is used?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we care that much about backward compatibility? We do have the advantage that Marlowe isn't currently used in real-world solutions (can we verify this assumption?) which means we can afford to break backwards compatibility without causing significant disruption. I would just make this a clean break at this point, essentially invalidating all legacy Marlowe contracts, as they are all 100% test contracts anyway.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If at some point in the future we want to do this, we could use GADTs and type parameters to carry a witness of validator versions at the type level (like the hard-fork combinator does in Cardano Node):

data MarloweParams (version :: ValidatorVersion) where
  MarloweParamsV0
    :: ValidatorHash
    -> CurrencySymbol
    -> MarloweParams ValidatorV0
  MarloweParamsV1
    :: ValidatorHash
    -> TxOutRef
    -> MarloweParams ValidatorV1
  

But at least for now, I don't think the added complexity is worth it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I much prefer the GADT approach, though we'd have to make sure this is compatible with the Plutus compiler.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the discussion so far, I propose we not include this flexibility to the old-style MarloweParams.

1. Customized monetary policies. *Some Marlowe dApps (e.g., DAOs or centrally administered applications) would benefit from supervisory control via a sophisticated minting policy.*
2. Minting of additional role tokens after the original minting. *There may be certain crowd-oriented Marlowe dApps where this is desirable.*
3. Burning of role tokens after a contract completes. *This makes it impossible ever to recover the minimum Ada associated with a role token, thus effectively increasing the up-front cost of Marlowe contracts.*

Copy link
Collaborator Author

@bwbush bwbush Jul 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not completely sure that moving MarloweParams into MarloweData (to maximize the use of reference scripts, SCP-4174) is compatible with the universal validator. We’d have to make sure that the minting script depends upon MarloweParams but the spending script doesn’t, even though these two scripts must be the same Plutus code.

Perhaps we should implement SCP-4107 and SCP-4174 at the same time?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why the spending script could not depend on the MarloweParams?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why the spending script could not depend on the MarloweParams?

It currently does, but it is desirable for the spending script not to depend upon any parameters because then it could be used as a reference script for every Marlowe contract. If the spending script depends upon a parameters, then we'd need one reference script for each parameter set.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see having a single Marlowe interpreter (per version) across the whole chain a really huge possible optimization strategy. We are now struggling with transaction size limits and this clearly impacts further development of the Language itself.

I want to strongly emphasize that we possibly lose a huge benefit of our architecture which is based on a constant language interpreter.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add MarloweParams to the spending script or something like that? It does sounds like having a single Marlowe validator per version would be very beneficial

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not completely sure that moving MarloweParams into MarloweData . . . is compatible with the universal validator.

This method is indeed compatible with a single Marlowe interpreter across the whole chain, if the monetary policy enforces that the Datum send to the Marlowe script contains the MarloweParams. This could be enforced with a minor change to https://github.com/input-output-hk/marlowe-cardano/blob/f51252d682238932a412f0f6baaf3f354309e2bc/marlowe/src/Language/Marlowe/Scripts.hs#L315

Copy link
Collaborator

@hrajchert hrajchert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in principle this LGTM, my main question is if this is a pre-requisite of the universal script or if it's something to avoid attack vectors.

This document describes a modification to the token-minting process for Marlowe and the unification of the minting of role tokens and the operation of the Marlowe application or payout validators. Although it requires a change to the `Language.Marlowe.Core.V1.Semantics.MarloweParams` datatype that will eliminate some use cases for role tokens, it will enforce certain new guarantees on the role-token usage.


## Motivation
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this MIP a requirement for having Universal Validator and being able to utilize reference scripts to reduce the transaction size or is that independent?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This MIP is independent of reference scripts, but we could modify this MIP to better support reference scripts by moving MarloweParams into the Datum.

Thus, having the `MarloweParams` parameterize the universal validator via that `TxOut` consumed in the minting process, rather than the `CurrencySymbol`, inexorably ties the minting validator to the spending validator. The present Marlowe validator's use of `CurrencySymbol` in its `MarloweParams` allows it to accept *any* currency symbol for role tokens, but this MIP's use of the `TxOut` in the `MarloweParams` for the *universal* validator ensures that the spending validator can only use tokens minted by minting validator because both these validators are *one and the same*.

Note that the MIP forbids three potentiallly important and presently possible use cases:
1. Customized monetary policies. *Some Marlowe dApps (e.g., DAOs or centrally administered applications) would benefit from supervisory control via a sophisticated minting policy.*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking the same thing, some of Marlowe's limitations due to its static nature could be overcome by using custom monetary policies.

At the same time, even if we only maintain/support one particular validator, it doesn't mean that someone could write/fork a validator that follows Marlowe's semantics with a custom monetary policy (although it would be much harder than using the official one)

Note that the MIP forbids three potentiallly important and presently possible use cases:
1. Customized monetary policies. *Some Marlowe dApps (e.g., DAOs or centrally administered applications) would benefit from supervisory control via a sophisticated minting policy.*
2. Minting of additional role tokens after the original minting. *There may be certain crowd-oriented Marlowe dApps where this is desirable.*
3. Burning of role tokens after a contract completes. *This makes it impossible ever to recover the minimum Ada associated with a role token, thus effectively increasing the up-front cost of Marlowe contracts.*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to burn tokens after a contract complete?
Is this something that the applyInput does when it reaches a close?
could we pay the min-ada to the token owner when burning?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unburnable tokens are a waste of ADA.

@bwbush bwbush requested a review from simonjohnthompson July 21, 2022 11:56
Comment on lines +74 to +79
* Marlowe backend, `Language.Marlowe.Client`
* Marlowe CLI, `marlowe-cli`
* Marlowe Run, `marlowe-dashboard-server`
* Marlowe history, `Language.Marlowe.Client.History`
* Marlowe test cases
* Marlowe Pioneer Program lectures

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these components are soon going to be superseded by runtime-based versions anyway. And we can still run the legacy code if we need to.

Copy link

@jhbertra jhbertra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. I think the proposal is reasonable, and the concerns that have been raised seem to either be non-issues (backwards compatibility) or have feasible workarounds (supporting custom monetary policies).

@bwbush bwbush requested a review from paluh July 26, 2022 14:52
#### Minting

When used for minting role tokens, this "universal validator" must enforce the following constraints:
1. The validator is parameterized by a `TxOut` that it consumes when minting tokens.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also consider a hybrid which takes Maybe TxOut. In the case of Nothing we get a constant validator which checks the datum for a policy id. I know it is not particularly pretty encoding but it covers both scenarios - more secure universal with sever space limitation (only referencing a script from a single contract thread - unique script per contract) or full script referencing (one script to rule them all per Marlowe version).

1. Customized monetary policies. *Some Marlowe dApps (e.g., DAOs or centrally administered applications) would benefit from supervisory control via a sophisticated minting policy.*
2. Minting of additional role tokens after the original minting. *There may be certain crowd-oriented Marlowe dApps where this is desirable.*
3. Burning of role tokens after a contract completes. *This makes it impossible ever to recover the minimum Ada associated with a role token, thus effectively increasing the up-front cost of Marlowe contracts.*

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see having a single Marlowe interpreter (per version) across the whole chain a really huge possible optimization strategy. We are now struggling with transaction size limits and this clearly impacts further development of the Language itself.

I want to strongly emphasize that we possibly lose a huge benefit of our architecture which is based on a constant language interpreter.


## Path to Active

In implementation is proposed in [marlowe-cardano PR#117](https://github.com/input-output-hk/marlowe-cardano/pull/117).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure but maybe it would be valuable to include some summary about the implementation here?

From my perspective it is worth mentioning that we are doing here a "unification" of miniting policy script and spending script which have completely different types! We should ask ourselves - is this proper usage of this technology which should provide as guarantees through types?

https://github.com/input-output-hk/marlowe-cardano/pull/117/files#diff-e96ad0912b23a6a2a412ad1447699ac9986a492161ee9a00e1407c01aadbc616R324

From my perspective this solution requires a lot of "low level hackery" and makes our on chain code pretty hard to understand but of course I'm not proficient plutus coder so my point may be invalid.

I wonder how spread this idiom is in the larger plutus community. Are there any other idioms which we can adopt?

The lack of an on-chain guarantee for the role-token monetary policy opens Marlowe to several off-chain vulnerabilities: For example, a Marlowe contract might be created for role tokens having an open monetary policy that allows additional role tokens to be created after the contract commences. The controller of that monetary policy could mint duplicate role tokens for parties in the contract and impersonate them, authorizing without consent transactions using the duplicate tokens.

Thus, enforcing that each role tokens be a minted only once provides a security guarantee that duplicate role tokens will not be later minted and those duplicates used for role-based authorization of Marlowe choices and deposits. It also provides an option for minting true non-fungible native role tokens (that there is provably only ever one minted) if minting is limited to exactly one token per role.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we take a step back and consider what these "on chain" guarantees mean for the Marlowe users?

  1. In the current situation how do we validate that a given transaction really represents a safe Marlowe contract "deployment"?

    It seems that we have to perform few steps to gain confidence that we deal with safe Marlowe setup (n - is contract thread length on the chain; I assume separate query per transaction):

    • We have to check if proper spending validator was used (O(1))

    • We have to check if proper/safe miniting policy was used to mint the tokens (O(n)).

    • We have to check initial state and the initial contract itself (O(n))...
      or we can skip traversing history and only analyze the current continuation or hash and state (O(1)).

    • We can check role distribution if this is also important to us (O(n)).

  2. How does this proposal improves the current situation?

    It seems that we can skip miniting script verification step and possibly have constant time of verification without quering the blockchain (O(1)) (Is this really the case in real scenarios - don't we want to usually gain the whole picture on the contract anyway? Is O(n) verification complexity a problem in real scenarios?).
    Additionally to perform this verification step we don't have to use two parameters (Marlowe validator script + Mniting policy script) but only Marlowe validator script is required.

  3. Can we reduce the above verification flow without introducing universal validator?

    I think so. In the next comment I sketch a verification schema which allows us
    to do O(1) verification of Marlowe Tx and proves that proper minting policy
    was used together with proper Marlowe validator.
    Verification step requires only a proper minting policy as an input and the
    transaction.

Copy link
Member

@paluh paluh Jul 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try to sketch a possible "Marlowe policy tracking" schema. It is a replacement for a unified validator which preserves O(1) characteristic of script(s) verification step.
This approach uses constant Marlowe validator and relies only on a small modifications of the Marlowe validator itself. It moves most of the invariant checks to the policy script.

Scope limit

I focus here on the policy tracking so I skipped details regarding MarloweParams handling as well as validatorToken burning. I want to only indicate that in this architecture we are going to have MarloweParams which consists of just a single value - roleCurrency. We are going to have a constant rolePayoutScript hash (role policy script gonna pick currency symbol from its datum) and a bit modified Marlowe validator check for the payout script and bit different constant payout script itself.
It is worth mentioning that we want to keep roleCurrency and minting of thread token even in the case of contracts without roles so we can mark PK payouts using datum with roleCurrency to prevent double spending.
I'm going to expand on that if we want to really consider these schema.

Types

data PolicyParams = PolicyParams
 { ppTxOutRef :: TxOutRef
 , ppRoles :: List TokeName -- Left for simplicity.
                            -- We can probably replace this list by just a `hash`. 
 }

data MarloweEnv = MarloweEnv
 { marloweData :: MarloweData
 , marloweParams :: MarloweParams
 , policyParams :: PolicyParams
 }

Verification flow:

Inputs

  • Our correct minting policy script function.

  • Transaction which we want to verify.

Verification procedure

  • Pick UTxO of interest (which possibly contains a Marlowe contract validator).

  • Check its datum for policyParams :: PolicyParams value (the whole datum should be value of type MarloweEnv).

  • Apply our policy script to the above policyParams to get currencySymbol :: CurrencySymbol from that.

  • Check if in the Value part of the UTxO we find a token of this currency with empty token name { currencySymbol -> { "" -> 1 }} (we call it validadatorToken from this point forward).

  • If that is the case then the check is done and we should be sure (we still lack proper proof ;-)) that a given transaction is a possible last element on the "Marlowe Contract thread" which was properly initialized and executed:

    • It uses proper Marlowe validator.

    • It used a proper policy script to mint role tokens.

    • The policy was correctly initialized so the currency is unique and only proper role tokens were minted and it is impossible to mint any more tokens of this currency.

Implementation

Minting policy

Constant:
  • marlowe :: Address - Marlowe Validator script address
Parameters:
  • PolicyParams { ppTxOutRef, ppRoles }
Checks:
  • Check if this transaction consumes ppTxOutRef.

  • Check if there is exactly one TxOut to an address of desired spending validator marlowe (uniqueness can be relaxed in the future - we can replace by stronger datum checks I think).

  • Check if the value attached to this TxOut contains validatorToken - one coin of ownCurrencySymbol with empty token name.

  • Check that there is not an empty string between ppRoles (not $ "" `elem` ppRoles).

  • Check if transaction mints set of tokens consisting of ppRoles and validatorToken ([""] <> ppRoles).

  • Check if datum for marlowe TxOut contains a value of MarloweEnv and (using pseudo notation to distinguish datum and values from script parameters):

    • datum.policyParams.ppTxOutRef == parameters.ppTxOutRef

    • extractRoles datum.marloweData.marloweContract == parameters.ppRoles

    • datum.marloweParams.roleCurrency == ownCurrencySymbol

Marlowe validator

Checks related to "policy tracking"
  • Check if a possible singleton continuation (txOut at marlowe address):

    • Is associated with datum in which policyParams is preserved.

    • roleCurrency references correct validatorToken in Value.

    • Preserves validatorToken in its Value.

  • If there is no continuation txOut check if validatorToken is burned in the transaction.

Execution
  • Marlowe validator uses marloweData to store its state.

  • Marlowe validator uses marloweParams.roleCurrency when authenticating parties.

  • Marlowe validator uses marloweParams.roleCurrency together with some constant payoutScript address to check payouts handling.

  • Marlowe validator uses marloweParams.roleCurrency to check if PK payouts are marked using this value in datum correctly.

Sketch of the proof

A proper proof should be provided but the sketch of the reasoning could look like this:

  • Correct minting policy => unique currency symbol because of ppTxOutRef check.

  • Correct minting policy => single and correct Marlowe validator output exists.

  • Correct minting policy => role tokens are minted correctly for a given Marlowe validator contract.

  • Correct Marlowe validator => Marlowe validator is preserved during contract execution.

  • Correct Marlowe validator => policyParams is preserved during contract execution.

  • Correct minting policy => unique validatorToken is assigned to correct Marlowe validator.

  • Correct Marlowe validator => validatorToken is preserved and witnesses a unique trace to to the initial transaction.

  • Correct Marlowe validator => validatorToken is burned at the end of the contract execution.

  • validatorToken which contains correct currencySymbol attached to an UTxO => Correct minting policy was used during initial transaction for minting.

Pros / Cons

Comming soon... ;-)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it uses one special token that the validator never lets go of and burns at the end. And we know the special token is unique because that is enforced by the minting policy, and we know that a validator that has it necessarily comes from the minting policy cause that is the only way of getting the token. Very clever :)

I cannot see any problems with the idea at the moment, but yes it is going to be tricky to make sure there really aren't subtleties

Copy link
Member

@paluh paluh Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it has any practical meaning or sens what I'm going to write now so please just ignore it if it doesn't ;-)

In merkleized case if we would like to merkleize role distribution as well we probably wanted to have a bit different strategy because roles would be minted throughout the execution of the contract.
In this case we can treat validatorToken value as a "counter" which should be increased on every step. This counter would be increased necessarily by our policy script - we should branch on the pcTxOutRef check and if we are in the middle of the thread we should just grab validatorToken from the input and increase the value. Policy script would also check at every step and output role tokens if necessary.
In such a case Marlowe validator should all the time check if the continuation contains counter increased by one.
In this scenario validatorToken witnesses that we used correct policy script at every step in the Marlowe contract thread back to the initial transaction and it handled all contract tokens minting.
This strategy is in some sens generalization of the previous one and can be used in the simpler case as well.

Side note: I think that this policy driven execution stepper could be generalized further. It can be a base for "a library" of composable constant functions on the chain. Composition would be done through a minting policy which takes a "plan" of the execution as parameter (ordered list of functions representing fun3 . fun2 . fun1) and initial value as parameter as well (and some TxOutRef of course as well ;-)). It should check at every step if we proceed accordingly to the plan. "Library functions" should only check the step counter growth and then they should perform their job.
Maybe we can change plan into DAG and start doing some concurrency by using token names as threadId's or something ;-)
I'm not sure if this idea has any real value though... but maybe if we are able to split some huge interpreter into small and fast composable parts then who knows ;-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a viable approach that solves the same currency-lookup problem that the universal validator does. However, in increase the transaction size (larger validator and extra token) and execute cost (more logic) and memory (larger script context). Is that worth it?

The spending validator probably doesn't a need "singleton continuation" check because validatorToken is a singleton that cannot be sent simultaneously to two continuations.

A variation of this approach (and the DSL) could open possibilities for minting additional role tokens while a contract is running. As you point out, something along these lines could

BTW, if we had used a validatorToken approach like this previously, then the validator wouldn't have been subject to double satisfaction.

I'm a little concerned about the use of a blank token name "" for validatorToken because it is a special value and because it means that a blank token name that is valid in Marlowe semantics is not valid on chain. This is probably okay, however, because the Semantics don't enforce the 32-byte maximum on token names, either.

Copy link
Member

@paluh paluh Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course verification schema gonna change. In such a case we have to just grab the whole history to discover that "non minting policy" was a valid choice. Unfortunately we also need info now about the actual validator address etc. so maybe it is just better to mint the thread token - it is not a significant overhead.

Under the table I put few comments. I would like to have really focused discussion which properties from the table are seriously meaningful and estimate more precisely what are the differences for this meaningful properties.

I would also like to have some perspective and evaluation of the flexibility and ease of reasoning regarding different approaches. I sketched above on demand minting schema for merkleized role distribution... it would be nice to have a set of solutions to this kind of variations and evaluate them.

Copy link
Member

@paluh paluh Jul 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@palas At the end I think that having uniform thread token based architecture (validatorToken) is really beneficial for Marlowe handling in general. runtime can just assume that we use constant validators and thread tokens and that is the way to discover a particular contract history. Even if it means a slight overhead (impact should be measured - we lower the throughput by the size of minting policy) in the initial transaction it provides a clear on chain interface. This approach doesn't affect spending validator so it size stays the same.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't necessarily think that tracking PolicyParams makes sens because verification can be done through direct check of the initial transaction.

Copy link
Member

@paluh paluh Jul 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding validatorToken name - if we consider any surface language to the Marlowe I would rather imagine that "" wasn't a role valid identifier ;-)

Copy link
Member

@paluh paluh Jul 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In principle we can reserve for validatorToken any identifier which is invalid for the Role value.

When used for minting role tokens, this "universal validator" must enforce the following constraints:
1. The validator is parameterized by a `TxOut` that it consumes when minting tokens.
2. The `Redeemer` for the validator is the list of roles (i.e., token names) that will be minted, along with the number of tokens minted on a role-by-role basis.
3. Minting can only occur if there is output to a the Marlowe script address: i.e., the minting transaction must also create a new Marlowe contract.
Copy link
Collaborator Author

@bwbush bwbush Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this clarification that the minting must create a new Marlowe contract.

1. Customized monetary policies. *Some Marlowe dApps (e.g., DAOs or centrally administered applications) would benefit from supervisory control via a sophisticated minting policy.*
2. Minting of additional role tokens after the original minting. *There may be certain crowd-oriented Marlowe dApps where this is desirable.*
3. Burning of role tokens after a contract completes. *This makes it impossible ever to recover the minimum Ada associated with a role token, thus effectively increasing the up-front cost of Marlowe contracts.*
4. Reuse of role tokens among multiple contracts.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this clarification that role tokens cannot be used among multiple contracts.

Copy link
Collaborator Author

@bwbush bwbush Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this isn't correct. The current implementation would allow the reuse of role tokens. I believe that it is impossible to forbid the reuse of role tokens. (We'd have to switch to "smart" tokens in order to forbid this.)

@bwbush
Copy link
Collaborator Author

bwbush commented Jul 29, 2022

What problems are we trying to solve here, and at what cost?

  • Same hash for minting and spending?
  • Enforce contract creation during minting?
  • Forbid reuse of role tokens?
  • Allow multiple copies of role tokens?
  • Currency symbol implies spending validator address?
  • Custom monetary policies?
  • Burnable role tokens?
  • Allow multiple Marlowe contracts to run in parallel in same transaction?
  • Same spending validator address for all Marlowe contracts?
  • Same payout validator address for all Marlowe contracts?
  • Fast off-chain verification of correct monetary policy?
  • Fast off-chain verification of correct spending validator?
  • Smaller validator size?
  • Lower execution cost?
  • Lower execution memory?

Here are properites of the three validators that we've been discussing, plus a fourth ("Resulable") that simple moves MarloweParams to Datum .

Property Current Universal validatorToken Reusable
Single spending script address? no no yes yes
Single payout script address? no no yes yes
Spending address implies currency symbol? no yes no no
MarloweParams in script in script in datum in datum
Currency in MarloweParams? yes no no yes
UTxO in MarloweParams no yes yes no
Reuse of role tokens yes yes no yes
Multiple Marlowe contract in same transaction? yes yes yes no
Mint during contract creation? not enforced enforced enforced either
Minting parameter TxOutRef MarloweParams TxOutRef, [RoleName] TxOutRef
Custom monetary policy yes no no yes
Allow multiple copies of role tokens? yes yes no yes
Burnable role tokens? no no no yes
Payout script per-currency per-currency all currencies all currencies
Payout datum role name role name role token role token
Single settlement tx "contract throughput" same lower higher higher
Execution memory relative to current same higher higher same
Execution steps relative to current same higher higher same

@paluh
Copy link
Member

paluh commented Jul 29, 2022

Transaction size relative to current

Let me rephrase this to:

Transaction "max datum throughput" (using reference scripts)

EDIT:
When we talk about transaction size limit the most important parameter from my perspective is the maximum "Contract datum" throughput. This metric should be closely correlated to the size of contracts which we are able to actually handle without merkleization (of course we should take also execution time and memory limit into account to get full picture):

Reference scripts gives us a flexibility to separate script deployment and Marlowe contract transaction settlement. If we use all the scripts by reference then we can assume that all the rest of the transaction size can be used by Marlowe contract.
We can still analyse how likely it is that our Marlowe contract submission doesn't require additional transaction (reference script deployment).
throughput - how much space unique policy script or unique validator script leaves in the transaction for datum.

Here are my rough estimates:

  • Current: validator has to be part of the first spending transaction because it is unique so the throughput is **really low**.

  • Universal: validator has to be provided as a minting policy script - it is unique. In such a case throughput is **really low**.

  • Resusable: depends on the size of the minting policy script of choice. We can assume **maximal** throughput.

  • validatorToken: This is not the minimal minting policy but the trade offs should be carefully analyzed (validatorToken gives us non reusable currency guarantee). We also need a place for additional token in Value and TxOutRef (we can skip role tokens list I think- we will see). In such a case throughput is **nearly maximal**.

Execution memory relative to the current

It is a bit speculative to compare these two but seems like in general between all options the difference won't be significant. It requires testing:

  • Universal: it has to load both policies on every step.

  • validatorToken: has to load additional Value and [data] (like I said - we can drop roleNames from PolicyParams I think or we can drop PolicyParams entirely if we want to drop O(1) verification) and check (preservation of token [and policy params]). On the other hand it loads much smaller minting policy in the initial step and it loads only spending validator in every input application.

Brian please let me know if I can update the table with the above estimations.

@paluh
Copy link
Member

paluh commented Jul 29, 2022

Few more comments / questions regarding semantics of properties listed in the table.

Custom monetary policy

validatorToken: monetary policy is a separate script from the spending script. We can publish a different minting method without modification to the spending validator. I would say that custom policy can be added on top "pretty easily".

Maybe that is not best example but I sketched above a generalization which can handle incremental minting in merkleized contracts for example but requires a slight change in the spending validator check and minting policy usage on every transaction.

Minting parameter

validatorToken: I think that we can safely drop role tokens [TokenName] from its parameters. I put it there to simplify reasoning but after a second though and speculation about dynamic minting I don't think that it has to be part of PolicyParams which could lower the footprint to just validatorToken [and txOutRef tracking] (which I consider unnecessary from practical point of view).

Allow multiple copies of role tokens?

I'm not sure what that means. Do we mean if we can change minting policy easily?

@bwbush bwbush removed their assignment Aug 3, 2022
@bwbush
Copy link
Collaborator Author

bwbush commented Aug 3, 2022

Brian please let me know if I can update the table with the above estimations.

@paluh , yes, please updated the table.

I think the memory impact is more important than the transaction-size impact. The transaction size can be alleviated by CIP-33 reference scripts, but that doesn't save memory. We have seen cases where a large ScriptContext causes exhaustion of the execution-memory budget.

@bwbush
Copy link
Collaborator Author

bwbush commented Aug 3, 2022

Allow multiple copies of role tokens?

I'm not sure what that means. Do we mean if we can change minting policy easily?

@paluh, PolicyParams has a list of token names, but the other approaches have a map of token name to number of tokens. I assume that having a list of token names implicitly means that PolicyParams intends that only one of each role token would be minted.

@bwbush
Copy link
Collaborator Author

bwbush commented Aug 3, 2022

My opinion about the universal validator has vacillated over the past two weeks, so I thought I'd summarize my current thoughts.

  1. This universal validator is not solving any problem or addressing any requirement that I know of evidence for, externally or internally.
    • It forces minting of role tokens to occur in the contract-creation transaction, but it does not enforce the converse, which means role tokens can be reused.
    • It makes the currency hash and the script hash identical.
    • It forbids custom monetary policies.
  2. The change to the universal validator increases execution cost of the Marlowe validator script because it increases both its execution memory and execution steps.
  3. As implemented, the universal validator is not compatible with the constant-validator approach that we'd like to use with CIP-33 reference scripts on Babbage.

@paluh
Copy link
Member

paluh commented Aug 4, 2022

@bwbush Should I iterate over validatorToken design here or should I create a discussion thread which can be turned into a proper MIP at the end?

@paluh
Copy link
Member

paluh commented Aug 5, 2022

Allow multiple copies of role tokens?

I'm not sure what that means. Do we mean if we can change minting policy easily?

@paluh, PolicyParams has a list of token names, but the other approaches have a map of token name to number of tokens. I assume that having a list of token names implicitly means that PolicyParams intends that only one of each role token would be minted.

@bwbush I think that we can drop role names from minting policy params or include only a hash of the Map. This list was a quick approximation (I did a sketch of possible alternative of the validator above).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants