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

PNodeTrusted type wrapper for distinguishing node-trusted script context values and other values from redeemer/datum #637

Open
SeungheonOh opened this issue Jan 16, 2024 · 6 comments
Labels
enhancement New feature or request low priority

Comments

@SeungheonOh
Copy link
Collaborator

SeungheonOh commented Jan 16, 2024

Idea from @peter-mlabs

Inputs of a smart contract script takes two kinds of arguments: arbitrary values from user and node-generated script context. Both of the arguments are Datum, and, therefore, user-provided value can also be a script-context. However, only what's provided by Node can be trusted as valid. By introducing a closed(no exposed constructors) wrapper PNodeTrusted we can mark the difference between two possible script-contexts.

Use case: it ensures certain values of user-defined types are always valid by allowing those types' smart-constructors to only allow values from PNodeTrusted. This is especially useful when used with state threads(like oracles) who's onchain datum is designed to be always valid.

-- | Not all `PTxInfo` is "valid"
mkMyDatum :: Term s (PTxInfo :--> PMyDatum)
mkMyDatum :: Term s (PNodeTrusted PTxInfo :--> PMyDatum)
@SeungheonOh SeungheonOh added enhancement New feature or request low priority labels Jan 16, 2024
@bladyjoker
Copy link
Collaborator

Question! Why do we care about this distinction? Would it make sense to just have safe PTxInfo constructors and data parsers to always be valid? In essence everything is PNodeTrusted?

Another question! What level of validity are you interested in? To understand whether a TxInfo is 'valid' can ultimately mean evaluating a TxInfo under ledger rules. But 'lesser' validity could mean for example that any DatumHash exists in txInfoDatums and such.

For things like TokenName, CurrencySymbol, mint or output Value have some simple validation rules that we definitely care about and are a no brainer, but for TxInfo and more complex types it's not necessarily clear what level of validity are we talking about?

@SeungheonOh
Copy link
Collaborator Author

SeungheonOh commented Apr 11, 2024

Distinction that separates if a given datum is created by the node itself or by user is useful because ones created by the node will always be "true", syntactically and semantically. So, for example, if ledger datum, let's say PTxOut, is from node directly, we know that tx referenced in txId will always exists actually on-chain and so forth. But sometimes we want to bake our own ledger type values from scratch; for example, when you try to lookup for some output references from transaction. In that cases, we will do something like

foo :: Term s PTxOut
foo = pconstant $ TxOut "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 99

When it is created like this(by user), made value might be in correct format(transaction hash is 32 bytes, etc etc; syntactical correctness) and everything, but we don't really know if this reference actually exists on-chain(semantical correctness). This goes similarly with other types.

Using smartconstructor gives a slightly different assurances than marking node trusted values. As shown above, we can construct valid PTxOut all day, but we will never know if that txOut will exists on node's context. In other words, it is impossible to enforce all datum to be node trusted just by enforcing smart constructor on everything. So smartconstructor will only ensure syntactical correctness of constructed data. (I think smart constructor is a good direction but is slightly unrelated to this issue)

@SeungheonOh
Copy link
Collaborator Author

SeungheonOh commented Apr 11, 2024

To understand whether a TxInfo is 'valid' can ultimately mean evaluating a TxInfo under ledger rules. But 'lesser' validity could mean for example that any DatumHash exists in txInfoDatums and such.

This will depend on where we draw lines between semantical and syntactical correctness. If we make assurance that DatumHash exists in txInfoDatums as semantics, there's one less thing to care about at smart constructor checking syntactical correctness. So this would be easier, but regular ledger types will be more loosely assured.

If we count this as syntactical correctness, we have more things to check on smart constructor, but than we get better assurance for non-nodetursted types.

I don't know where would be best to draw this line

@L-as
Copy link
Member

L-as commented Apr 14, 2024

I think what you want is not PNodeTrusted, but PUntrusted, which I feel like already might exist somewhere?

You want to denote that you have data that might be of some type, and in this case it's only for Data-encoded types.

@SeungheonOh
Copy link
Collaborator Author

data that might be of some type
This is PData and PAsData PXYZ where PXYZ is Data-encoded. So it exists.

PNodeTrusted is trying to distinguish ledger types that came directly from the node, i.g. script context, datum, and redeemer. They often have stricter invariants that can be exploited

@peter-mlabs
Copy link

Where this idea came from is trying to identify what the introduction rules for something like an oracle exchange rate should be.

If you're posting a new change rate, that you might have something like

Integer -> Positive -> ExchangeRate

but if you're consuming an exchange rate from a reference UTxO, you need a few things:

  • You need a reference UTxO extracted from the script context that is passed as an argument to the script
  • You need to check that the reference UTxO carries the oracle's state thread token
  • You need to decode the reference UTxO's datum into an ExchangeRate

Folks usually do this ad-hoc by performing all of this manually, but its not type safe.

You can manually create a TxOut that has the right state thread token and an arbitrary datum, and that's no longer valid. And you can even throw this into a script context inside your script if you want.

The first bit -- "extracted from the script context that is passed as an argument to the script" -- is what the PNodeTrusted distinguishes. Its for distinguishing whether a value comes from the node (i.e., exists on-chain) or is constructed by the author of the script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request low priority
Projects
None yet
Development

No branches or pull requests

4 participants