Skip to content

Commit

Permalink
Fix decoding of Hydra keys and ignore problematic head init (#1857)
Browse files Browse the repository at this point in the history
We were not correctly decoding Hydra verification keys (but crashing via
error) because of a missing data constructor.

This also makes the observeInitTx sort out heads that would have such
problematic party datums.

An example transaction with problematic data is:
b860a236a7e77577628bf705286d449188831cf90d714934f1cc06369c6e3953 on
preprod

---

<!-- Consider each and tick it off one way or the other -->
* [x] CHANGELOG updated or not needed
* [x] Documentation updated or not needed
* [x] Haddocks updated or not needed
* [x] No new TODOs introduced or explained herafter
  • Loading branch information
noonio authored Feb 14, 2025
2 parents aabec67 + 98f33f4 commit b6d963e
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 3 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ changes.

## [0.20.1] - UNRELEASED

- Fix a bug where decoding `Party` information from chain would crash the node
or chain observer. A problematic transaction will now be ignored and not
deemed a valid head protocol transaction. An example was if the datum would
contain CBOR instead of just hex encoded bytes.

- Stream historical data from disk in the hydra-node API server.

- Record used and free memory when running `bench-e2e` benchmark.
Expand Down
11 changes: 9 additions & 2 deletions hydra-tx/src/Hydra/Tx/Crypto.hs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import Hydra.Cardano.Api (
Key (..),
SerialiseAsCBOR,
SerialiseAsRawBytes (..),
SerialiseAsRawBytesError (..),
serialiseToRawBytesHexText,
)
import Hydra.Contract.HeadState qualified as OnChain
Expand Down Expand Up @@ -83,7 +84,10 @@ instance SerialiseAsRawBytes (Hash HydraKey) where
serialiseToRawBytes (HydraKeyHash vkh) = hashToBytes vkh

deserialiseFromRawBytes (AsHash AsHydraKey) bs =
maybe (error "TODO: SerialiseAsRawBytesError, but constructor not exported") (Right . HydraKeyHash) (hashFromBytes bs)
maybe
(Left $ SerialiseAsRawBytesError "invalid length when deserializing Hash HydraKey")
(Right . HydraKeyHash)
(hashFromBytes bs)

instance Key HydraKey where
-- Hydra verification key, which can be used to 'verify' signed messages.
Expand Down Expand Up @@ -140,7 +144,10 @@ instance SerialiseAsRawBytes (VerificationKey HydraKey) where
rawSerialiseVerKeyDSIGN vk

deserialiseFromRawBytes (AsVerificationKey AsHydraKey) bs =
maybe (error "TODO: SerialiseAsRawBytesError, but constructor not exported") (Right . HydraVerificationKey) (rawDeserialiseVerKeyDSIGN bs)
maybe
(Left $ SerialiseAsRawBytesError "invalid length when deserializing VerificationKey HydraKey")
(Right . HydraVerificationKey)
(rawDeserialiseVerKeyDSIGN bs)

instance ToJSON (VerificationKey HydraKey) where
toJSON = toJSON . serialiseToRawBytesHexText
Expand Down
7 changes: 6 additions & 1 deletion hydra-tx/src/Hydra/Tx/Init.hs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ data InitObservation = InitObservation
data NotAnInitReason
= NoHeadOutput
| NotAHeadDatum
| InvalidPartyInDatum
| NoSTFound
| NotAHeadPolicy
deriving stock (Show, Eq, Generic)
Expand Down Expand Up @@ -131,14 +132,18 @@ observeInitTx tx = do
unless (pid == HeadTokens.headPolicyId seedTxIn) $
Left NotAHeadPolicy

parties <-
maybe (Left InvalidPartyInDatum) Right $
traverse partyFromChain onChainParties

pure $
InitObservation
{ headId = mkHeadId pid
, seedTxIn
, initialThreadUTxO = (mkTxIn tx ix, toCtxUTxOTxOut headOut)
, initials
, contestationPeriod
, parties = mapMaybe partyFromChain onChainParties
, parties
, participants = assetNameToOnChainId <$> mintedTokenNames pid
}
where
Expand Down
4 changes: 4 additions & 0 deletions hydra-tx/src/Hydra/Tx/Observe.hs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ data HeadObservation
-- | Observe any Hydra head transaction.
observeHeadTx :: NetworkId -> UTxO -> Tx -> HeadObservation
observeHeadTx networkId utxo tx =
-- XXX: This is throwing away valuable information! We should be collecting
-- all "not an XX" reasons here in case we fall through and want that
-- diagnostic information in the call site of this function. Collecting errors
-- could be done with 'validation' or a similar package.
fromMaybe NoHeadTx $
either (const Nothing) (Just . Init) (observeInitTx tx)
<|> Abort <$> observeAbortTx utxo tx
Expand Down

0 comments on commit b6d963e

Please sign in to comment.