Skip to content

Migrate Rpc.Transaction test to Cardano.Api.Experimental API #6491

@carbolymer

Description

@carbolymer

Summary

The RPC transaction integration test (cardano-testnet/test/.../Rpc/Transaction.hs) currently uses the old Cardano.Api transaction-building API. It should be migrated to the new Cardano.Api.Experimental API to:

  • Align with the direction of cardano-api (old API is being deprecated)
  • Use ledger-aligned types directly, reducing impedance mismatch
  • Serve as a reference example for RPC + experimental API integration

Current old-API patterns to replace

The test currently uses these old-API constructs:

  1. Era handling: ConwayEraOnwards/ShelleyBasedEra with convert — replace with Exp.Era (Exp.ConwayEra)
  2. TxOut construction: TxOut addr (lovelaceToTxOutValue sbe coin) TxOutDatumNone ReferenceScriptNone — replace with Exp.TxOut (Ledger.mkBasicTxOut ledgerAddr value)
  3. TxBodyContent: defaultTxBodyContent sbe with old setters and wrapper types (BuildTxWith, TxFeeExplicit, KeyWitness KeyWitnessForSpending, LedgerProtocolParameters) — replace with Exp.defaultTxBodyContent + experimental setters using direct ledger types
  4. Tx creation: createTransactionBody sbe content — replace with Exp.makeUnsignedTx era content
  5. Tx signing: signShelleyTransaction sbe txBody [wit0] — replace with Exp.makeKeyWitness era unsignedTx wit0 then Exp.signTx era [] [keyWit] unsignedTx
  6. TxId extraction: getTxId $ getTxBody signedTx — replace with Exp.hashTxBody (ledgerTx ^. L.bodyTxL) or equivalent
  7. Serialization: serialiseToCBOR signedTx — replace with serialiseToRawBytes (Exp.SignedTx ledgerTx)

Migration plan

Step 1: Update imports

  • Add import qualified Cardano.Api.Experimental as Exp
  • Add import qualified Cardano.Api.Experimental.Tx as Exp
  • Keep Cardano.Api for shared types still needed (address deserialization, TxIn, TxIx, ShelleyWitnessSigningKey, etc.)
  • Keep Cardano.Api.Ledger for direct ledger types (Addr, MaryValue, Coin, etc.)

Step 2: Update era handling

  • Replace (ceo, eraProxy) = (conwayBasedEra, asType) with era = Exp.ConwayEra
  • Derive sbe = Exp.convert era only where testnet infrastructure still requires ShelleyBasedEra (e.g., cardanoNodeEra = AnyShelleyBasedEra sbe)
  • Address deserialization can stay as old-API (deserialiseAddress), converting to ledger Addr via toShelleyAddr where needed for tx outputs

Step 3: Update transaction building

Replace old-style TxBodyContent construction:

-- OLD
let content =
      defaultTxBodyContent sbe
        & setTxIns [(txIn0, pure $ KeyWitness KeyWitnessForSpending)]
        & setTxFee (TxFeeExplicit sbe (L.Coin fee))
        & setTxOuts [TxOut addr1 (lovelaceToTxOutValue sbe $ L.Coin amount) TxOutDatumNone ReferenceScriptNone, ...]
        & setTxProtocolParams (pure . pure $ LedgerProtocolParameters pparams)

With experimental-style:

-- NEW
let ledgerAddr0 = toShelleyAddr addr0
    ledgerAddr1 = toShelleyAddr addr1
    mkOut addr coin = Exp.obtainCommonConstraints era $
      Exp.TxOut (Ledger.mkBasicTxOut addr (Ledger.valueFromList coin []))
    content =
      Exp.defaultTxBodyContent
        & Exp.setTxIns [(txIn0, Exp.AnyKeyWitnessPlaceholder)]
        & Exp.setTxFee (L.Coin fee)
        & Exp.setTxOuts [mkOut ledgerAddr1 amount, mkOut ledgerAddr0 change]
        & Exp.setTxProtocolParams (Just pparams)

Step 4: Update transaction creation and signing

-- OLD
txBody <- H.leftFail $ createTransactionBody sbe content
let signedTx = signShelleyTransaction sbe txBody [wit0]

-- NEW
let unsignedTx = Exp.makeUnsignedTx era content
    keyWit = Exp.makeKeyWitness era unsignedTx wit0
    Exp.SignedTx signedLedgerTx = Exp.signTx era [] [keyWit] unsignedTx

Step 5: Update TxId extraction and serialization

-- OLD
txId' <- H.noteShow . getTxId $ getTxBody signedTx
... serialiseToCBOR signedTx

-- NEW  
txId' <- H.noteShow $ Exp.hashTxBody (signedLedgerTx ^. L.bodyTxL)
... serialiseToRawBytes (Exp.SignedTx signedLedgerTx)

Step 6: Verify utxoRpcPParamsToProtocolParams compatibility

utxoRpcPParamsToProtocolParams already takes Era era and returns L.PParams (ShelleyLedgerEra era) (ledger PParams), so it's already compatible with the experimental API. The call site change is minimal — pass era directly instead of convert ceo.

Notes

  • The testnet infrastructure (TestnetRuntime, createAndRunTestnet, etc.) still uses AnyShelleyBasedEra, so sbe = Exp.convert era will still be needed for the testnet options. This is not part of this migration scope.
  • Address deserialization from RPC responses (deserialiseAddressBs) uses old-API AddressInEra. These can either be kept (with toShelleyAddr conversion) or replaced with direct ledger address deserialization if available.
  • The helper functions txoRefToTxIn and deserialiseAddressBs use old-API types (TxIn, SerialiseAddress) and can remain as-is since they're about deserialization, not tx building.
  • Reference test for the experimental API pattern: cardano-api/test/cardano-api-test/Test/Cardano/Api/Experimental.hs

Test plan

  • Migrate the code following the steps above
  • Build successfully with cabal build cardano-testnet-test
  • Run the test: TASTY_PATTERN='/RPC Transaction Submit/' cabal test cardano-testnet-test
  • Verify the transaction is still submitted and confirmed correctly via RPC

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions