Skip to content

Commit a848339

Browse files
committed
cardano-rpc: Fix address serialisation.
1 parent 8966469 commit a848339

4 files changed

Lines changed: 42 additions & 14 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
project: cardano-rpc
2+
pr: 0
3+
kind:
4+
- bugfix
5+
description: |
6+
Fix wire encoding of two TxOutput fields: address now carries raw ledger address bytes instead of bech32/base58 text, and Datum.hash now carries the 32-byte datum hash instead of the datum CBOR for inline datums, matching other UTxO RPC implementations.

cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Type/TxOutput.hs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ module Cardano.Rpc.Server.Internal.UtxoRpc.Type.TxOutput
1414
)
1515
where
1616

17-
import Cardano.Api.Address
1817
import Cardano.Api.Era
1918
import Cardano.Api.Error
2019
import Cardano.Api.Experimental.Era
@@ -37,8 +36,6 @@ import Cardano.Binary qualified as CBOR
3736
import RIO hiding (toList)
3837

3938
import Data.ProtoLens (defMessage)
40-
import Data.Text qualified as T
41-
import Data.Text.Encoding qualified as T
4239
import GHC.IsList
4340
import Network.GRPC.Spec
4441

@@ -123,12 +120,12 @@ txOutToUtxoRpcTxOutput sbe (TxOut addressInEra txOutValue datum script) = do
123120
TxOutDatumInline _ hashableScriptData ->
124121
Just $
125122
defMessage
126-
& U5c.hash .~ serialiseToCBOR hashableScriptData
123+
& U5c.hash .~ serialiseToRawBytes (hashScriptDataBytes hashableScriptData)
127124
& U5c.payload .~ scriptDataToUtxoRpcPlutusData (getScriptData hashableScriptData)
128125
& U5c.originalCbor .~ getOriginalScriptDataBytes hashableScriptData
129126

130127
defMessage
131-
& U5c.address .~ T.encodeUtf8 (shelleyBasedEraConstraints sbe $ serialiseAddress addressInEra)
128+
& U5c.address .~ shelleyBasedEraConstraints sbe (serialiseToRawBytes addressInEra)
132129
& U5c.coin .~ inject (L.unCoin (txOutValueToLovelace txOutValue))
133130
& U5c.assets .~ multiAsset
134131
& U5c.maybe'datum .~ datumRpc
@@ -143,11 +140,10 @@ utxoRpcTxOutputToTxOut
143140
-> m (TxOut CtxUTxO era)
144141
utxoRpcTxOutputToTxOut txOutput = do
145142
let era = useEra @era
146-
addrUtf8 <- liftEitherError $ T.decodeUtf8' (txOutput ^. U5c.address)
147143
address <-
148-
maybe (throwM . stringException $ "Cannot decode address: " <> T.unpack addrUtf8) pure $
149-
obtainCommonConstraints era $
150-
deserialiseAddress asType addrUtf8
144+
obtainCommonConstraints era $
145+
liftEitherError $
146+
deserialiseFromRawBytes asType (txOutput ^. U5c.address)
151147
datum <-
152148
case txOutput ^. U5c.maybe'datum of
153149
Just datumRpc ->

cardano-rpc/test/cardano-rpc-test/Test/Cardano/Rpc/FetchBlockTx.hs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ module Test.Cardano.Rpc.FetchBlockTx where
88

99
import Cardano.Api (SlotNo (..))
1010
import Cardano.Api.Address
11-
( serialiseAddress
12-
, toShelleyAddr
11+
( toShelleyAddr
1312
, toShelleyStakeAddr
1413
, toShelleyStakeCredential
1514
)
@@ -31,7 +30,6 @@ import RIO hiding (toList)
3130

3231
import Data.Map.Strict qualified as M
3332
import Data.ProtoLens (decodeMessage, encodeMessage)
34-
import Data.Text.Encoding qualified as T
3533
import GHC.IsList (fromList, toList)
3634
import GHC.Stack (withFrozenCallStack)
3735
import Network.GRPC.Spec (Proto (..))
@@ -96,7 +94,7 @@ txToUtxoRpcTxProjections sbe = H.withTests 40 . H.property $ anyEraTxConstraints
9694
expectedAddress ledgerOutput =
9795
case fromShelleyTxOut sbe ledgerOutput of
9896
TxOut addressInEra _ _ _ ->
99-
shelleyBasedEraConstraints sbe $ T.encodeUtf8 (serialiseAddress addressInEra)
97+
shelleyBasedEraConstraints sbe $ serialiseToRawBytes addressInEra
10098
map (\o -> (o ^. U5c.address, o ^. U5c.coin)) protoOutputs
10199
=== map
102100
(\o -> (expectedAddress o, inject $ o ^. L.coinTxOutL))
@@ -325,7 +323,7 @@ hprop_tx_to_utxorpc_tx_injected_optional_fields = H.withTests 10 . H.property $
325323
totalCollateral === inject (L.Coin totalCollateralCoin)
326324
collateralReturn <- H.nothingFail $ collateral ^. U5c.maybe'collateralReturn
327325
collateralReturn ^. U5c.address
328-
=== shelleyBasedEraConstraints sbe (T.encodeUtf8 (serialiseAddress returnAddress))
326+
=== shelleyBasedEraConstraints sbe (serialiseToRawBytes returnAddress)
329327
collateralReturn ^. U5c.coin === inject (L.Coin returnCoin)
330328

331329
H.note_ "The proposal carries the injected deposit, return account, anchor and action"

cardano-rpc/test/cardano-rpc-test/Test/Cardano/Rpc/TxOutput.hs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
{-# LANGUAGE GADTs #-}
12
{-# LANGUAGE RankNTypes #-}
23
{-# LANGUAGE ScopedTypeVariables #-}
34
{-# LANGUAGE TypeApplications #-}
45

56
module Test.Cardano.Rpc.TxOutput where
67

78
import Cardano.Api.Experimental.Era
9+
import Cardano.Api.Plutus (hashScriptDataBytes)
10+
import Cardano.Api.Serialise.Raw
11+
import Cardano.Api.Tx
12+
import Cardano.Rpc.Proto.Api.UtxoRpc.Query qualified as U5c
813
import Cardano.Rpc.Server.Internal.UtxoRpc.Type
914

1015
import RIO
@@ -15,6 +20,7 @@ import Test.Gen.Cardano.Api.Typed
1520

1621
import Hedgehog
1722
import Hedgehog qualified as H
23+
import Hedgehog.Extras qualified as H
1824

1925
-- | Test if TxOut in UTXO context does roundtrip
2026
hprop_roundtrip_tx_output :: Property
@@ -27,3 +33,25 @@ hprop_roundtrip_tx_output = H.property $ do
2733
txOut
2834
(txOutToUtxoRpcTxOutput (convert era))
2935
(first @Either displayException . utxoRpcTxOutputToTxOut)
36+
37+
-- | Test that TxOutput fields carry raw bytes on the wire
38+
hprop_tx_output_wire_format :: Property
39+
hprop_tx_output_wire_format = H.property $ do
40+
let era = ConwayEra
41+
42+
txOut@(TxOut addressInEra _ datum _) <- forAll $ genTxOutUTxOContext (convert era)
43+
44+
let protoTxOutput = txOutToUtxoRpcTxOutput (convert era) txOut
45+
46+
H.note_ "Address field carries raw ledger address bytes"
47+
protoTxOutput ^. U5c.address === serialiseToRawBytes addressInEra
48+
49+
case datum of
50+
TxOutDatumNone -> pure ()
51+
TxOutDatumHash _ scriptDataHash -> do
52+
H.note_ "Datum hash field carries the raw script data hash"
53+
protoTxOutput ^. U5c.datum . U5c.hash === serialiseToRawBytes scriptDataHash
54+
TxOutDatumInline _ hashableScriptData -> do
55+
H.note_ "Inline datum hash field carries the datum hash, not the datum CBOR"
56+
protoTxOutput ^. U5c.datum . U5c.hash
57+
=== serialiseToRawBytes (hashScriptDataBytes hashableScriptData)

0 commit comments

Comments
 (0)