-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Marlowe contracts lack on-chain identity #223
Comments
@edsko, thank you for your helpful analyses and suggestions.
This is the idea solution, and a future version of Marlowe's Cardano implementation may thread identity tokens through each contract. (One of the recent Marlowe Improvement Proposal discussions called this a
Yes, the PAB-based backend was only designed to handle the contracts that do use role tokens. That code is being replace by a Marlowe Runtime that remedies the difficulty with discovering contract history for role-less Marlowe contracts: https://github.com/input-output-hk/marlowe-cardano/tree/cardano-node-1.35/marlowe-runtime. We're currently changing the application and payout validators to have constant hashes (one script per Marlowe version, regardless of role currency symbol) in order to take advantage of CIP-33 reference scripts. This requires several changes to maintain on-chain security and off-chain contract-history discovery: https://github.com/input-output-hk/marlowe-cardano/tree/SCP-4367-constant-validator. Finally, we've had quite a bit of discussion about integrating the monetary policy with the spending validator and have an implementation of that. There is a tradeoff between flexibility in monetary policy vs tighter control of minting/burning, and the approach interacts with things like constancy or non-constancy of addresses. |
Hi @bwbush, Thank you for your detailed response, and apologies for my slow reaction, it's been a busy two weeks. I have studied the MIP you linked to, read through (most of) the discusison, and also took a look at the proof-of-concept implementation that is linked to from the MIP (#117). Let me try to summarize the proposal, to make sure I understand it correctly:
A consequence of this is that the role token minting policy is no longer a parameter, but a fixed part of the Marlowe ecosystem (whether or not this is the "universal validator" that #117 proposes I think is orthogonal). This all sounds great and will work just fine for our use case, provided that we can still choose our role payout script. This is the only way for us to hook into the execution of a Marlowe script, and that ability is critical for us. But nothing in the MIP or #117 suggests that that will change, so I guess that is fine. We will need to change the off-chain code I suppose, but that is already the case: the evaluator takes the role payout script as an argument but the off-chain code doesn't currently allow to choose; which is fine; we can use our own off-chain code. For us it will be critical that the role payout script is also aware of that data MarloweParams = MarloweParams {
rolePayoutValidatorHash :: ValidatorHash,
uniqueTxOutRef :: (TxId, Integer)
} which doesn't preclude the possibility that the |
Oh no, I just realized that that branch you linked to removes the option to set a different role payout validator... :-o That would be a disaster for us, as that is the only way that we effect the execution of the contract. Why is that being removed...? |
Ok, I have read @paluh 's proposal a bit more carefully again. The proposal to mint one special "role" that will act as the validator token is indeed exactly in line with what I was proposing above. The proposal also states that the role payout script will not be a parameter anymore; but it doesn't say (unless I missed it somewhere) how role payouts will then work instead. Will the funds remain locked in the contract UTxO, and they can be redeemed one by one from there? I think that would work for us actually; I need to think a bit more about it. The good thing about that is that we can then verify that that special "validator token" is also present in that UTxO. This is the thing that I wanted to discuss in relation to my original proposal, above: even if we introduce a validator token for the contract, we would still lose the connection between the execution of that contract and the various payouts, and that link is important for us. We need to know that this payout came from the execution of this script; that identity is key. I think with @paluh 's proposal, if I understand correctly that the funds are not paid out to different scripts at all, that problem disappears. The cost to it I guess is that funds must be redeemed sequentially, but that doesn't seem like a huge deal. |
@bwbush @paluh I realize I've been rambling a bit above. I would like to try and summarize what I think you guys are proposing and are implementing, to make sure I understand it correctly.
Importantly (for us), point (5) means that when redeeming, we can check for the presence of the validator token, and so know for sure that the funds that are being redeemed really came from the execution of the contract with identity Could you let me know if the above understanding is correct? (If it is, that would perfectly for us, and we do not need the ability to specify a different role payout script after all.) |
Thanks for letting me know! And apologies for disappearing off the radar, the client for which I was doing this work paused the work, hence I've been busy with other things instead. |
Proviso: I've labelled this as a bug after a fairly thorough deep-dive into the code, but it's possible that I have misunderstood something. In that case, please accept my apologies 🙂
Most contracts that are state-machine like abstractions make use of an NFT as a unique identifier of any particular instance of the contract. In earlier implementations of Marlowe, where it still used the Plutus state machine abstraction machinery, this was the case as well because the SM machinery provided it implicitly. However, the current implementation does not use the state machine abstraction anymore, and no NFT is created for new Marlowe contract instances.
When the Marlowe off-chain code tries to find the UTxO that holds the current state of the contract in
getMarloweTxOuts
, it does this purely on the basis of theSmallTypedValidator
. This in turn depends only on theMarloweParams
:Here
rolePayoutValidatorHash
is normally set tomkRolePayoutValidatorHash rolesCurrency
, so the only thing that varies here is therolesCurrency
.When a particular Marlowe contract does not use roles, that
rolesCurrency
is set toadaSymbol
. This means that the search for a UTxO for a Marlowe contract without roles will fail with anAmbiguousOnChainState
error if there is any other Marlowe contract running at all. Worse, if the contract we think we are interacting with has actually terminated, we might instead be interacting with the wrong contract (if there happens to be a single other one).This makes the use of roles almost compulsory. When a new contract is started and it uses roles, a new role currency symbol is created using
OneShotCurrency
. Since this is anchored to a particular UTxO, just like an NFT is, this will be unique every time a new contract is created.This improves the situation: now every contract that is created using the Marlowe off-chain code does in fact have a unique ID, due to that unique
rolesCurrency
. However, as far as I can tell, it does not solve the problem: nothing is preventing malicious users from creating another UTxO at the same validator with the sameMarloweParams
. This is not the intended usage, of course, but nothing in the on-chain code prevents this from happening. As before, the off-chain code will detect the problem and throw anAmbiguousOnChainState
error when it happens -- but again, it will fail to detect the problem if the original contract has terminated, leaving the possibility we might be able to trick people in engaging with the new contract.We could perhaps make the situation slightly better by using a role payout script that instead requires that role tokens are handed in (like it does now) and are then burned when funds are redeemed. This means that if funds are redeemed, the original contract cannot be interacted with anymore. But that's still an "if"; under some circumstances it might be worthwhile for an attacker to trick people in locking up funds even if they (attacker or victim) cannot redeem them. It doesn't feel like a real solution.
I think the solution here is is to create an NFT (state token) for each new contract instance, and add that NFT to
MarloweParams
. This way we can now uniquely pointed to an instance of a Marlowe contract on-chain, and there is no ambiguity. When the contract terminates and the payouts are made, the state token would be burned so the contract cannot be interacted with anymore and it's impossible to start another contract with the same NFT.It would then probably be good if the role payout script hash would also depend on this NFT, so that this is tied down as well.
The text was updated successfully, but these errors were encountered: