Plutonomicon / cardano-transaction-lib

A Purescript library for building smart contract transactions on Cardano

Home Page:https://plutonomicon.github.io/cardano-transaction-lib/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Automatic collateral selection does not go well with `mustNotSpendUtxos`

jstolarek opened this issue · comments

When using mustNotSpendUtxos and mustNotSpendUtxosWithOutRefs it sometimes happens that one of the forbidden UTxOs is selected as a collateral. Collateral selection should honour the list of forbidden UTxOs defined by the user.

Which revision are you on?
I was quite sure we already fixed this: #1512

We are on rev: b7e8d396711f95e7a7b755a2a7e7089df712aaf5 which seems to have the fix. However I don't think the fix does the job in some cases. For example, we have a test case where the first transaction submitted requires a collateral and also have a specified non spendable utxo. Depending on the initial configuration of wallet utxos we can make the system reject the transaction due to missing collateral. To give some concrete example:

Scenario 1

Initial utxo distribution:

[ BigInt.fromInt 1_000_000
, BigInt.fromInt 150_000_000
]

Non spendable utxo:

(Just (TransactionOutput { address: (Address { addressCredential: (PubKeyCredential (PubKeyHash (Ed25519KeyHash 05ff9c2520f604a8e344755d00f6000d9d6e43345ba6711363bcd58b))), addressStakingCredential: Nothing }), amount: (PlutusValue (Map [(Tuple (CurrencySymbol (hexToByteArrayUnsafe "")) (Map [(Tuple (TokenName (hexToRawBytesUnsafe "")) fromString "1000000")]))])), datum: NoOutputDatum, referenceScript: Nothing }))

Result of getWalletCollateral

(Just [(TransactionUnspentOutput { input: (TransactionInput { index: 1u, transactionId: (TransactionHash (hexToByteArrayUnsafe "ba46501ccee2f425fc515149757c4606811ba4e7e19228d6fd38d8d593a157ee")) }), output: (TransactionOutputWithRefScript { output: (TransactionOutput { address: (Address { addressCredential: (PubKeyCredential (PubKeyHash (Ed25519KeyHash 05ff9c2520f604a8e344755d00f6000d9d6e43345ba6711363bcd58b))), addressStakingCredential: Nothing }), amount: (PlutusValue (Map [(Tuple (CurrencySymbol (hexToByteArrayUnsafe "")) (Map [(Tuple (TokenName (hexToRawBytesUnsafe "")) fromString "150000000")]))])), datum: NoOutputDatum, referenceScript: Nothing }), scriptRef: Nothing }) })])

The 1Ada utxo is not chosen as a possible collateral so its not a problem that we forbid spending it. The transaction succeeds without a problem.

Scenario 2

Initial utxo distribution:

[ BigInt.fromInt 6_000_000
, BigInt.fromInt 150_000_000
]

Non spendable utxo:

(Just (TransactionOutput { address: (Address { addressCredential: (PubKeyCredential (PubKeyHash (Ed25519KeyHash 36409f4f12b733a9c3df403885aff2ed7316add1e37b13dd2b283d08))), addressStakingCredential: Nothing }), amount: (PlutusValue (Map [(Tuple (CurrencySymbol (hexToByteArrayUnsafe "")) (Map [(Tuple (TokenName (hexToRawBytesUnsafe "")) fromString "6000000")]))])), datum: NoOutputDatum, referenceScript: Nothing }))

Result of getWalletCollateral:

(Just [(TransactionUnspentOutput { input: (TransactionInput { index: 0u, transactionId: (TransactionHash (hexToByteArrayUnsafe "2566916395d2f593dfe6f3dbc692604aebd89d0c60b03e9f16197e0454ade90d")) }), output: (TransactionOutputWithRefScript { output: (TransactionOutput { address: (Address { addressCredential: (PubKeyCredential (PubKeyHash (Ed25519KeyHash 36409f4f12b733a9c3df403885aff2ed7316add1e37b13dd2b283d08))), addressStakingCredential: Nothing }), amount: (PlutusValue (Map [(Tuple (CurrencySymbol (hexToByteArrayUnsafe "")) (Map [(Tuple (TokenName (hexToRawBytesUnsafe "")) fromString "6000000")]))])), datum: NoOutputDatum, referenceScript: Nothing }), scriptRef: Nothing }) })])

In this case the non spendable utxo was also selected as the only available collateral candidate. If we try to submit a transaction in this case, we'll get an error like:

Expected failure (and got failure): Failed to submit tx:
(ClientOtherError [{"missingCollateralInputs":null},{"collateralTooSmall":{"requiredCollateral":662097,"actualCollateral":0}}])

Basically there can be the case where the algorithm will choose only a single utxo as a candidate for a collateral. In this case if this utxo is also non spendable during some transaction, this transaction will fail to be successfully built.

@jankun4 could you please provide a reproducible test to simplify debugging for us?

Basically there can be the case where the algorithm will choose only a single utxo as a candidate for a collateral. In this case if this utxo is also non spendable during some transaction, this transaction will fail to be successfully built.

What do you think should be the correct behavior?

could you please provide a reproducible test to simplify debugging for us?

I don't think we can easily provide a minimal test case - we'd need to get the time to build a minimal example approved by our managers.

What do you think should be the correct behavior?

The correct behaviour would be to never select forbidden UTxOs as a collateral. In case the forbidden UTxOs are the only viable collaterals, this should lead to balancing error.