From 5504768a17ae3d07685e216253d230637629240b Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Tue, 20 Dec 2022 09:03:11 -0500 Subject: [PATCH 01/28] Added the CPS README with a possible solution --- CPS-????/README.md | 145 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 CPS-????/README.md diff --git a/CPS-????/README.md b/CPS-????/README.md new file mode 100644 index 000000000..b9c191f00 --- /dev/null +++ b/CPS-????/README.md @@ -0,0 +1,145 @@ +--- +CPS: ? +Title: Spending Scripts can't actualize the full potential of eUTxO +Status: Open +Category: Smart Contracts +Authors: + - fallen-icarus +Proposed Solutions: [] +Discussions: + - https://github.com/cardano-foundation/CIPs/issues/417 +Created: 2022-12-20 +--- + +## Abstract +Spendings scripts are currently executed once for every UTxO being consumed from a script address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. As of now, scripts are forced to be executed once per UTxO even if these extra executions are completely redundant. The eUTxO architecture allows a script to see the entire transaction information which means it is already possible to create scripts that validatre based on the transaction as a whole. The problem is that there is no way to use such a script without redundant executions. Not only does this waste computational resources, but it can also result in the transaction fee growing quadratically based on the number ot UTxOs being consumed from the relevant script address. + +## Problem +Consider the following transaction: +``` Bash +cardano-cli transaction build \ + --tx-in c1d01ea50fd233f9fbaef3a295ba607a72c736e58c9c9df588abf4e5009ad4fe#0 \ + --tx-in 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#2 \ + --spending-tx-in-reference 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#0 \ + --spending-plutus-script-v2 \ + --spending-reference-tx-in-inline-datum-present \ + --spending-reference-tx-in-redeemer-file $swapRedeemerFile \ + --tx-in 766555130db8ff7b50fc548cbff3caa0d0557ce5af804da3b993cd090f1a8c3a#1 \ + --spending-tx-in-reference 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#0 \ + --spending-plutus-script-v2 \ + --spending-reference-tx-in-inline-datum-present \ + --spending-reference-tx-in-redeemer-file $swapRedeemerFile \ + --tx-out "$(cat ../assets/wallets/01.addr) 2000000 lovelace + 300 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ + --tx-out "$(cat ${swapScriptAddrFile}) + 4000000 lovelace + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 250 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ + --tx-out-inline-datum-file $swapDatumFile \ + --tx-in-collateral bc54229f0755611ba14a2679774a7c7d394b0a476e59c609035e06244e1572bb#0 \ + --change-address $(cat ../assets/wallets/01.addr) \ + --protocol-params-file "${tmpDir}protocol.json" \ + --testnet-magic 1 \ + --out-file "${tmpDir}tx.body" +``` +The second and third inputs are from the same script address. This particular script is a basic swap script that checks if the value being withdrawn from the script address is less than or equal to the value being deposited to the script address times a predefined ratio (the conversion rate between the two assets). The script itself passes or fails based on the entire transaction. The fact that two UTxOs are being consumed instead of one does not change the validity of the transaction. + +The problem is that, with the current design, this script is forced to be executed once for every UTxO being consumed from the script address. And since the validity depends on the transaction as a whole, which hasn't changed between executions, these extra executions are completely redundant. + +Being that my script checks all the inputs and outputs in the transaction to determine validity, below is the transaction fee estimation based on the number of UTxOs: +``` Txt +tx fee = # ref script executions * ( 0.3 ADA + 0.02 ADA * ( # input utxos + # output utxos ) ) +``` +The transaction fee increases linearly for every utxo (inputs + outputs) in the transaction, then quadratically the reference script needs to be executed more than once. + +## Use Cases +Any time a script's result depends on the entire transaction. The use cases I have personally encountered are: +1. Atomic Swaps +2. A multisig address where the owners want to withdraw more than one UTxO at a time + +## Goals +1. Stop redundantly executing scripts. +2. Do not remove the option to allow script execution per UTxO. There could be cases where this option is more efficient than having to traverse the transaction information to get the relevant datum. +3. Try to use the ledger as is. The ways scripts are stored onchain should not change unless absolutely necesary. +4. Try to leave the developer experience as untouched as possible. We don't want to make developers re-learn things. +5. Allow backwards compatibility of scripts without opening up security holes. + +## Open Questions +1. Would this require a hardfork? +2. Would this require a new version of plutus? + +--- +## Possible High-Level Solution +I am ignorant about certain lower-level implementation details but to the best of my knowledge, the following idea would not require a hardfork but would require a new version of plutus. + +Scripts whos validation depends on the transaction as a whole will be referred to as tx-level scripts while scripts whos validation depends on an individual utxo will be referred to as utxo-level scripts. + +I believe it is worth noting that minting scripts already function with the desired behavior. Consider this transaction: +``` Bash +cardano-cli transaction build \ + --tx-in fadae52f0323d7178c8116aa6adce31aba3ad6c561cbe5b31009251f742aa1bb#1 \ + --tx-out "$(cat ../../assets/wallets/01.addr) 2000000 lovelace + 1000 ${alwaysSucceedSymbol}.${tokenName}" \ + --tx-out "$(cat ../../assets/wallets/02.addr) 2000000 lovelace + 50 ${alwaysSucceedSymbol}.${tokenName}" \ + --mint "1050 ${alwaysSucceedSymbol}.${tokenName}" \ + --mint-script-file alwaysSucceedsMintingPolicy.plutus \ + --mint-redeemer-file unit.json \ + --tx-in-collateral af3b8901a464f53cb69e6e240a506947154b1fedbe89ab7ff9263ed2263f5cf5#0 \ + --change-address $(cat ../../assets/wallets/01.addr) \ + --protocol-params-file "${tmpDir}protocol.json" \ + --testnet-magic 1 \ + --out-file "${tmpDir}tx.body" +``` +Here the minting script is only executed once despite minting occurring in two different outputs. At a high level, minting scripts are written like this: +``` Haskell +-- | minting policy before being compiled to plutus +mkMintPolicy :: Redeemer -> ScriptContext -> Bool +mkMintPolicy r ctx = ... +``` +while spending scripts are written like this: +``` Haskell +-- | validator function before being compiled to plutus +mkValidator :: Datum -> Redeemer -> ScriptContext -> Bool +mkValidator d r ctx = ... +``` +The fact that the spending script handles a datum while the minting script does not should immediately jump out. Taking inspiration from the fact that minting policies do not use datums (while also recognizing that spending scripts do), I propose the following high level change to the way spending scripts are written: +``` Haskell +-- | new validator function before being compiled to plutus +mkValidator :: Maybe Datum -> Redeemer -> ScriptContext -> Bool +mkValidator Nothing r ctx = {- the case where the script can be used at a transaction level -} +mkValidator (Just d) r ctx = {- the case where the script can be used at the utxo level -} +``` +Then the basic cardano-cli usage would be (re-using my swap transaction example): +``` Bash +cardano-cli transaction build \ + --tx-in c1d01ea50fd233f9fbaef3a295ba607a72c736e58c9c9df588abf4e5009ad4fe#0 \ + --tx-in 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#2 \ + --spending-plutus-script-v3 \ + --spending-reference-tx-in-inline-datum-present \ + --tx-in 766555130db8ff7b50fc548cbff3caa0d0557ce5af804da3b993cd090f1a8c3a#1 \ + --spending-plutus-script-v3 \ + --spending-reference-tx-in-inline-datum-present \ + --tx-out "$(cat ../assets/wallets/01.addr) 2000000 lovelace + 300 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ + --tx-out "$(cat ${swapScriptAddrFile}) + 4000000 lovelace + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 250 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ + --tx-out-inline-datum-file $swapDatumFile \ + --tx-in-collateral bc54229f0755611ba14a2679774a7c7d394b0a476e59c609035e06244e1572bb#0 \ + --change-address $(cat ../assets/wallets/01.addr) \ + --tx-level-spending-tx-in-reference 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#0 \ + --tx-level-spending-reference-tx-in-redeemer-file $swapRedeemerFile \ + --protocol-params-file "${tmpDir}protocol.json" \ + --testnet-magic 1 \ + --out-file "${tmpDir}tx.body" +``` +I removed the `spending-tx-in-reference` lines and added `tx-level-spending-tx-in-reference` and `tx-level-spending-reference-tx-in-redeemer-file` after `change-address`. If validation needs to be done at the utxo-level, the original `spending-tx-in-reference` lines can still be used. + +When the spending script is used with the `tx-level-reference-script` option, `Nothing` is passed in for the `Maybe Datum`. On the other hand, when `spending-tx-in-reference` is used like usual, the datum will be parsed and passed with the `Just`. This way the code explicitly handles both the tx-level case and the utxo-level case. + +The above cardano-cli example also uses a difference version of plutus. This seems required since previous versions do not use a `Maybe Datum`. + +Using this technique, it would also be theoretically possible to spend a utxo at a script address even if it doesn't have a datum attached. This assumes the script can be used at the transaction level. + +### What if a malicious entity forcibly passes in the wrong version of the datum (`Nothing` when it should be `Just d` or vice versa)? + The script logic can be written to account for this so I argue it is up to the developer to defend against this kind of attack. A simple error message when the wrong version is used could suffice for most use cases. For my use case, the code would be: +``` Haskell +mkValidator :: Maybe Datum -> Redeemer -> ScriptContext -> Bool +mkValidator (Just _) _ _ = traceError "Not meant to be used at the utxo level" -- ^ I don't want the script used in this case +mkValidator Nothing r ctx = {- do what I want -} +``` + +### Can multiple transaction level scripts be used in one transaction? +I don't see why not. The node is capable of detecting if all relevant scripts are present in the transaction. The transaction would only be valid if all necessary scripts succeed. \ No newline at end of file From 803e0bf60d403c25e330938edb9ea9c790442e57 Mon Sep 17 00:00:00 2001 From: fallen-icarus <108348082+fallen-icarus@users.noreply.github.com> Date: Tue, 20 Dec 2022 09:11:11 -0500 Subject: [PATCH 02/28] Capitalized parts of Title in yaml --- CPS-????/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index b9c191f00..d1b3a1b50 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -1,6 +1,6 @@ --- CPS: ? -Title: Spending Scripts can't actualize the full potential of eUTxO +Title: Spending Scripts Can't Actualize the Full Potential of eUTxO Status: Open Category: Smart Contracts Authors: @@ -142,4 +142,4 @@ mkValidator Nothing r ctx = {- do what I want -} ``` ### Can multiple transaction level scripts be used in one transaction? -I don't see why not. The node is capable of detecting if all relevant scripts are present in the transaction. The transaction would only be valid if all necessary scripts succeed. \ No newline at end of file +I don't see why not. The node is capable of detecting if all relevant scripts are present in the transaction. The transaction would only be valid if all necessary scripts succeed. From 3a04f25ed3ce6a4792a40873a33fb64bcc37747c Mon Sep 17 00:00:00 2001 From: fallen-icarus <108348082+fallen-icarus@users.noreply.github.com> Date: Tue, 20 Dec 2022 09:13:20 -0500 Subject: [PATCH 03/28] grammar --- CPS-????/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index d1b3a1b50..0b9e28437 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -46,7 +46,7 @@ Being that my script checks all the inputs and outputs in the transaction to det ``` Txt tx fee = # ref script executions * ( 0.3 ADA + 0.02 ADA * ( # input utxos + # output utxos ) ) ``` -The transaction fee increases linearly for every utxo (inputs + outputs) in the transaction, then quadratically the reference script needs to be executed more than once. +The transaction fee increases linearly for every utxo (inputs + outputs) in the transaction, then quadratically if the reference script needs to be executed more than once. ## Use Cases Any time a script's result depends on the entire transaction. The use cases I have personally encountered are: From f4a519f3e856dddf905f7b683deeb1c9b5712b1e Mon Sep 17 00:00:00 2001 From: fallen-icarus <108348082+fallen-icarus@users.noreply.github.com> Date: Tue, 20 Dec 2022 09:20:30 -0500 Subject: [PATCH 04/28] Changed the category to plutus --- CPS-????/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 0b9e28437..79cba9fea 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -2,7 +2,7 @@ CPS: ? Title: Spending Scripts Can't Actualize the Full Potential of eUTxO Status: Open -Category: Smart Contracts +Category: Plutus Authors: - fallen-icarus Proposed Solutions: [] From d63285079637f50a91c52e537540911a106c71e4 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Sat, 7 Jan 2023 11:56:44 -0500 Subject: [PATCH 05/28] Updated the readme --- CPS-????/README.md | 153 ++++++++++++++++++++++++++++++--------------- 1 file changed, 102 insertions(+), 51 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 79cba9fea..736121955 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -1,6 +1,6 @@ --- CPS: ? -Title: Spending Scripts Can't Actualize the Full Potential of eUTxO +Title: Spending Script Redundant Executions Status: Open Category: Plutus Authors: @@ -12,7 +12,7 @@ Created: 2022-12-20 --- ## Abstract -Spendings scripts are currently executed once for every UTxO being consumed from a script address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. As of now, scripts are forced to be executed once per UTxO even if these extra executions are completely redundant. The eUTxO architecture allows a script to see the entire transaction information which means it is already possible to create scripts that validatre based on the transaction as a whole. The problem is that there is no way to use such a script without redundant executions. Not only does this waste computational resources, but it can also result in the transaction fee growing quadratically based on the number ot UTxOs being consumed from the relevant script address. +Spendings scripts are currently executed once for every UTxO being consumed from a script address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. The eUTxO architecture allows a spending script to see the entire transaction context which means it is already possible to create scripts that validate based on the transaction as a whole. As of now, spending scripts that do this are forced to be executed once per UTxO even if these extra executions are completely redundant. Not only does this waste computational resources, but it can also result in the transaction fee growing quickly based on the number ot UTxOs being consumed from the relevant script address. There is a trick that can be used to minimize this limitation but it has its own drawbacks. At the end of this CPS is a proposed solution that not only addresses these redundant executions but would also make it possible to spend script utxos that have missing/wrong datums. ## Problem Consider the following transaction: @@ -38,27 +38,79 @@ cardano-cli transaction build \ --testnet-magic 1 \ --out-file "${tmpDir}tx.body" ``` -The second and third inputs are from the same script address. This particular script is a basic swap script that checks if the value being withdrawn from the script address is less than or equal to the value being deposited to the script address times a predefined ratio (the conversion rate between the two assets). The script itself passes or fails based on the entire transaction. The fact that two UTxOs are being consumed instead of one does not change the validity of the transaction. -The problem is that, with the current design, this script is forced to be executed once for every UTxO being consumed from the script address. And since the validity depends on the transaction as a whole, which hasn't changed between executions, these extra executions are completely redundant. +The second and third inputs are from the same script address. This particular script is a basic atomic swap script that checks if the value being withdrawn from the script address is less than or equal to the value being deposited to the script address multiplied by a predefined ratio (the conversion rate between the two assets). The script itself passes or fails based on the entire transaction. The fact that two UTxOs are being consumed instead of one does not change the validity of the transaction. + +The problem is that, with the current design, this script is forced to be executed once for every UTxO consumed from the corresponding script address. And since the validity depends on the transaction as a whole, which hasn't changed between executions, these extra executions are completely redundant. + +Being that the atomic swap script checks all the inputs and outputs in the transaction to determine validity, below is the transaction fee estimation with the current limitation: -Being that my script checks all the inputs and outputs in the transaction to determine validity, below is the transaction fee estimation based on the number of UTxOs: ``` Txt -tx fee = # ref script executions * ( 0.3 ADA + 0.02 ADA * ( # input utxos + # output utxos ) ) +tx fee = 0.3 ADA * #swap_ref_script_executions + 0.02 ADA * ( #input_utxos + #output_utxos) ``` -The transaction fee increases linearly for every utxo (inputs + outputs) in the transaction, then quadratically if the reference script needs to be executed more than once. + +As the equation shows, when the swap script must be exected once per UTxO, the fee can grow quite large very quickly. Meanwhile, if the redundant exectutions were not there, the fee would grow very slowly. + +## The Trick To Minimize This Limitation +The trick involves taking advantage of the fact that staking scripts do not suffer from the same limitation as spending scripts. + +> Given a [spending] script **s**, you can turn it into a [spending] script **s'** that checks that 0 ADA is withdrawn from a [staking] script **s''** with the same logic as **s**. This is a transformation that is valid for any [spending] script. -- @L-as + + +In a nut shell, you use a staking script **AS** a spending script. Here is how the above transaction example would look with this trick: + +``` Bash +cardano-cli transaction build \ + --tx-in c1d01ea50fd233f9fbaef3a295ba607a72c736e58c9c9df588abf4e5009ad4fe#0 \ + --tx-in 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#2 \ + --spending-tx-in-reference 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#0 \ + --spending-plutus-script-v2 \ + --spending-reference-tx-in-inline-datum-present \ + --spending-reference-tx-in-redeemer-file $swapRedeemerFile \ + --tx-in 766555130db8ff7b50fc548cbff3caa0d0557ce5af804da3b993cd090f1a8c3a#1 \ + --spending-tx-in-reference 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#0 \ + --spending-plutus-script-v2 \ + --spending-reference-tx-in-inline-datum-present \ + --spending-reference-tx-in-redeemer-file $swapRedeemerFile \ + --tx-out "$(cat ../assets/wallets/01.addr) 2000000 lovelace + 300 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ + --tx-out "$(cat ${swapScriptAddrFile}) + 4000000 lovelace + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 250 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ + --tx-out-inline-datum-file $swapDatumFile \ + --withdrawal "$(cat ${stakingScriptAddrFile})+0" \ + --withdrawal-script-file $stakingScriptFile \ + --withdrawal-redeemer-file $swapRedeemerFile \ + --tx-in-collateral bc54229f0755611ba14a2679774a7c7d394b0a476e59c609035e06244e1572bb#0 \ + --change-address $(cat ../assets/wallets/01.addr) \ + --protocol-params-file "${tmpDir}protocol.json" \ + --testnet-magic 1 \ + --out-file "${tmpDir}tx.body" +``` + +There are three new `--withdrawal` lines. The logic for the atomic swap is in the staking script. The actual spending script just checks that the required staking script was executed AND 0 ADA was withdrawn from the reward address. + +The only requirement to use this trick is that the swap address was created using the `s'` spending script and the `s''` staking script. The staking address DOES NOT need to actually have rewards or even be delegated for this trick to work. + +With this method, the main computation is only executed once. + +### The Drawbacks + +1. This trick now means that everything requires two plutus scripts instead of just one. While the spending script can be made extremely small, this doesn't change the fact that two are now needed. +2. This trick DOES NOT stop the redundant executions; the spending script will still redundantly check if the required staking script was executed. This trick just minimizes the cost of the redundant executions by moving the heavy computation into a script that is only executed once per transaction. +3. This is not the intended use for staking scripts. ## Use Cases -Any time a script's result depends on the entire transaction. The use cases I have personally encountered are: -1. Atomic Swaps -2. A multisig address where the owners want to withdraw more than one UTxO at a time +Any time a spending script's result depends on the entire transaction and not on any particular UTxO. + +1. P2P Atomic Swaps +2. P2P Lending +3. Certain DAO applications +4. Certain Multisig applications that require more complex logic than what is possible with native scripts ## Goals -1. Stop redundantly executing scripts. -2. Do not remove the option to allow script execution per UTxO. There could be cases where this option is more efficient than having to traverse the transaction information to get the relevant datum. -3. Try to use the ledger as is. The ways scripts are stored onchain should not change unless absolutely necesary. -4. Try to leave the developer experience as untouched as possible. We don't want to make developers re-learn things. -5. Allow backwards compatibility of scripts without opening up security holes. +1. Stop redundantly executing spending scripts. +2. Do not remove the option to allow script execution per UTxO. There are still cases where the script should validate based off individual UTxOs. Also, there could be cases where this option is more efficient than having to traverse the transaction's context to get the relevant datum. An example of this latter case is when only one UTxO is consumed from a script address. +3. Try to use the ledger as is. The way scripts are stored onchain should not change unless absolutely necessary. +4. Try to leave the developer experience as untouched as possible. We don't want to make developers re-learn plutus. +5. Do not add too much new overhead to already resource constrained plutus scripts. ## Open Questions 1. Would this require a hardfork? @@ -66,45 +118,39 @@ Any time a script's result depends on the entire transaction. The use cases I ha --- ## Possible High-Level Solution -I am ignorant about certain lower-level implementation details but to the best of my knowledge, the following idea would not require a hardfork but would require a new version of plutus. +What follows is my high-level suggestion for how to solve this issue. As a bonus based on the way my solution would work, it would also become possible to spend script UTxOs that having missing/incorrect datums. To the best of my knowledge, this solution would not require a hardfork but would require a new version of plutus. -Scripts whos validation depends on the transaction as a whole will be referred to as tx-level scripts while scripts whos validation depends on an individual utxo will be referred to as utxo-level scripts. +*Scripts whose validation depends on the transaction as a whole will be referred to as **tx-level scripts** while scripts whose validation depends on an individual utxo will be referred to as **utxo-level scripts**.* + +In addition to staking scripts, minting scripts also already execute based off the transaction as a whole. At a high level, minting, staking, and spending plutus scripts are written like this: -I believe it is worth noting that minting scripts already function with the desired behavior. Consider this transaction: -``` Bash -cardano-cli transaction build \ - --tx-in fadae52f0323d7178c8116aa6adce31aba3ad6c561cbe5b31009251f742aa1bb#1 \ - --tx-out "$(cat ../../assets/wallets/01.addr) 2000000 lovelace + 1000 ${alwaysSucceedSymbol}.${tokenName}" \ - --tx-out "$(cat ../../assets/wallets/02.addr) 2000000 lovelace + 50 ${alwaysSucceedSymbol}.${tokenName}" \ - --mint "1050 ${alwaysSucceedSymbol}.${tokenName}" \ - --mint-script-file alwaysSucceedsMintingPolicy.plutus \ - --mint-redeemer-file unit.json \ - --tx-in-collateral af3b8901a464f53cb69e6e240a506947154b1fedbe89ab7ff9263ed2263f5cf5#0 \ - --change-address $(cat ../../assets/wallets/01.addr) \ - --protocol-params-file "${tmpDir}protocol.json" \ - --testnet-magic 1 \ - --out-file "${tmpDir}tx.body" -``` -Here the minting script is only executed once despite minting occurring in two different outputs. At a high level, minting scripts are written like this: ``` Haskell -- | minting policy before being compiled to plutus -mkMintPolicy :: Redeemer -> ScriptContext -> Bool -mkMintPolicy r ctx = ... -``` -while spending scripts are written like this: -``` Haskell --- | validator function before being compiled to plutus -mkValidator :: Datum -> Redeemer -> ScriptContext -> Bool -mkValidator d r ctx = ... +mkMintingScript :: Redeemer -> ScriptContext -> Bool +mkMintingScript r ctx = ... + +-- | Staking script before being compiled to plutus +mkStakingScript :: Redeemer -> ScriptContext -> Bool +mkStakingScript r ctx = ... + +-- | Spending script before being compiled to plutus +mkSpendingScript :: Datum -> Redeemer -> ScriptContext -> Bool +mkSpendingScript d r ctx = ... ``` -The fact that the spending script handles a datum while the minting script does not should immediately jump out. Taking inspiration from the fact that minting policies do not use datums (while also recognizing that spending scripts do), I propose the following high level change to the way spending scripts are written: + +### Part 1 +The first part of the solution involves changing the way spending scripts are written to this: + ``` Haskell --- | new validator function before being compiled to plutus +-- | New spending script before being compiled to plutus mkValidator :: Maybe Datum -> Redeemer -> ScriptContext -> Bool mkValidator Nothing r ctx = {- the case where the script can be used at a transaction level -} mkValidator (Just d) r ctx = {- the case where the script can be used at the utxo level -} ``` -Then the basic cardano-cli usage would be (re-using my swap transaction example): + +### Part 2 +The second part involves **EXPLICITLY** telling the node which version to use. Here would be my atomic swap transaction using the transaction level version: + ``` Bash cardano-cli transaction build \ --tx-in c1d01ea50fd233f9fbaef3a295ba607a72c736e58c9c9df588abf4e5009ad4fe#0 \ @@ -125,21 +171,26 @@ cardano-cli transaction build \ --testnet-magic 1 \ --out-file "${tmpDir}tx.body" ``` -I removed the `spending-tx-in-reference` lines and added `tx-level-spending-tx-in-reference` and `tx-level-spending-reference-tx-in-redeemer-file` after `change-address`. If validation needs to be done at the utxo-level, the original `spending-tx-in-reference` lines can still be used. -When the spending script is used with the `tx-level-reference-script` option, `Nothing` is passed in for the `Maybe Datum`. On the other hand, when `spending-tx-in-reference` is used like usual, the datum will be parsed and passed with the `Just`. This way the code explicitly handles both the tx-level case and the utxo-level case. +I removed the `spending-tx-in-reference` and the `spending-reference-tx-in-redeemer-file` lines and added `tx-level-spending-tx-in-reference` and `tx-level-spending-reference-tx-in-redeemer-file` after `change-address`. -The above cardano-cli example also uses a difference version of plutus. This seems required since previous versions do not use a `Maybe Datum`. +Right after the actual script `tx-in`s are `--spending-plutus-script-v3` and `--spending-reference-tx-in-inline-datum-present`. Keeping the datum flags here allows for "mixing and matching" inline datums and datum hashes. On the other hand, it is okay to move the redeemer flag to the end since plutus would still assume all UTxOs from a script address to be using the same redeemer. -Using this technique, it would also be theoretically possible to spend a utxo at a script address even if it doesn't have a datum attached. This assumes the script can be used at the transaction level. +If validation needs to be done at the utxo-level, the original transaction can still be used (possibly with the `utxo-level` prefix attached) but the `spending-plutus-script-v2` flag would be changed to `spending-plutus-script-v3`. This seems required since previous plutus scripts do not use a `Maybe Datum`. + +When the spending script is used with the `tx-level-reference-script` option, `Nothing` is passed in for the `Maybe Datum`. On the other hand, when `spending-tx-in-reference` is used like usual, the datum will be parsed and passed with the `Just`. This way the code explicitly handles both the tx-level case and the utxo-level case. Using this technique, there would be no need for an on-chain encoding of a missing datum. Theoretically, all UTxOs could now be consumed as long as the tx-level script logic is used. This includes UTxOs that have incorrect/missing datums. + +### How will the node know if all required scripts are present? +The node is already capable of detecting whether all required witnesses are present. ### What if a malicious entity forcibly passes in the wrong version of the datum (`Nothing` when it should be `Just d` or vice versa)? - The script logic can be written to account for this so I argue it is up to the developer to defend against this kind of attack. A simple error message when the wrong version is used could suffice for most use cases. For my use case, the code would be: +The script logic can be written to account for this so I argue it is up to the developer to defend against this kind of attack. A simple error message when the wrong version is used could suffice for most use cases. For my use case, the code could be: + ``` Haskell mkValidator :: Maybe Datum -> Redeemer -> ScriptContext -> Bool -mkValidator (Just _) _ _ = traceError "Not meant to be used at the utxo level" -- ^ I don't want the script used in this case +mkValidator (Just _) _ _ = traceError "Not meant to be used at the utxo level" mkValidator Nothing r ctx = {- do what I want -} ``` ### Can multiple transaction level scripts be used in one transaction? -I don't see why not. The node is capable of detecting if all relevant scripts are present in the transaction. The transaction would only be valid if all necessary scripts succeed. +I don't see why not. The node is capable of detecting if all relevant scripts are present in the transaction. The transaction would only be valid if all necessary scripts succeed. \ No newline at end of file From 7816bf8c8f53ec37c0ecfb6f81509f73553559d6 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Sat, 7 Jan 2023 12:13:47 -0500 Subject: [PATCH 06/28] Added suggestion about possible low level representation for Maybe Datum --- CPS-????/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 736121955..d03d61e0e 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -193,4 +193,14 @@ mkValidator Nothing r ctx = {- do what I want -} ``` ### Can multiple transaction level scripts be used in one transaction? -I don't see why not. The node is capable of detecting if all relevant scripts are present in the transaction. The transaction would only be valid if all necessary scripts succeed. \ No newline at end of file +I don't see why not. The node is capable of detecting if all relevant scripts are present in the transaction. The transaction would only be valid if all necessary scripts succeed. + +### How would the `Maybe Datum` be represented at a low-level? + +Perhap it can be represented with a constructor value of (-1) like: + +``` JSON +{"constructor":-1,"fields":[]} +``` + +`unStableMakeIsData` uses positive constructor numbers and `makeIsDataStable` can enforce the use of positive constructor numbers. This way the (-1) constructor is reserved for this purpose. \ No newline at end of file From 823fb16575ab8ccc280f1ba8ee9d60a19bf77ea3 Mon Sep 17 00:00:00 2001 From: fallen-icarus <108348082+fallen-icarus@users.noreply.github.com> Date: Sat, 7 Jan 2023 12:39:36 -0500 Subject: [PATCH 07/28] Added more to low level suggestion --- CPS-????/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index d03d61e0e..27d24ff3b 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -196,6 +196,7 @@ mkValidator Nothing r ctx = {- do what I want -} I don't see why not. The node is capable of detecting if all relevant scripts are present in the transaction. The transaction would only be valid if all necessary scripts succeed. ### How would the `Maybe Datum` be represented at a low-level? +(I do not know much about the low-level design of plutus so I could be way off with this part.) Perhap it can be represented with a constructor value of (-1) like: @@ -203,4 +204,4 @@ Perhap it can be represented with a constructor value of (-1) like: {"constructor":-1,"fields":[]} ``` -`unStableMakeIsData` uses positive constructor numbers and `makeIsDataStable` can enforce the use of positive constructor numbers. This way the (-1) constructor is reserved for this purpose. \ No newline at end of file +`unStableMakeIsData` uses positive constructor numbers and `makeIsDataStable` can enforce the use of positive constructor numbers. This way the (-1) constructor is reserved for this purpose. The idea would be to have something that is guaranteed to fail on-chain whenever it is used just like the `undefined` in Haskell. While technically this would mean it is still possible to lock a UTxO at a script address forever, a user would have to go out of his/her way to make this happen. The default behavior would be the proper usage. From c8d916d6b78e782ccc747bdbdbf55790d0d29375 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Sun, 8 Jan 2023 09:34:20 -0500 Subject: [PATCH 08/28] Edited `Maybe Datum` low level representation section. --- CPS-????/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 27d24ff3b..07c93c3fa 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -198,10 +198,10 @@ I don't see why not. The node is capable of detecting if all relevant scripts ar ### How would the `Maybe Datum` be represented at a low-level? (I do not know much about the low-level design of plutus so I could be way off with this part.) -Perhap it can be represented with a constructor value of (-1) like: +I do not know if it is possible but perhaps the `Nothing` can be represented with an invalid de bruijn constructor value of (-1) like: ``` JSON {"constructor":-1,"fields":[]} ``` -`unStableMakeIsData` uses positive constructor numbers and `makeIsDataStable` can enforce the use of positive constructor numbers. This way the (-1) constructor is reserved for this purpose. The idea would be to have something that is guaranteed to fail on-chain whenever it is used just like the `undefined` in Haskell. While technically this would mean it is still possible to lock a UTxO at a script address forever, a user would have to go out of his/her way to make this happen. The default behavior would be the proper usage. +All de bruijn indexes are supposed to be natural numbers so the (-1) is guaranteed to not be used for real datums. The idea is similar to using `undefined` in Haskell where it points to a thunk that is guaranteed to raise an exception. From 7b142f85648ac5e5167fe527689a1a097e0129bb Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Sun, 8 Jan 2023 09:41:28 -0500 Subject: [PATCH 09/28] Added comment about hardcoding into plutus. --- CPS-????/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 07c93c3fa..da849af92 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -204,4 +204,4 @@ I do not know if it is possible but perhaps the `Nothing` can be represented wit {"constructor":-1,"fields":[]} ``` -All de bruijn indexes are supposed to be natural numbers so the (-1) is guaranteed to not be used for real datums. The idea is similar to using `undefined` in Haskell where it points to a thunk that is guaranteed to raise an exception. +All de bruijn indexes are supposed to be natural numbers so the (-1) is guaranteed to not be used for real datums. The idea is similar to using `undefined` in Haskell where it points to a thunk that is guaranteed to raise an exception. This invalid representation for `BuiltinData` would need to be hard-coded into plutus. From e4010dbe3e8320f1f58de1bcd1cb34bc6f3aa33e Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Sun, 8 Jan 2023 11:37:32 -0500 Subject: [PATCH 10/28] Added haddock documentation supporting possible use for negative de bruijn index. --- CPS-????/README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index da849af92..e4d8813bb 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -198,10 +198,18 @@ I don't see why not. The node is capable of detecting if all relevant scripts ar ### How would the `Maybe Datum` be represented at a low-level? (I do not know much about the low-level design of plutus so I could be way off with this part.) -I do not know if it is possible but perhaps the `Nothing` can be represented with an invalid de bruijn constructor value of (-1) like: +Perhaps the `Nothing` can be represented with an invalid de bruijn constructor value of (-1) like: ``` JSON {"constructor":-1,"fields":[]} ``` -All de bruijn indexes are supposed to be natural numbers so the (-1) is guaranteed to not be used for real datums. The idea is similar to using `undefined` in Haskell where it points to a thunk that is guaranteed to raise an exception. This invalid representation for `BuiltinData` would need to be hard-coded into plutus. +All de bruijn indexes are supposed to be natural numbers so the (-1) is guaranteed to not be used for real datums. The idea is similar to using `undefined` in Haskell where it points to a thunk that is guaranteed to raise an exception when used outside of just being a place holder. This invalid representation for `BuiltinData` would need to be hard-coded into plutus. + +According to the haddock documentation for `BuiltinData`, the constructor seems to be of the type `BuiltinInteger` which is just a type synonym for `Integer`. `mkConstr` has the type signature + +``` Haskell +mkConstr :: BuiltinInteger -> BuiltinList BuiltinData -> BuiltinData +``` + +Therefore it seems like this negative constructor can be used without changing the underlying representation for `BuiltinData`. Again this negative constructor is not meant to be used as an actual `Datum` so any attempt to parse the this `BuiltinData` representation for `Nothing` should fail. \ No newline at end of file From acd316273a87140f25b3c8d4dd322e26565e7c25 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Sun, 8 Jan 2023 11:44:53 -0500 Subject: [PATCH 11/28] Added more comments. --- CPS-????/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index e4d8813bb..b042d6739 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -204,12 +204,12 @@ Perhaps the `Nothing` can be represented with an invalid de bruijn constructor v {"constructor":-1,"fields":[]} ``` -All de bruijn indexes are supposed to be natural numbers so the (-1) is guaranteed to not be used for real datums. The idea is similar to using `undefined` in Haskell where it points to a thunk that is guaranteed to raise an exception when used outside of just being a place holder. This invalid representation for `BuiltinData` would need to be hard-coded into plutus. +All de bruijn indexes are supposed to be natural numbers so the (-1) is guaranteed to not be used for real datums. The idea is similar to using `undefined` in Haskell where it points to a thunk that is guaranteed to raise an exception when used outside of just being a place holder for undefined functions. This invalid representation for `BuiltinData` would need to be hard-coded into plutus. -According to the haddock documentation for `BuiltinData`, the constructor seems to be of the type `BuiltinInteger` which is just a type synonym for `Integer`. `mkConstr` has the type signature +According to the haddock documentation for `BuiltinData`, the constructor seems to be of the type `BuiltinInteger` which is just a type synonym for `Integer`. Futher, `mkConstr` has the type signature ``` Haskell mkConstr :: BuiltinInteger -> BuiltinList BuiltinData -> BuiltinData ``` -Therefore it seems like this negative constructor can be used without changing the underlying representation for `BuiltinData`. Again this negative constructor is not meant to be used as an actual `Datum` so any attempt to parse the this `BuiltinData` representation for `Nothing` should fail. \ No newline at end of file +Therefore it seems like this negative constructor can be used without changing the underlying representation for `BuiltinData`. Again this negative constructor is not meant to be used with an actual `Datum` so any attempt to parse the this `BuiltinData` representation for `Nothing` should fail. \ No newline at end of file From 9ffb99e6c0573bcdac691aed7cf6ad27e1c4bc84 Mon Sep 17 00:00:00 2001 From: fallen-icarus <108348082+fallen-icarus@users.noreply.github.com> Date: Sun, 8 Jan 2023 11:52:19 -0500 Subject: [PATCH 12/28] Spelling --- CPS-????/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index b042d6739..099c16a5c 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -206,10 +206,10 @@ Perhaps the `Nothing` can be represented with an invalid de bruijn constructor v All de bruijn indexes are supposed to be natural numbers so the (-1) is guaranteed to not be used for real datums. The idea is similar to using `undefined` in Haskell where it points to a thunk that is guaranteed to raise an exception when used outside of just being a place holder for undefined functions. This invalid representation for `BuiltinData` would need to be hard-coded into plutus. -According to the haddock documentation for `BuiltinData`, the constructor seems to be of the type `BuiltinInteger` which is just a type synonym for `Integer`. Futher, `mkConstr` has the type signature +According to the haddock documentation for `BuiltinData`, the constructor seems to be of the type `BuiltinInteger` which is just a type synonym for `Integer`. Further, `mkConstr` has the type signature ``` Haskell mkConstr :: BuiltinInteger -> BuiltinList BuiltinData -> BuiltinData ``` -Therefore it seems like this negative constructor can be used without changing the underlying representation for `BuiltinData`. Again this negative constructor is not meant to be used with an actual `Datum` so any attempt to parse the this `BuiltinData` representation for `Nothing` should fail. \ No newline at end of file +Therefore it seems like this negative constructor can be used without changing the underlying representation for `BuiltinData`. Again this negative constructor is not meant to be used with an actual `Datum` so any attempt to parse the this `BuiltinData` representation for `Nothing` should fail. From 57235ea3d9964a08b8b440724e31f036eb583766 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Mon, 9 Jan 2023 07:41:06 -0500 Subject: [PATCH 13/28] Replaced low-level spec with simpler one --- CPS-????/README.md | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 099c16a5c..c9d7b81c3 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -198,18 +198,37 @@ I don't see why not. The node is capable of detecting if all relevant scripts ar ### How would the `Maybe Datum` be represented at a low-level? (I do not know much about the low-level design of plutus so I could be way off with this part.) -Perhaps the `Nothing` can be represented with an invalid de bruijn constructor value of (-1) like: +The idea is to add an outer de bruijn layer around the `BuiltinData` before passing it to a script and then have the script immediately unwrap this extra layer. + +An example will help explain it. Imagine you have the low level encoding for the unit datum: ``` JSON -{"constructor":-1,"fields":[]} +{"constructor":0,"fields":[]} ``` -All de bruijn indexes are supposed to be natural numbers so the (-1) is guaranteed to not be used for real datums. The idea is similar to using `undefined` in Haskell where it points to a thunk that is guaranteed to raise an exception when used outside of just being a place holder for undefined functions. This invalid representation for `BuiltinData` would need to be hard-coded into plutus. +If you want to pass this into a script as `Just ()`, you would add an outer de bruijn level BEFORE passing it to the script like this: + +``` JSON +{"constructor":1,"fields":[{"constructor":0,"fields":[]}]} +``` -According to the haddock documentation for `BuiltinData`, the constructor seems to be of the type `BuiltinInteger` which is just a type synonym for `Integer`. Further, `mkConstr` has the type signature +The unit datum is now inside `"fields"`. The script will immediately remove this outer layer by pattern matching like: ``` Haskell -mkConstr :: BuiltinInteger -> BuiltinList BuiltinData -> BuiltinData +mkSpendingScript :: Maybe Datum -> Redeemer -> ScriptContext -> Bool +mkSpendingScript (Just ()) r ctx = ... ``` -Therefore it seems like this negative constructor can be used without changing the underlying representation for `BuiltinData`. Again this negative constructor is not meant to be used with an actual `Datum` so any attempt to parse the this `BuiltinData` representation for `Nothing` should fail. +If you would like to pass in `Nothing` instead, you would use: + +``` JSON +{"constructor":0,"fields":[]} +``` + +**Notice how it looks exactly like the unit datum encoding. However, since this is a DIFFERENT de bruijn level, they are functionally different.** + +This extra layer would only be used at script exection time. All on-chain datums will not have this extra layer. All of the datums inside `ScriptContext` would also be left untouched. + +**Because this uses a different de bruijn level, this method does not use a default datum. All datums are left available for developers.** + +The use of this extra layer should be abstracted away so that users and developers don't even know it is being used. \ No newline at end of file From 3f9cb9d1030c4c1923fc91a4eeaf1f9e89bfd86a Mon Sep 17 00:00:00 2001 From: fallen-icarus <108348082+fallen-icarus@users.noreply.github.com> Date: Mon, 9 Jan 2023 07:43:07 -0500 Subject: [PATCH 14/28] Added comment to abstract --- CPS-????/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index c9d7b81c3..9194aabb1 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -12,7 +12,7 @@ Created: 2022-12-20 --- ## Abstract -Spendings scripts are currently executed once for every UTxO being consumed from a script address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. The eUTxO architecture allows a spending script to see the entire transaction context which means it is already possible to create scripts that validate based on the transaction as a whole. As of now, spending scripts that do this are forced to be executed once per UTxO even if these extra executions are completely redundant. Not only does this waste computational resources, but it can also result in the transaction fee growing quickly based on the number ot UTxOs being consumed from the relevant script address. There is a trick that can be used to minimize this limitation but it has its own drawbacks. At the end of this CPS is a proposed solution that not only addresses these redundant executions but would also make it possible to spend script utxos that have missing/wrong datums. +Spendings scripts are currently executed once for every UTxO being consumed from a script address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. The eUTxO architecture allows a spending script to see the entire transaction context which means it is already possible to create scripts that validate based on the transaction as a whole. As of now, spending scripts that do this are forced to be executed once per UTxO even if these extra executions are completely redundant. Not only does this waste computational resources, but it can also result in the transaction fee growing quickly based on the number ot UTxOs being consumed from the relevant script address. There is a trick that can be used to minimize this limitation but it has its own drawbacks. At the end of this CPS is a proposed solution (with a possible low-level specification) that not only addresses these redundant executions but would also make it possible to spend script utxos that have missing/wrong datums. ## Problem Consider the following transaction: @@ -231,4 +231,4 @@ This extra layer would only be used at script exection time. All on-chain datums **Because this uses a different de bruijn level, this method does not use a default datum. All datums are left available for developers.** -The use of this extra layer should be abstracted away so that users and developers don't even know it is being used. \ No newline at end of file +The use of this extra layer should be abstracted away so that users and developers don't even know it is being used. From 59135d23a58405a106fc83a9ef1fd3df3b0fbad1 Mon Sep 17 00:00:00 2001 From: fallen-icarus <108348082+fallen-icarus@users.noreply.github.com> Date: Mon, 9 Jan 2023 07:47:14 -0500 Subject: [PATCH 15/28] Removed High-level adjective --- CPS-????/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 9194aabb1..e027ec5ca 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -117,8 +117,8 @@ Any time a spending script's result depends on the entire transaction and not on 2. Would this require a new version of plutus? --- -## Possible High-Level Solution -What follows is my high-level suggestion for how to solve this issue. As a bonus based on the way my solution would work, it would also become possible to spend script UTxOs that having missing/incorrect datums. To the best of my knowledge, this solution would not require a hardfork but would require a new version of plutus. +## Possible Solution +What follows is my suggestion for how to solve this issue. As a bonus based on the way my solution would work, it would also become possible to spend script UTxOs that having missing/incorrect datums. To the best of my knowledge, this solution would not require a hardfork but would require a new version of plutus. *Scripts whose validation depends on the transaction as a whole will be referred to as **tx-level scripts** while scripts whose validation depends on an individual utxo will be referred to as **utxo-level scripts**.* From 9939892e290342539a859239e7e0f8939b63d8b1 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Mon, 9 Jan 2023 12:56:36 -0500 Subject: [PATCH 16/28] Added specification in terms of Data too --- CPS-????/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CPS-????/README.md b/CPS-????/README.md index e027ec5ca..2425ecb87 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -232,3 +232,11 @@ This extra layer would only be used at script exection time. All on-chain datums **Because this uses a different de bruijn level, this method does not use a default datum. All datums are left available for developers.** The use of this extra layer should be abstracted away so that users and developers don't even know it is being used. + +Some comments mention the specification being in terms of `Data` so here they are: +``` Haskell +Nothing = Constr 0 [] +Just datum = Constr 1 [dataOfDatum] +``` + +Again, since this is a different de bruijn level, this is not the same thing as using a default datum value. \ No newline at end of file From 0fd5b1ae8a560a6705911317aeba2ec97cbef7fb Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Tue, 17 Jan 2023 13:14:11 -0500 Subject: [PATCH 17/28] Removed proposed solution and cut down on extraneous info in examples. --- CPS-????/README.md | 236 ++++++++++----------------------------------- 1 file changed, 51 insertions(+), 185 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 2425ecb87..e21f15a78 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -12,86 +12,77 @@ Created: 2022-12-20 --- ## Abstract -Spendings scripts are currently executed once for every UTxO being consumed from a script address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. The eUTxO architecture allows a spending script to see the entire transaction context which means it is already possible to create scripts that validate based on the transaction as a whole. As of now, spending scripts that do this are forced to be executed once per UTxO even if these extra executions are completely redundant. Not only does this waste computational resources, but it can also result in the transaction fee growing quickly based on the number ot UTxOs being consumed from the relevant script address. There is a trick that can be used to minimize this limitation but it has its own drawbacks. At the end of this CPS is a proposed solution (with a possible low-level specification) that not only addresses these redundant executions but would also make it possible to spend script utxos that have missing/wrong datums. +Spendings scripts are currently executed once for every UTxO being consumed from a script address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. The eUTxO architecture allows a spending script to see the entire transaction context which means it is already possible to create scripts that validate based on the transaction as a whole. As of now, spending scripts that do this are forced to be executed once per UTxO even if these extra executions are completely redundant. Not only does this waste computational resources, but it can also result in the transaction fee growing quickly based on the number ot UTxOs being consumed from the relevant script address. There is a technique that can be used to minimize this limitation but it has its own drawbacks. ## Problem -Consider the following transaction: +Currently, all scripts require some variation of the following for every utxo being consumed from the script address: + +``` Bash + --tx-in \ + --spending-tx-in-reference \ + --spending-plutus-script-v2 \ + --spending-reference-tx-in-inline-datum-present \ + --spending-reference-tx-in-redeemer-file \ +``` +If three utxos were to be spent from this script address, then you would need three of these: + ``` Bash -cardano-cli transaction build \ - --tx-in c1d01ea50fd233f9fbaef3a295ba607a72c736e58c9c9df588abf4e5009ad4fe#0 \ - --tx-in 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#2 \ - --spending-tx-in-reference 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#0 \ + --tx-in \ + --spending-tx-in-reference \ + --spending-plutus-script-v2 \ + --spending-reference-tx-in-inline-datum-present \ + --spending-reference-tx-in-redeemer-file \ + --tx-in \ + --spending-tx-in-reference \ --spending-plutus-script-v2 \ --spending-reference-tx-in-inline-datum-present \ - --spending-reference-tx-in-redeemer-file $swapRedeemerFile \ - --tx-in 766555130db8ff7b50fc548cbff3caa0d0557ce5af804da3b993cd090f1a8c3a#1 \ - --spending-tx-in-reference 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#0 \ + --spending-reference-tx-in-redeemer-file \ + --tx-in \ + --spending-tx-in-reference \ --spending-plutus-script-v2 \ --spending-reference-tx-in-inline-datum-present \ - --spending-reference-tx-in-redeemer-file $swapRedeemerFile \ - --tx-out "$(cat ../assets/wallets/01.addr) 2000000 lovelace + 300 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ - --tx-out "$(cat ${swapScriptAddrFile}) + 4000000 lovelace + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 250 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ - --tx-out-inline-datum-file $swapDatumFile \ - --tx-in-collateral bc54229f0755611ba14a2679774a7c7d394b0a476e59c609035e06244e1572bb#0 \ - --change-address $(cat ../assets/wallets/01.addr) \ - --protocol-params-file "${tmpDir}protocol.json" \ - --testnet-magic 1 \ - --out-file "${tmpDir}tx.body" + --spending-reference-tx-in-redeemer-file \ ``` -The second and third inputs are from the same script address. This particular script is a basic atomic swap script that checks if the value being withdrawn from the script address is less than or equal to the value being deposited to the script address multiplied by a predefined ratio (the conversion rate between the two assets). The script itself passes or fails based on the entire transaction. The fact that two UTxOs are being consumed instead of one does not change the validity of the transaction. +What this translates to is that this script will be executed three times (once for each grouping). But what if the script validates based off of the transaction context as a whole and not any individual utxo? How can we use this "transaction level" script so that it is only executed once? There is no need to execute it more than once because the transaction context will be exactly the same for each execution. Another way to look at it is that we want a script to be able act like a payment skey where only one signature is needed no matter how many utxos are spent from the corresponding address. -The problem is that, with the current design, this script is forced to be executed once for every UTxO consumed from the corresponding script address. And since the validity depends on the transaction as a whole, which hasn't changed between executions, these extra executions are completely redundant. +This brings us to the problem: **The current design does not allow for a spending script to be executed only once no matter how many utxos are spent from the corresponding script address.** This is unfortunate since the eUTxO architecture allows writing such scripts. As a result, any time a developer writes a "transaction level" script, the script will be redundantly executed for each additional utxo being spent from the script's address. These redundant executions are a waste of scarce resources and result in much higher fees for end users. **The current design prevents developers from using the full potential of the eUTxO.** -Being that the atomic swap script checks all the inputs and outputs in the transaction to determine validity, below is the transaction fee estimation with the current limitation: - -``` Txt -tx fee = 0.3 ADA * #swap_ref_script_executions + 0.02 ADA * ( #input_utxos + #output_utxos) -``` - -As the equation shows, when the swap script must be exected once per UTxO, the fee can grow quite large very quickly. Meanwhile, if the redundant exectutions were not there, the fee would grow very slowly. - -## The Trick To Minimize This Limitation -The trick involves taking advantage of the fact that staking scripts do not suffer from the same limitation as spending scripts. +### The Current Alternative +The alternative involves taking advantage of the fact that staking scripts do not suffer from the same limitation as spending scripts. > Given a [spending] script **s**, you can turn it into a [spending] script **s'** that checks that 0 ADA is withdrawn from a [staking] script **s''** with the same logic as **s**. This is a transformation that is valid for any [spending] script. -- @L-as - -In a nut shell, you use a staking script **AS** a spending script. Here is how the above transaction example would look with this trick: +In a nut shell, you use a staking script **AS** a spending script. Here is how the above transaction example would look with this technique: ``` Bash -cardano-cli transaction build \ - --tx-in c1d01ea50fd233f9fbaef3a295ba607a72c736e58c9c9df588abf4e5009ad4fe#0 \ - --tx-in 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#2 \ - --spending-tx-in-reference 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#0 \ + --tx-in \ + --spending-tx-in-reference \ --spending-plutus-script-v2 \ --spending-reference-tx-in-inline-datum-present \ - --spending-reference-tx-in-redeemer-file $swapRedeemerFile \ - --tx-in 766555130db8ff7b50fc548cbff3caa0d0557ce5af804da3b993cd090f1a8c3a#1 \ - --spending-tx-in-reference 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#0 \ + --spending-reference-tx-in-redeemer-file \ + --tx-in \ + --spending-tx-in-reference \ --spending-plutus-script-v2 \ --spending-reference-tx-in-inline-datum-present \ - --spending-reference-tx-in-redeemer-file $swapRedeemerFile \ - --tx-out "$(cat ../assets/wallets/01.addr) 2000000 lovelace + 300 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ - --tx-out "$(cat ${swapScriptAddrFile}) + 4000000 lovelace + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 250 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ - --tx-out-inline-datum-file $swapDatumFile \ - --withdrawal "$(cat ${stakingScriptAddrFile})+0" \ - --withdrawal-script-file $stakingScriptFile \ - --withdrawal-redeemer-file $swapRedeemerFile \ - --tx-in-collateral bc54229f0755611ba14a2679774a7c7d394b0a476e59c609035e06244e1572bb#0 \ - --change-address $(cat ../assets/wallets/01.addr) \ - --protocol-params-file "${tmpDir}protocol.json" \ - --testnet-magic 1 \ - --out-file "${tmpDir}tx.body" + --spending-reference-tx-in-redeemer-file \ + --tx-in \ + --spending-tx-in-reference \ + --spending-plutus-script-v2 \ + --spending-reference-tx-in-inline-datum-present \ + --spending-reference-tx-in-redeemer-file \ + --withdrawal +0 \ + --withdrawal-script-file \ + --withdrawal-redeemer-file ``` -There are three new `--withdrawal` lines. The logic for the atomic swap is in the staking script. The actual spending script just checks that the required staking script was executed AND 0 ADA was withdrawn from the reward address. +There are three new `--withdrawal` lines at the end. The transaction level logic is in the staking script. Meanwhile the spending script just checks that the required staking script is executed AND 0 ADA is withdrawn from the rewards address in the transaction. -The only requirement to use this trick is that the swap address was created using the `s'` spending script and the `s''` staking script. The staking address DOES NOT need to actually have rewards or even be delegated for this trick to work. +The only requirement to use this trick is that the script address must have been created using the `s'` spending script and the `s''` staking script. The staking address DOES NOT need to actually have rewards or even be delegated for this trick to work. -With this method, the main computation is only executed once. +With this method, the main computation is only executed once no matter how many utxos are spent from the script address. -### The Drawbacks +#### The Drawbacks 1. This trick now means that everything requires two plutus scripts instead of just one. While the spending script can be made extremely small, this doesn't change the fact that two are now needed. 2. This trick DOES NOT stop the redundant executions; the spending script will still redundantly check if the required staking script was executed. This trick just minimizes the cost of the redundant executions by moving the heavy computation into a script that is only executed once per transaction. @@ -100,10 +91,10 @@ With this method, the main computation is only executed once. ## Use Cases Any time a spending script's result depends on the entire transaction and not on any particular UTxO. -1. P2P Atomic Swaps -2. P2P Lending -3. Certain DAO applications -4. Certain Multisig applications that require more complex logic than what is possible with native scripts +1. P2P Atomic Swaps - within one transaction, the value entering the script address must be some proportion of the value leaving the script address +2. P2P Lending - within one transaction, the amount borrowed must be some proportion of the amount posted as collateral. Likewise, the amount of collateral reclaimed depends on how much of the loan has been repaid. +3. DAOs +4. Multisig applications that require more complex logic than what is possible with native scripts ## Goals 1. Stop redundantly executing spending scripts. @@ -114,129 +105,4 @@ Any time a spending script's result depends on the entire transaction and not on ## Open Questions 1. Would this require a hardfork? -2. Would this require a new version of plutus? - ---- -## Possible Solution -What follows is my suggestion for how to solve this issue. As a bonus based on the way my solution would work, it would also become possible to spend script UTxOs that having missing/incorrect datums. To the best of my knowledge, this solution would not require a hardfork but would require a new version of plutus. - -*Scripts whose validation depends on the transaction as a whole will be referred to as **tx-level scripts** while scripts whose validation depends on an individual utxo will be referred to as **utxo-level scripts**.* - -In addition to staking scripts, minting scripts also already execute based off the transaction as a whole. At a high level, minting, staking, and spending plutus scripts are written like this: - -``` Haskell --- | minting policy before being compiled to plutus -mkMintingScript :: Redeemer -> ScriptContext -> Bool -mkMintingScript r ctx = ... - --- | Staking script before being compiled to plutus -mkStakingScript :: Redeemer -> ScriptContext -> Bool -mkStakingScript r ctx = ... - --- | Spending script before being compiled to plutus -mkSpendingScript :: Datum -> Redeemer -> ScriptContext -> Bool -mkSpendingScript d r ctx = ... -``` - -### Part 1 -The first part of the solution involves changing the way spending scripts are written to this: - -``` Haskell --- | New spending script before being compiled to plutus -mkValidator :: Maybe Datum -> Redeemer -> ScriptContext -> Bool -mkValidator Nothing r ctx = {- the case where the script can be used at a transaction level -} -mkValidator (Just d) r ctx = {- the case where the script can be used at the utxo level -} -``` - -### Part 2 -The second part involves **EXPLICITLY** telling the node which version to use. Here would be my atomic swap transaction using the transaction level version: - -``` Bash -cardano-cli transaction build \ - --tx-in c1d01ea50fd233f9fbaef3a295ba607a72c736e58c9c9df588abf4e5009ad4fe#0 \ - --tx-in 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#2 \ - --spending-plutus-script-v3 \ - --spending-reference-tx-in-inline-datum-present \ - --tx-in 766555130db8ff7b50fc548cbff3caa0d0557ce5af804da3b993cd090f1a8c3a#1 \ - --spending-plutus-script-v3 \ - --spending-reference-tx-in-inline-datum-present \ - --tx-out "$(cat ../assets/wallets/01.addr) 2000000 lovelace + 300 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ - --tx-out "$(cat ${swapScriptAddrFile}) + 4000000 lovelace + 0 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.54657374546f6b656e0a + 250 c0f8644a01a6bf5db02f4afe30d604975e63dd274f1098a1738e561d.4f74686572546f6b656e0a" \ - --tx-out-inline-datum-file $swapDatumFile \ - --tx-in-collateral bc54229f0755611ba14a2679774a7c7d394b0a476e59c609035e06244e1572bb#0 \ - --change-address $(cat ../assets/wallets/01.addr) \ - --tx-level-spending-tx-in-reference 622034715b64318e9e2176b7ad9bb22c3432f360293e9258729ce23c1999b9d8#0 \ - --tx-level-spending-reference-tx-in-redeemer-file $swapRedeemerFile \ - --protocol-params-file "${tmpDir}protocol.json" \ - --testnet-magic 1 \ - --out-file "${tmpDir}tx.body" -``` - -I removed the `spending-tx-in-reference` and the `spending-reference-tx-in-redeemer-file` lines and added `tx-level-spending-tx-in-reference` and `tx-level-spending-reference-tx-in-redeemer-file` after `change-address`. - -Right after the actual script `tx-in`s are `--spending-plutus-script-v3` and `--spending-reference-tx-in-inline-datum-present`. Keeping the datum flags here allows for "mixing and matching" inline datums and datum hashes. On the other hand, it is okay to move the redeemer flag to the end since plutus would still assume all UTxOs from a script address to be using the same redeemer. - -If validation needs to be done at the utxo-level, the original transaction can still be used (possibly with the `utxo-level` prefix attached) but the `spending-plutus-script-v2` flag would be changed to `spending-plutus-script-v3`. This seems required since previous plutus scripts do not use a `Maybe Datum`. - -When the spending script is used with the `tx-level-reference-script` option, `Nothing` is passed in for the `Maybe Datum`. On the other hand, when `spending-tx-in-reference` is used like usual, the datum will be parsed and passed with the `Just`. This way the code explicitly handles both the tx-level case and the utxo-level case. Using this technique, there would be no need for an on-chain encoding of a missing datum. Theoretically, all UTxOs could now be consumed as long as the tx-level script logic is used. This includes UTxOs that have incorrect/missing datums. - -### How will the node know if all required scripts are present? -The node is already capable of detecting whether all required witnesses are present. - -### What if a malicious entity forcibly passes in the wrong version of the datum (`Nothing` when it should be `Just d` or vice versa)? -The script logic can be written to account for this so I argue it is up to the developer to defend against this kind of attack. A simple error message when the wrong version is used could suffice for most use cases. For my use case, the code could be: - -``` Haskell -mkValidator :: Maybe Datum -> Redeemer -> ScriptContext -> Bool -mkValidator (Just _) _ _ = traceError "Not meant to be used at the utxo level" -mkValidator Nothing r ctx = {- do what I want -} -``` - -### Can multiple transaction level scripts be used in one transaction? -I don't see why not. The node is capable of detecting if all relevant scripts are present in the transaction. The transaction would only be valid if all necessary scripts succeed. - -### How would the `Maybe Datum` be represented at a low-level? -(I do not know much about the low-level design of plutus so I could be way off with this part.) - -The idea is to add an outer de bruijn layer around the `BuiltinData` before passing it to a script and then have the script immediately unwrap this extra layer. - -An example will help explain it. Imagine you have the low level encoding for the unit datum: - -``` JSON -{"constructor":0,"fields":[]} -``` - -If you want to pass this into a script as `Just ()`, you would add an outer de bruijn level BEFORE passing it to the script like this: - -``` JSON -{"constructor":1,"fields":[{"constructor":0,"fields":[]}]} -``` - -The unit datum is now inside `"fields"`. The script will immediately remove this outer layer by pattern matching like: - -``` Haskell -mkSpendingScript :: Maybe Datum -> Redeemer -> ScriptContext -> Bool -mkSpendingScript (Just ()) r ctx = ... -``` - -If you would like to pass in `Nothing` instead, you would use: - -``` JSON -{"constructor":0,"fields":[]} -``` - -**Notice how it looks exactly like the unit datum encoding. However, since this is a DIFFERENT de bruijn level, they are functionally different.** - -This extra layer would only be used at script exection time. All on-chain datums will not have this extra layer. All of the datums inside `ScriptContext` would also be left untouched. - -**Because this uses a different de bruijn level, this method does not use a default datum. All datums are left available for developers.** - -The use of this extra layer should be abstracted away so that users and developers don't even know it is being used. - -Some comments mention the specification being in terms of `Data` so here they are: -``` Haskell -Nothing = Constr 0 [] -Just datum = Constr 1 [dataOfDatum] -``` - -Again, since this is a different de bruijn level, this is not the same thing as using a default datum value. \ No newline at end of file +2. Would this require a new version of plutus? \ No newline at end of file From 068bbd868b90d2de2a5c5d30efeb6bc8a3fe42f5 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Tue, 17 Jan 2023 13:27:50 -0500 Subject: [PATCH 18/28] Capitalized UTxO --- CPS-????/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index e21f15a78..a211f27cc 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -44,9 +44,9 @@ If three utxos were to be spent from this script address, then you would need th --spending-reference-tx-in-redeemer-file \ ``` -What this translates to is that this script will be executed three times (once for each grouping). But what if the script validates based off of the transaction context as a whole and not any individual utxo? How can we use this "transaction level" script so that it is only executed once? There is no need to execute it more than once because the transaction context will be exactly the same for each execution. Another way to look at it is that we want a script to be able act like a payment skey where only one signature is needed no matter how many utxos are spent from the corresponding address. +What this translates to is that this script will be executed three times (once for each grouping). But what if the script validates based off of the transaction context as a whole and not any individual UTxO? How can we use this "transaction level" script so that it is only executed once? There is no need to execute it more than once because the transaction context will be exactly the same for each execution. Another way to look at it is that we want a script to be able act like a payment skey where only one signature is needed no matter how many UTxOs are spent from the corresponding address. -This brings us to the problem: **The current design does not allow for a spending script to be executed only once no matter how many utxos are spent from the corresponding script address.** This is unfortunate since the eUTxO architecture allows writing such scripts. As a result, any time a developer writes a "transaction level" script, the script will be redundantly executed for each additional utxo being spent from the script's address. These redundant executions are a waste of scarce resources and result in much higher fees for end users. **The current design prevents developers from using the full potential of the eUTxO.** +This brings us to the problem: **The current design does not allow for a spending script to be executed only once no matter how many UTxOs are spent from the corresponding script address.** This is unfortunate since the eUTxO architecture allows writing such scripts. As a result, any time a developer writes a "transaction level" script, the script will be redundantly executed for each additional UTxO being spent from the script's address. These redundant executions are a waste of scarce resources and result in much higher fees for end users. **The current design prevents developers from using the full potential of the eUTxO.** ### The Current Alternative The alternative involves taking advantage of the fact that staking scripts do not suffer from the same limitation as spending scripts. @@ -76,11 +76,11 @@ In a nut shell, you use a staking script **AS** a spending script. Here is how t --withdrawal-redeemer-file ``` -There are three new `--withdrawal` lines at the end. The transaction level logic is in the staking script. Meanwhile the spending script just checks that the required staking script is executed AND 0 ADA is withdrawn from the rewards address in the transaction. +There are three new `--withdrawal` lines at the end. The transaction level logic is in the staking script. Meanwhile, the spending script just checks that the required staking script is executed AND 0 ADA is withdrawn from the rewards address in the transaction. The only requirement to use this trick is that the script address must have been created using the `s'` spending script and the `s''` staking script. The staking address DOES NOT need to actually have rewards or even be delegated for this trick to work. -With this method, the main computation is only executed once no matter how many utxos are spent from the script address. +With this method, the main computation is only executed once no matter how many UTxOs are spent from the script address. #### The Drawbacks From cd657fd592962cc1e74c6e3752c91575b2d5a20b Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Tue, 17 Jan 2023 13:33:55 -0500 Subject: [PATCH 19/28] Edits --- CPS-????/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index a211f27cc..c12b34f69 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -12,10 +12,10 @@ Created: 2022-12-20 --- ## Abstract -Spendings scripts are currently executed once for every UTxO being consumed from a script address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. The eUTxO architecture allows a spending script to see the entire transaction context which means it is already possible to create scripts that validate based on the transaction as a whole. As of now, spending scripts that do this are forced to be executed once per UTxO even if these extra executions are completely redundant. Not only does this waste computational resources, but it can also result in the transaction fee growing quickly based on the number ot UTxOs being consumed from the relevant script address. There is a technique that can be used to minimize this limitation but it has its own drawbacks. +Spendings scripts are currently executed once for every UTxO being consumed from a script address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. The eUTxO architecture allows a spending script to see the entire transaction context which means it is already possible to create scripts that validate based on the transaction as a whole. As of now, spending scripts that do this are forced to be executed once per UTxO even if these extra executions are completely redundant. Not only does this waste computational resources, but it can also result in the transaction fee growing quickly based on the number of UTxOs being consumed from the relevant script address. There is a technique that can be used to minimize this limitation but it has its own drawbacks. ## Problem -Currently, all scripts require some variation of the following for every utxo being consumed from the script address: +Currently, all scripts require some variation of the following for every UTxO being consumed from the script address: ``` Bash --tx-in \ @@ -24,7 +24,7 @@ Currently, all scripts require some variation of the following for every utxo be --spending-reference-tx-in-inline-datum-present \ --spending-reference-tx-in-redeemer-file \ ``` -If three utxos were to be spent from this script address, then you would need three of these: +If three UTxOs were to be spent from this script address, then you would need three of these: ``` Bash --tx-in \ @@ -44,7 +44,7 @@ If three utxos were to be spent from this script address, then you would need th --spending-reference-tx-in-redeemer-file \ ``` -What this translates to is that this script will be executed three times (once for each grouping). But what if the script validates based off of the transaction context as a whole and not any individual UTxO? How can we use this "transaction level" script so that it is only executed once? There is no need to execute it more than once because the transaction context will be exactly the same for each execution. Another way to look at it is that we want a script to be able act like a payment skey where only one signature is needed no matter how many UTxOs are spent from the corresponding address. +What this translates to is that this script will be executed three times (once for each grouping). But what if the script validates based off of the transaction context as a whole and not on any individual UTxO? How can we use this "transaction level" script so that it is only executed once? There is no need to execute it more than once because the transaction context will be exactly the same for each execution. Another way to look at it is that we want a script to be able to act like a payment skey where only one signature is needed no matter how many UTxOs are spent from the corresponding address. This brings us to the problem: **The current design does not allow for a spending script to be executed only once no matter how many UTxOs are spent from the corresponding script address.** This is unfortunate since the eUTxO architecture allows writing such scripts. As a result, any time a developer writes a "transaction level" script, the script will be redundantly executed for each additional UTxO being spent from the script's address. These redundant executions are a waste of scarce resources and result in much higher fees for end users. **The current design prevents developers from using the full potential of the eUTxO.** @@ -76,7 +76,7 @@ In a nut shell, you use a staking script **AS** a spending script. Here is how t --withdrawal-redeemer-file ``` -There are three new `--withdrawal` lines at the end. The transaction level logic is in the staking script. Meanwhile, the spending script just checks that the required staking script is executed AND 0 ADA is withdrawn from the rewards address in the transaction. +There are three new `--withdrawal` lines at the end. The transaction level logic is in the staking script. Meanwhile, the spending script just checks that the required staking script is executed AND 0 ADA is withdrawn from the rewards address in the transaction. The withdrawal of the 0 ADA is necessary to force the staking script to be executed. The only requirement to use this trick is that the script address must have been created using the `s'` spending script and the `s''` staking script. The staking address DOES NOT need to actually have rewards or even be delegated for this trick to work. @@ -92,7 +92,7 @@ With this method, the main computation is only executed once no matter how many Any time a spending script's result depends on the entire transaction and not on any particular UTxO. 1. P2P Atomic Swaps - within one transaction, the value entering the script address must be some proportion of the value leaving the script address -2. P2P Lending - within one transaction, the amount borrowed must be some proportion of the amount posted as collateral. Likewise, the amount of collateral reclaimed depends on how much of the loan has been repaid. +2. P2P Lending - within one transaction, the amount borrowed must be some proportion of the amount posted as collateral. Likewise, the amount of collateral reclaimed depends on how much of the loan is being repaid. 3. DAOs 4. Multisig applications that require more complex logic than what is possible with native scripts From 6a7f67aa2a87da84dc905de57b3b8f933f22f1f6 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Wed, 18 Jan 2023 10:32:41 -0500 Subject: [PATCH 20/28] Replaced problem example with an easy to understand scenario. + edits --- CPS-????/README.md | 87 ++++++++++++++-------------------------------- 1 file changed, 26 insertions(+), 61 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index c12b34f69..ee5421465 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -10,95 +10,60 @@ Discussions: - https://github.com/cardano-foundation/CIPs/issues/417 Created: 2022-12-20 --- +# CPS-XXXX: Spending Script Redundant Executions ## Abstract Spendings scripts are currently executed once for every UTxO being consumed from a script address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. The eUTxO architecture allows a spending script to see the entire transaction context which means it is already possible to create scripts that validate based on the transaction as a whole. As of now, spending scripts that do this are forced to be executed once per UTxO even if these extra executions are completely redundant. Not only does this waste computational resources, but it can also result in the transaction fee growing quickly based on the number of UTxOs being consumed from the relevant script address. There is a technique that can be used to minimize this limitation but it has its own drawbacks. ## Problem -Currently, all scripts require some variation of the following for every UTxO being consumed from the script address: - -``` Bash - --tx-in \ - --spending-tx-in-reference \ - --spending-plutus-script-v2 \ - --spending-reference-tx-in-inline-datum-present \ - --spending-reference-tx-in-redeemer-file \ -``` -If three UTxOs were to be spent from this script address, then you would need three of these: - -``` Bash - --tx-in \ - --spending-tx-in-reference \ - --spending-plutus-script-v2 \ - --spending-reference-tx-in-inline-datum-present \ - --spending-reference-tx-in-redeemer-file \ - --tx-in \ - --spending-tx-in-reference \ - --spending-plutus-script-v2 \ - --spending-reference-tx-in-inline-datum-present \ - --spending-reference-tx-in-redeemer-file \ - --tx-in \ - --spending-tx-in-reference \ - --spending-plutus-script-v2 \ - --spending-reference-tx-in-inline-datum-present \ - --spending-reference-tx-in-redeemer-file \ +Imagine a scenario where Alice would like to trustlessly swap assets with Bob. An atomic swap contract is used and any UTxOs that are to be swapped contain an inline datum of an asking price. Here is a table to represent all of Alice's available UTxOs to swap (at the atomic swap script address): + +|UTxO ID| Asking Price|Amount| +|--|--|--| +| 1 | 1.5 DUST/ADA | 10 ADA | +| 2 | 2 DUST/ADA | 20 ADA | +| 3 | 1 DUST/ADA | 5 ADA | + +If Bob wants to swap his DUST for Alice's ADA, he will need to satisfy Alice's asking price. As an example, if he wants to take all 10 ADA from UTxO 1, he will need to deposit 15 DUST to Alice's atomic swap address in the same transaction that consumes UTxO 1. + +But what if he wants to swap his DUST for 25 ADA? That would require using at least two of Alice's UTxOs. Doing this is trivial for the atomic swap contract because it validates based off of the transaction as a whole: + +``` Txt +The value being deposited to the script address must be <= the value withdrawn from the script address * the weighted average asking price of all UTxOs being swapped. ``` -What this translates to is that this script will be executed three times (once for each grouping). But what if the script validates based off of the transaction context as a whole and not on any individual UTxO? How can we use this "transaction level" script so that it is only executed once? There is no need to execute it more than once because the transaction context will be exactly the same for each execution. Another way to look at it is that we want a script to be able to act like a payment skey where only one signature is needed no matter how many UTxOs are spent from the corresponding address. +This brings us to the problem: **there is currently no way to efficiently use a script that validates based off of the transaction as a whole**. (These scripts will be called "transaction level spending scripts" for the rest of this CPS). With the current design of Cardano, if Bob consumed UTxO 2 and UTxO 3 in the same transaction to satisfy his desire for 25 ADA, the atomic swap contract will be executed twice. Since the transaction context doesn't change between executions, the second execution is completely redundant. -This brings us to the problem: **The current design does not allow for a spending script to be executed only once no matter how many UTxOs are spent from the corresponding script address.** This is unfortunate since the eUTxO architecture allows writing such scripts. As a result, any time a developer writes a "transaction level" script, the script will be redundantly executed for each additional UTxO being spent from the script's address. These redundant executions are a waste of scarce resources and result in much higher fees for end users. **The current design prevents developers from using the full potential of the eUTxO.** +Any time a developer creates a transaction level spending script, it will be redundantly executed for every additional UTxO consumed from the corresponding script address. These redundant executions are a waste of scarce resources and result in much higher fees for end users. Not only that, but the inability to efficiently use these transaction level spending scripts significantly handicaps the use of the eUTxO model. The eUTxO model already allows for creating such spending scripts; developers are just unable to properly use them. ### The Current Alternative The alternative involves taking advantage of the fact that staking scripts do not suffer from the same limitation as spending scripts. > Given a [spending] script **s**, you can turn it into a [spending] script **s'** that checks that 0 ADA is withdrawn from a [staking] script **s''** with the same logic as **s**. This is a transformation that is valid for any [spending] script. -- @L-as -In a nut shell, you use a staking script **AS** a spending script. Here is how the above transaction example would look with this technique: - -``` Bash - --tx-in \ - --spending-tx-in-reference \ - --spending-plutus-script-v2 \ - --spending-reference-tx-in-inline-datum-present \ - --spending-reference-tx-in-redeemer-file \ - --tx-in \ - --spending-tx-in-reference \ - --spending-plutus-script-v2 \ - --spending-reference-tx-in-inline-datum-present \ - --spending-reference-tx-in-redeemer-file \ - --tx-in \ - --spending-tx-in-reference \ - --spending-plutus-script-v2 \ - --spending-reference-tx-in-inline-datum-present \ - --spending-reference-tx-in-redeemer-file \ - --withdrawal +0 \ - --withdrawal-script-file \ - --withdrawal-redeemer-file -``` - -There are three new `--withdrawal` lines at the end. The transaction level logic is in the staking script. Meanwhile, the spending script just checks that the required staking script is executed AND 0 ADA is withdrawn from the rewards address in the transaction. The withdrawal of the 0 ADA is necessary to force the staking script to be executed. +In a nut shell, you use a staking script **AS** a spending script. The transaction level logic would be in the staking script. Meanwhile, the spending script just checks that both the required staking script is executed AND 0 ADA is withdrawn from the rewards address within the transaction. The withdrawal of the 0 ADA is necessary to force the staking script to be executed. -The only requirement to use this trick is that the script address must have been created using the `s'` spending script and the `s''` staking script. The staking address DOES NOT need to actually have rewards or even be delegated for this trick to work. +The only requirement to use this technique is that the script address must have been created using the `s'` spending script and the `s''` staking script. The staking address DOES NOT need to actually have rewards or even be delegated for this technique to work. With this method, the main computation is only executed once no matter how many UTxOs are spent from the script address. #### The Drawbacks -1. This trick now means that everything requires two plutus scripts instead of just one. While the spending script can be made extremely small, this doesn't change the fact that two are now needed. -2. This trick DOES NOT stop the redundant executions; the spending script will still redundantly check if the required staking script was executed. This trick just minimizes the cost of the redundant executions by moving the heavy computation into a script that is only executed once per transaction. +1. This technique now means that everything requires two plutus scripts instead of just one. While the spending script can be made extremely small, this doesn't change the fact that two are now needed. +2. This technique DOES NOT stop the redundant executions; the spending script will still redundantly check if the required staking script was executed. This trick just minimizes the cost of the redundant executions by moving the heavy computation into a script that is only executed once per transaction. 3. This is not the intended use for staking scripts. ## Use Cases -Any time a spending script's result depends on the entire transaction and not on any particular UTxO. +Any time a spending script's validation depends on the entire transaction and not on any particular UTxO. -1. P2P Atomic Swaps - within one transaction, the value entering the script address must be some proportion of the value leaving the script address -2. P2P Lending - within one transaction, the amount borrowed must be some proportion of the amount posted as collateral. Likewise, the amount of collateral reclaimed depends on how much of the loan is being repaid. +1. P2P Atomic Swaps - within one transaction, the value entering the swap address must be some proportion of the value leaving the swap address +2. P2P Lending - within one transaction, the amount borrowed must be some proportion of the amount posted as collateral. Likewise, the amount of collateral reclaimed depends on how much of the loan is repaid. 3. DAOs 4. Multisig applications that require more complex logic than what is possible with native scripts ## Goals -1. Stop redundantly executing spending scripts. -2. Do not remove the option to allow script execution per UTxO. There are still cases where the script should validate based off individual UTxOs. Also, there could be cases where this option is more efficient than having to traverse the transaction's context to get the relevant datum. An example of this latter case is when only one UTxO is consumed from a script address. +1. Stop redundantly executing transaction level spending scripts. +2. Do not remove the option to allow script execution per UTxO. There are still use cases where the script should validate based off individual UTxOs. Also, there could be cases where this option is more efficient than having to traverse the transaction's context to get the relevant datum. An example of this latter case is when only one UTxO is consumed from a script address. 3. Try to use the ledger as is. The way scripts are stored onchain should not change unless absolutely necessary. 4. Try to leave the developer experience as untouched as possible. We don't want to make developers re-learn plutus. 5. Do not add too much new overhead to already resource constrained plutus scripts. From 40b37c4b8030d9f25ec5cd8e4c741c39e77eb322 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Wed, 18 Jan 2023 10:38:42 -0500 Subject: [PATCH 21/28] Edited multisig use case --- CPS-????/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index ee5421465..8658933a0 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -59,7 +59,7 @@ Any time a spending script's validation depends on the entire transaction and no 1. P2P Atomic Swaps - within one transaction, the value entering the swap address must be some proportion of the value leaving the swap address 2. P2P Lending - within one transaction, the amount borrowed must be some proportion of the amount posted as collateral. Likewise, the amount of collateral reclaimed depends on how much of the loan is repaid. 3. DAOs -4. Multisig applications that require more complex logic than what is possible with native scripts +4. Multisig - within one transaction, the multisig threshold must be met ## Goals 1. Stop redundantly executing transaction level spending scripts. From fd604a337750fb6c0a9ea4abe7aa6989c4bef668 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Wed, 18 Jan 2023 10:55:13 -0500 Subject: [PATCH 22/28] Broke equation over multiple lines --- CPS-????/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 8658933a0..10fb78b3a 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -29,10 +29,12 @@ If Bob wants to swap his DUST for Alice's ADA, he will need to satisfy Alice's a But what if he wants to swap his DUST for 25 ADA? That would require using at least two of Alice's UTxOs. Doing this is trivial for the atomic swap contract because it validates based off of the transaction as a whole: ``` Txt -The value being deposited to the script address must be <= the value withdrawn from the script address * the weighted average asking price of all UTxOs being swapped. +The value being deposited to the script address must be <= + the value withdrawn from the script address * + the weighted average asking price of all UTxOs being spent from the swap address. ``` -This brings us to the problem: **there is currently no way to efficiently use a script that validates based off of the transaction as a whole**. (These scripts will be called "transaction level spending scripts" for the rest of this CPS). With the current design of Cardano, if Bob consumed UTxO 2 and UTxO 3 in the same transaction to satisfy his desire for 25 ADA, the atomic swap contract will be executed twice. Since the transaction context doesn't change between executions, the second execution is completely redundant. +This brings us to the problem: **there is currently no way to efficiently use a spending script that validates based off of the transaction as a whole**. (These scripts will be called "transaction level spending scripts" for the rest of this CPS). With the current design of Cardano, if Bob consumed UTxO 2 and UTxO 3 in the same transaction to satisfy his desire for 25 ADA, the atomic swap contract will be executed twice. Since the transaction context doesn't change between executions, the second execution is completely redundant. Any time a developer creates a transaction level spending script, it will be redundantly executed for every additional UTxO consumed from the corresponding script address. These redundant executions are a waste of scarce resources and result in much higher fees for end users. Not only that, but the inability to efficiently use these transaction level spending scripts significantly handicaps the use of the eUTxO model. The eUTxO model already allows for creating such spending scripts; developers are just unable to properly use them. From f9cc61c4e314a2784ca9df98a07ab374dac1d9aa Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Wed, 18 Jan 2023 10:56:25 -0500 Subject: [PATCH 23/28] Fixed equation --- CPS-????/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 10fb78b3a..dd79e9802 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -29,8 +29,8 @@ If Bob wants to swap his DUST for Alice's ADA, he will need to satisfy Alice's a But what if he wants to swap his DUST for 25 ADA? That would require using at least two of Alice's UTxOs. Doing this is trivial for the atomic swap contract because it validates based off of the transaction as a whole: ``` Txt -The value being deposited to the script address must be <= - the value withdrawn from the script address * +The value being deposited to the swap address must be >= + the value withdrawn from the swap address * the weighted average asking price of all UTxOs being spent from the swap address. ``` From 808c528a9accde21fed01f337fc9539d1e76b1d9 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Wed, 18 Jan 2023 10:58:17 -0500 Subject: [PATCH 24/28] removed period in equation --- CPS-????/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index dd79e9802..14f2faecb 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -31,7 +31,7 @@ But what if he wants to swap his DUST for 25 ADA? That would require using at le ``` Txt The value being deposited to the swap address must be >= the value withdrawn from the swap address * - the weighted average asking price of all UTxOs being spent from the swap address. + the weighted average asking price of all UTxOs being spent from the swap address ``` This brings us to the problem: **there is currently no way to efficiently use a spending script that validates based off of the transaction as a whole**. (These scripts will be called "transaction level spending scripts" for the rest of this CPS). With the current design of Cardano, if Bob consumed UTxO 2 and UTxO 3 in the same transaction to satisfy his desire for 25 ADA, the atomic swap contract will be executed twice. Since the transaction context doesn't change between executions, the second execution is completely redundant. From 0618887e6d6aa4c3b3a69ce51e0c1e7c32920f54 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Thu, 19 Jan 2023 08:20:38 -0500 Subject: [PATCH 25/28] Removed multisig use case until confirmed. Edits. --- CPS-????/README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 14f2faecb..eafff41e7 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -13,7 +13,7 @@ Created: 2022-12-20 # CPS-XXXX: Spending Script Redundant Executions ## Abstract -Spendings scripts are currently executed once for every UTxO being consumed from a script address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. The eUTxO architecture allows a spending script to see the entire transaction context which means it is already possible to create scripts that validate based on the transaction as a whole. As of now, spending scripts that do this are forced to be executed once per UTxO even if these extra executions are completely redundant. Not only does this waste computational resources, but it can also result in the transaction fee growing quickly based on the number of UTxOs being consumed from the relevant script address. There is a technique that can be used to minimize this limitation but it has its own drawbacks. +Plutus spendings scripts are currently executed once for every UTxO being consumed from the script's address. However, there are use cases where the validity of the transaction depends on the transaction as a whole and not on any individual UTxO. The eUTxO model allows a spending script to see the entire transaction context which means it is already possible to create scripts that validate based on the transaction as a whole. As of now, spending scripts that do this are forced to be executed once per UTxO even if these extra executions are completely redundant. Not only does this waste computational resources, but it can also result in the transaction fee growing quickly based on the number of UTxOs being consumed from the relevant script address. There is a technique that can be used to minimize this limitation but it has its own drawbacks. ## Problem Imagine a scenario where Alice would like to trustlessly swap assets with Bob. An atomic swap contract is used and any UTxOs that are to be swapped contain an inline datum of an asking price. Here is a table to represent all of Alice's available UTxOs to swap (at the atomic swap script address): @@ -34,7 +34,7 @@ The value being deposited to the swap address must be >= the weighted average asking price of all UTxOs being spent from the swap address ``` -This brings us to the problem: **there is currently no way to efficiently use a spending script that validates based off of the transaction as a whole**. (These scripts will be called "transaction level spending scripts" for the rest of this CPS). With the current design of Cardano, if Bob consumed UTxO 2 and UTxO 3 in the same transaction to satisfy his desire for 25 ADA, the atomic swap contract will be executed twice. Since the transaction context doesn't change between executions, the second execution is completely redundant. +This brings us to the problem: **there is currently no way to efficiently use a plutus spending script that validates based off of the transaction as a whole**. (These scripts will be called *transaction level spending scripts* for the rest of this CPS). With the current design of Cardano, if Bob consumed UTxO 2 and UTxO 3 in the same transaction to satisfy his desire for 25 ADA, the atomic swap contract will be executed twice. Since the transaction context doesn't change between executions, the second execution is completely redundant. Any time a developer creates a transaction level spending script, it will be redundantly executed for every additional UTxO consumed from the corresponding script address. These redundant executions are a waste of scarce resources and result in much higher fees for end users. Not only that, but the inability to efficiently use these transaction level spending scripts significantly handicaps the use of the eUTxO model. The eUTxO model already allows for creating such spending scripts; developers are just unable to properly use them. @@ -45,14 +45,14 @@ The alternative involves taking advantage of the fact that staking scripts do no In a nut shell, you use a staking script **AS** a spending script. The transaction level logic would be in the staking script. Meanwhile, the spending script just checks that both the required staking script is executed AND 0 ADA is withdrawn from the rewards address within the transaction. The withdrawal of the 0 ADA is necessary to force the staking script to be executed. -The only requirement to use this technique is that the script address must have been created using the `s'` spending script and the `s''` staking script. The staking address DOES NOT need to actually have rewards or even be delegated for this technique to work. +The only requirement to use this technique is that the script address must have been created using the `s'` spending script and the `s''` staking script. The staking address DOES NOT need to actually have rewards or even be delegated/registered for this technique to work. With this method, the main computation is only executed once no matter how many UTxOs are spent from the script address. #### The Drawbacks 1. This technique now means that everything requires two plutus scripts instead of just one. While the spending script can be made extremely small, this doesn't change the fact that two are now needed. -2. This technique DOES NOT stop the redundant executions; the spending script will still redundantly check if the required staking script was executed. This trick just minimizes the cost of the redundant executions by moving the heavy computation into a script that is only executed once per transaction. +2. This technique DOES NOT stop the redundant executions; the spending script will still redundantly check if the required staking script was executed. This technique just minimizes the cost of the redundant executions by moving the heavy computation into a script that is only executed once per transaction. 3. This is not the intended use for staking scripts. ## Use Cases @@ -61,7 +61,6 @@ Any time a spending script's validation depends on the entire transaction and no 1. P2P Atomic Swaps - within one transaction, the value entering the swap address must be some proportion of the value leaving the swap address 2. P2P Lending - within one transaction, the amount borrowed must be some proportion of the amount posted as collateral. Likewise, the amount of collateral reclaimed depends on how much of the loan is repaid. 3. DAOs -4. Multisig - within one transaction, the multisig threshold must be met ## Goals 1. Stop redundantly executing transaction level spending scripts. From b4946f20f90c267e29a3075a3bae55ac63ac7b92 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Thu, 19 Jan 2023 10:03:54 -0500 Subject: [PATCH 26/28] Added comments about simple scripts --- CPS-????/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CPS-????/README.md b/CPS-????/README.md index eafff41e7..ec57fefca 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -55,12 +55,19 @@ With this method, the main computation is only executed once no matter how many 2. This technique DOES NOT stop the redundant executions; the spending script will still redundantly check if the required staking script was executed. This technique just minimizes the cost of the redundant executions by moving the heavy computation into a script that is only executed once per transaction. 3. This is not the intended use for staking scripts. +### Simple Scripts +I tried to test Cardano simple scripts to determine if this problem also impacts them. However, given that simple scripts are so small, it was hard to tell from just looking at the transaction fee. Since the interface for using Cardano simple scripts is the same as for using plutus scripts,I have to assume that this problem also impacts simple scripts. + +If simple scripts are indeed impacted by this problem, the above technique would not work for them. The technique only works for plutus scripts. + ## Use Cases Any time a spending script's validation depends on the entire transaction and not on any particular UTxO. 1. P2P Atomic Swaps - within one transaction, the value entering the swap address must be some proportion of the value leaving the swap address 2. P2P Lending - within one transaction, the amount borrowed must be some proportion of the amount posted as collateral. Likewise, the amount of collateral reclaimed depends on how much of the loan is repaid. 3. DAOs +4. (Assuming simple scripts also suffer from this problem) Multisig +5. (Assuming simple scripts also suffer from this problem) Time Locking ## Goals 1. Stop redundantly executing transaction level spending scripts. From 64f10619a59cff61e2dc57b3fdaf611e8c36a4d4 Mon Sep 17 00:00:00 2001 From: fallen-icarus Date: Mon, 23 Jan 2023 07:46:15 -0500 Subject: [PATCH 27/28] added missing space --- CPS-????/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index ec57fefca..6f2757f46 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -56,7 +56,7 @@ With this method, the main computation is only executed once no matter how many 3. This is not the intended use for staking scripts. ### Simple Scripts -I tried to test Cardano simple scripts to determine if this problem also impacts them. However, given that simple scripts are so small, it was hard to tell from just looking at the transaction fee. Since the interface for using Cardano simple scripts is the same as for using plutus scripts,I have to assume that this problem also impacts simple scripts. +I tried to test Cardano simple scripts to determine if this problem also impacts them. However, given that simple scripts are so small, it was hard to tell from just looking at the transaction fee. Since the interface for using Cardano simple scripts is the same as for using plutus scripts, I have to assume that this problem also impacts simple scripts. If simple scripts are indeed impacted by this problem, the above technique would not work for them. The technique only works for plutus scripts. From 7f9f23893427fc10a97624cb627c86254735ef82 Mon Sep 17 00:00:00 2001 From: Robert Phair Date: Mon, 24 Apr 2023 20:20:53 +0530 Subject: [PATCH 28/28] fixating CPS number to facilitate incoming references Co-authored-by: Matthias Benkort <5680256+KtorZ@users.noreply.github.com> --- CPS-????/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPS-????/README.md b/CPS-????/README.md index 6f2757f46..96e832eae 100644 --- a/CPS-????/README.md +++ b/CPS-????/README.md @@ -1,5 +1,5 @@ --- -CPS: ? +CPS: 4 Title: Spending Script Redundant Executions Status: Open Category: Plutus