Skip to content

Commit a7ec745

Browse files
committed
cardano-rpc: Fix address serialisation.
1 parent 792f6ae commit a7ec745

5 files changed

Lines changed: 51 additions & 16 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: 1258
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/cardano-rpc.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ library
9090
build-depends:
9191
aeson,
9292
base,
93+
base16-bytestring,
9394
bytestring,
9495
cardano-api >=11.2,
9596
cardano-binary,

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

Lines changed: 12 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
@@ -36,9 +35,9 @@ import Cardano.Binary qualified as CBOR
3635

3736
import RIO hiding (toList)
3837

38+
import Data.ByteString.Base16 qualified as Base16
39+
import Data.ByteString.Char8 qualified as BSC
3940
import Data.ProtoLens (defMessage)
40-
import Data.Text qualified as T
41-
import Data.Text.Encoding qualified as T
4241
import GHC.IsList
4342
import Network.GRPC.Spec
4443

@@ -123,12 +122,12 @@ txOutToUtxoRpcTxOutput sbe (TxOut addressInEra txOutValue datum script) = do
123122
TxOutDatumInline _ hashableScriptData ->
124123
Just $
125124
defMessage
126-
& U5c.hash .~ serialiseToCBOR hashableScriptData
125+
& U5c.hash .~ serialiseToRawBytes (hashScriptDataBytes hashableScriptData)
127126
& U5c.payload .~ scriptDataToUtxoRpcPlutusData (getScriptData hashableScriptData)
128127
& U5c.originalCbor .~ getOriginalScriptDataBytes hashableScriptData
129128

130129
defMessage
131-
& U5c.address .~ T.encodeUtf8 (shelleyBasedEraConstraints sbe $ serialiseAddress addressInEra)
130+
& U5c.address .~ shelleyBasedEraConstraints sbe (serialiseToRawBytes addressInEra)
132131
& U5c.coin .~ inject (L.unCoin (txOutValueToLovelace txOutValue))
133132
& U5c.assets .~ multiAsset
134133
& U5c.maybe'datum .~ datumRpc
@@ -143,11 +142,15 @@ utxoRpcTxOutputToTxOut
143142
-> m (TxOut CtxUTxO era)
144143
utxoRpcTxOutputToTxOut txOutput = do
145144
let era = useEra @era
146-
addrUtf8 <- liftEitherError $ T.decodeUtf8' (txOutput ^. U5c.address)
145+
let addressBytes = txOutput ^. U5c.address
146+
annotateError (SerialiseAsRawBytesError msg) =
147+
SerialiseAsRawBytesError $
148+
msg <> ", address (hex): " <> BSC.unpack (Base16.encode addressBytes)
147149
address <-
148-
maybe (throwM . stringException $ "Cannot decode address: " <> T.unpack addrUtf8) pure $
149-
obtainCommonConstraints era $
150-
deserialiseAddress asType addrUtf8
150+
obtainCommonConstraints era $
151+
liftEitherError $
152+
first annotateError $
153+
deserialiseFromRawBytes asType addressBytes
151154
datum <-
152155
case txOutput ^. U5c.maybe'datum of
153156
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: 29 additions & 2 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,15 +20,37 @@ import Test.Gen.Cardano.Api.Typed
1520

1621
import Hedgehog
1722
import Hedgehog qualified as H
23+
import Hedgehog.Extras qualified as H
24+
25+
era :: Era ConwayEra
26+
era = ConwayEra
1827

1928
-- | Test if TxOut in UTXO context does roundtrip
2029
hprop_roundtrip_tx_output :: Property
2130
hprop_roundtrip_tx_output = H.property $ do
22-
let era = ConwayEra
23-
2431
txOut <- forAll $ genTxOutUTxOContext (convert era)
2532

2633
H.tripping
2734
txOut
2835
(txOutToUtxoRpcTxOutput (convert era))
2936
(first @Either displayException . utxoRpcTxOutputToTxOut)
37+
38+
-- | Test that TxOutput fields carry raw bytes on the wire
39+
hprop_tx_output_wire_format :: Property
40+
hprop_tx_output_wire_format = H.property $ do
41+
txOut@(TxOut addressInEra _ datum _) <- forAll $ genTxOutUTxOContext (convert era)
42+
43+
let protoTxOutput = txOutToUtxoRpcTxOutput (convert era) txOut
44+
45+
H.note_ "Address field carries raw ledger address bytes"
46+
protoTxOutput ^. U5c.address === serialiseToRawBytes addressInEra
47+
48+
case datum of
49+
TxOutDatumNone -> pure ()
50+
TxOutDatumHash _ scriptDataHash -> do
51+
H.note_ "Datum hash field carries the raw script data hash"
52+
protoTxOutput ^. U5c.datum . U5c.hash === serialiseToRawBytes scriptDataHash
53+
TxOutDatumInline _ hashableScriptData -> do
54+
H.note_ "Inline datum hash field carries the datum hash, not the datum CBOR"
55+
protoTxOutput ^. U5c.datum . U5c.hash
56+
=== serialiseToRawBytes (hashScriptDataBytes hashableScriptData)

0 commit comments

Comments
 (0)