Add unsafeLovelaceValueOf: a positional lovelace accessor for ledger Values#7838
Conversation
…lues lovelaceValueOf reads the ada quantity via valueOf, which walks the outer map comparing each currency-symbol key against adaSymbol and then the inner map comparing each token-name key against adaToken. For a canonical ledger-provided Value (a TxOut value) ada sorts first and is always present, so the lovelace quantity is exactly the first-of-first amount and can be read positionally, skipping both equalsByteString comparisons and the key unwrapping. unsafeLovelaceValueOf provides that positional read in both PlutusLedgerApi.V1.Value (Scott-encoded) and PlutusLedgerApi.V1.Data.Value (Data-backed), re-exported through PlutusLedgerApi.V1. It is not a drop-in replacement for lovelaceValueOf: the ada-first-and-present invariant holds only for ledger-provided values, not for mint or user-constructed Values, where it would misread or fail. The precondition is documented in the Haddock. Isolated budget goldens on a ledger-shaped value show -54% CPU / -65% memory for the Data-backed variant and -26% CPU / -23% memory for the Scott variant, both evaluating to the same result. Adds property tests asserting agreement with lovelaceValueOf, plus budget and PIR-readable goldens for GHC 9.6 and 9.12.
zliu41
left a comment
There was a problem hiding this comment.
Was this requested or suggested by someone or was it your own idea?
I shortened the PR description for you. Your version used too many words to say something that can be said very simply.
The idea I got while optimizing UPLC-CAPE validators and comparing Plinth/Scalus implementations. I saw that "unsafe" Lovelace retrieval gives quite some ex budget boost, especially when its done multiple times (for different tx inputs). |
zliu41
left a comment
There was a problem hiding this comment.
The idea I got while optimizing UPLC-CAPE validators and comparing Plinth/Scalus implementations. I saw that "unsafe" Lovelace retrieval gives quite some ex budget boost, especially when its done multiple times (for different tx inputs).
Very nice.
PlutusLedgerApi.V2, V3, and Data.V1/V2/V3 now re-export the same Value API that PlutusLedgerApi.V1 already exposed: the read accessors (valueOf, lovelaceValueOf, unsafeLovelaceValueOf, currencySymbolValueOf, assetClassValueOf), the AssetClass type and its constructors, and the helpers flattenValue, symbols, isZero, split, scale, lovelaceValue, currencySymbol, tokenName. Previously these umbrella modules exposed only the Value type and a few constructors, so reading a value from a V2/V3 or Data.* context needed a separate import from V1.Value or V1.Data.Value. There is no per-version Value type (V2 re-exports V1.Value, V3 re-exports V2.Value, and the Data.* chain mirrors that), so the accessors apply unchanged. Widening the re-exports made three explicit imports redundant under -Werror=unused-imports; removed them: import ...V1.Value (valueOf) in cardano-loans, and the whole-module ...V1.Data.Value imports in script-contexts V1.Data and V2.Data ScriptContexts. Stacked on the unsafeLovelaceValueOf branch (#7838), which adds the accessor this widening re-exports.
unsafeLovelaceValueOfassumes that the first token of the first currency in the Value represents lovelace. Returns a silently wrong answer or fails if that's not the case. It is thus much faster thanlovelaceValueOf.Impact
Isolated budget goldens on a ledger-shaped value (ada first, then three native assets), the same input fed to both functions:
lovelaceValueOfunsafeLovelaceValueOflovelaceValueOfunsafeLovelaceValueOfBoth variants evaluate to the same result. Because both already short-circuit on the ada-first entry, the delta is purely the removed key extraction, comparisons, and lookup recursion, so it is a fixed saving independent of how many other assets the value carries. A validator that reads lovelace on every input and every output, a common shape, compounds it.
Coverage
A property test in both
Spec.V1.ValueandSpec.V1.Data.Valuechecks that on a canonical ledger-shaped valueunsafeLovelaceValueOfagrees withlovelaceValueOfand returns the exact lovelace amount. The budget numbers above come from newgoldenEvalCekCatchBudgetgoldens; I also added PIR-readable goldens so the structural difference is visible in review. Goldens are committed for GHC 9.6 and 9.12.Implements IntersectMBO/plutus-private#2304.