Skip to content

Commit

Permalink
Merge pull request #1129 from input-output-hk/submit-tx-in-cbor
Browse files Browse the repository at this point in the history
Accept three types of encoding in submit-transaction endpoint
  • Loading branch information
v0d1ch authored Oct 19, 2023
2 parents 7175e6c + c2a07c1 commit 00ca1e2
Show file tree
Hide file tree
Showing 7 changed files with 1,161 additions and 849 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ changes.

## [0.14.0] - UNRELEASED

- Hydra node API `submit-transaction` endpoint now accepts three types of
encoding: Base16 encoded CBOR string, TextEnvelope type and JSON.

- **BREAKING** Introduce messages resending logic in the `Network`
layer to improve reliability in the face of transient connection
issues
Expand Down
10 changes: 6 additions & 4 deletions hydra-cluster/src/Hydra/Cluster/Scenarios.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import Hydra.API.HTTPServer (
DraftCommitTxRequest (..),
DraftCommitTxResponse (..),
ScriptInfo (..),
SubmitTxRequest (..),
TransactionSubmitted (..),
TxOutWithWitness (..),
)
Expand All @@ -49,6 +48,7 @@ import Hydra.Cardano.Api (
mkVkAddress,
selectLovelace,
signTx,
toLedgerTx,
toScriptData,
writeFileTextEnvelope,
)
Expand Down Expand Up @@ -419,7 +419,7 @@ canSubmitTransactionThroughAPI ::
TxId ->
IO ()
canSubmitTransactionThroughAPI tracer workDir node hydraScriptsTxId =
(`finally` returnFundsToFaucet tracer node Alice) $ do
(`finally` returnFundsToFaucet tracer node Alice) $ do
refuelIfNeeded tracer node Alice 25_000_000
aliceChainConfig <- chainConfigFor Alice workDir nodeSocket [] $ UnsafeContestationPeriod 100
let hydraNodeId = 1
Expand All @@ -442,14 +442,16 @@ canSubmitTransactionThroughAPI tracer workDir node hydraScriptsTxId =
Left e -> failure $ show e
Right body -> do
let unsignedTx = makeSignedTransaction [] body
let unsignedRequest = SubmitTxRequest{txToSubmit = unsignedTx}
let unsignedRequest = toJSON $ toLedgerTx unsignedTx
sendRequest hydraNodeId unsignedRequest
`shouldThrow` expectErrorStatus 400 (Just "MissingVKeyWitnessesUTXOW")

let signedRequest = SubmitTxRequest{txToSubmit = signTx cardanoBobSk unsignedTx}
let signedTx = signTx cardanoBobSk unsignedTx
let signedRequest = toJSON $ toLedgerTx signedTx
(sendRequest hydraNodeId signedRequest <&> responseBody)
`shouldReturn` TransactionSubmitted
where

RunningNode{networkId, nodeSocket} = node
sendRequest hydraNodeId tx =
runReq defaultHttpConfig $
Expand Down
1,114 changes: 1,114 additions & 0 deletions hydra-node/golden/ReasonablySized (SubmitTxRequest (Tx BabbageEra)).json

Large diffs are not rendered by default.

828 changes: 0 additions & 828 deletions hydra-node/golden/ReasonablySized SubmitTxRequest.json

This file was deleted.

6 changes: 5 additions & 1 deletion hydra-node/json-schemas/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,12 @@ channels:
message:
summary: |
Cardano transaction to be submitted to the L1 network.
Accepts transactions encoded as Base16 CBOR string, TextEnvelope type or JSON.
payload:
$ref: "api.yaml#/components/schemas/Transaction"
oneOf:
- $ref: "api.yaml#/components/schemas/RawTransaction"
- $ref: "api.yaml#/components/schemas/TextEnvelopeTransaction"
- $ref: "api.yaml#/components/schemas/Transaction"
bindings:
http:
type: response
Expand Down
16 changes: 5 additions & 11 deletions hydra-node/src/Hydra/API/HTTPServer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -120,18 +120,12 @@ instance Arbitrary DraftCommitTxRequest where
shrink = \case
DraftCommitTxRequest u -> DraftCommitTxRequest <$> shrink u

newtype SubmitTxRequest = SubmitTxRequest
{ txToSubmit :: Tx
newtype SubmitTxRequest tx = SubmitTxRequest
{ txToSubmit :: tx
}
deriving stock (Eq, Show, Generic)
deriving newtype (Eq, Show, Arbitrary)
deriving newtype (ToJSON, FromJSON)

instance Arbitrary SubmitTxRequest where
arbitrary = genericArbitrary

shrink = \case
SubmitTxRequest u -> SubmitTxRequest <$> shrink u

data TransactionSubmitted = TransactionSubmitted
deriving stock (Eq, Show, Generic)

Expand Down Expand Up @@ -302,10 +296,10 @@ handleSubmitUserTx ::
LBS.ByteString ->
IO Response
handleSubmitUserTx directChain body = do
case Aeson.eitherDecode' body :: Either String SubmitTxRequest of
case Aeson.eitherDecode' body :: Either String Tx of
Left err ->
pure $ responseLBS status400 [] (Aeson.encode $ Aeson.String $ pack err)
Right SubmitTxRequest{txToSubmit} -> do
Right txToSubmit -> do
try (submitTx txToSubmit) <&> \case
Left (e :: PostTxError Tx) -> return400 e
Right _ ->
Expand Down
33 changes: 28 additions & 5 deletions hydra-node/test/Hydra/API/HTTPServerSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,28 @@ module Hydra.API.HTTPServerSpec where
import Hydra.Prelude hiding (get)
import Test.Hydra.Prelude

import Data.Aeson (encode)
import Cardano.Binary (serialize')
import Data.Aeson (Result (Error, Success), Value (String), encode, fromJSON)
import Data.Aeson.Lens (key, nth)
import Hydra.API.HTTPServer (DraftCommitTxRequest, DraftCommitTxResponse, SubmitTxRequest, TransactionSubmitted, httpApp)
import qualified Data.ByteString.Base16 as Base16
import Hydra.API.HTTPServer (DraftCommitTxRequest, DraftCommitTxResponse, SubmitTxRequest (..), TransactionSubmitted, httpApp)
import Hydra.API.ServerSpec (dummyChainHandle)
import Hydra.Cardano.Api (toLedgerTx, serialiseToTextEnvelope)
import Hydra.Chain.Direct.Fixture (defaultPParams)
import Hydra.Chain.Direct.State ()
import Hydra.JSONSchema (prop_validateJSONSchema)
import Hydra.Ledger.Cardano (Tx)
import Hydra.Logging (nullTracer)
import Test.Aeson.GenericSpecs (roundtripAndGoldenSpecs)
import Test.Hspec.Wai (MatchBody (..), ResponseMatcher (matchBody), get, shouldRespondWith, with)
import Test.QuickCheck.Property (property, withMaxSuccess)
import Test.QuickCheck.Property (counterexample, forAll, property, withMaxSuccess)

spec :: Spec
spec = do
parallel $ do
roundtripAndGoldenSpecs (Proxy @(ReasonablySized DraftCommitTxResponse))
roundtripAndGoldenSpecs (Proxy @(ReasonablySized DraftCommitTxRequest))
roundtripAndGoldenSpecs (Proxy @(ReasonablySized SubmitTxRequest))
roundtripAndGoldenSpecs (Proxy @(ReasonablySized (SubmitTxRequest Tx)))
roundtripAndGoldenSpecs (Proxy @(ReasonablySized TransactionSubmitted))

prop "Validate /commit publish api schema" $
Expand All @@ -40,7 +44,7 @@ spec = do
prop "Validate /cardano-transaction publish api schema" $
property $
withMaxSuccess 1 $
prop_validateJSONSchema @SubmitTxRequest "api.json" $
prop_validateJSONSchema @(SubmitTxRequest Tx) "api.json" $
key "channels"
. key "/cardano-transaction"
. key "publish"
Expand All @@ -60,6 +64,25 @@ spec = do
. key "payload"

apiServerSpec
describe "SubmitTxRequest accepted tx formats" $ do
prop "accepts Base16 cbor encoded bytestring" $
forAll (arbitrary @Tx) $ \tx ->
let json = String $ decodeUtf8 $ Base16.encode $ serialize' (toLedgerTx tx)
in case fromJSON @(SubmitTxRequest Tx) json of
Success{} -> property True
Error e -> counterexample (toString $ toText e) $ property False
prop "accepts json encoded transaction" $
forAll (arbitrary @Tx) $ \tx ->
let json = toJSON (toLedgerTx tx)
in case fromJSON @(SubmitTxRequest Tx) json of
Success{} -> property True
Error e -> counterexample (toString $ toText e) $ property False
prop "accepts transaction encoded as TextEnvelope" $
forAll (arbitrary @Tx) $ \tx ->
let json = toJSON $ serialiseToTextEnvelope Nothing tx
in case fromJSON @(SubmitTxRequest Tx) json of
Success{} -> property True
Error e -> counterexample (toString $ toText e) $ property False

-- TODO: we should add more tests for other routes here (eg. /commit)
apiServerSpec :: Spec
Expand Down

0 comments on commit 00ca1e2

Please sign in to comment.