Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kupo x Hydra #117

Merged
merged 23 commits into from
Oct 13, 2023
Merged

Kupo x Hydra #117

merged 23 commits into from
Oct 13, 2023

Conversation

KtorZ
Copy link
Member

@KtorZ KtorZ commented Apr 19, 2023

🐉 Adds --hydra-host and --hydra-port command line options to connect and index the head state of a hydra-node

🐉 Internally, Hydra is just another ChainProducer and HeadIsOpen and SnapshotConfirmed messages are mapped to rollforward of blocks.

TODO:

  • Finish Hydra decoders (missing datum, script and metadata)
  • Handle intersection negotiation by skipping blocks
  • Many tests assume a busy network and fail because of this. Make it possible to run them against a busy hydra-node easily (e.g. re-using the benchmarks in https://github.com/input-output-hk/hydra)
  • TBD: Clear index state when seeing a HeadIsClosed message? Use a rollbackward?

@KtorZ KtorZ self-assigned this Apr 19, 2023
@ch1bo ch1bo self-assigned this Sep 19, 2023
@ch1bo ch1bo linked an issue Sep 20, 2023 that may be closed by this pull request
2 tasks
ch1bo and others added 6 commits September 22, 2023 10:03
This way the default is still to compile with -Werror, but it allows a
local override in a cabal.project.local file with:

  constraints:
    kupo -production
We need to keep the transactions around until a SnapshotConfirmed will
yield a "block" containing them.
This should make much of a difference, but is a more idiomatic way to
store things by key.
@ch1bo ch1bo force-pushed the ensemble/hydra-producer branch 2 times, most recently from cc666a9 to 09e560e Compare September 25, 2023 11:06
ch1bo and others added 7 commits September 25, 2023 17:20
We are re-using the ServerOutput golden files from the hydra repository
to test compatibility with the hydra-node websocket API.
- Stub for TxValid decoder by using empty values (apart from txId)
- Add confirmedTransactionIds parsing for Snapshot
This should already give use enough information to index some UTxO in
the head.
Simulates transactions for each UTxO in the initial snapshot to make
kupo index this "genesis" block.
The decoder is copy & pasted from ogmios with only ada/lovelace needing
replacing. We should DRY this (or change our apis to be compatible).
--since origin \
--match * \
--workdir ./db
```
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

-> [Point]
-> WS.Connection
-> m IntersectionNotFoundException
runChainSyncClient mailbox beforeMainLoop _pts ws = do
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're still doing nothing with the intersection points here, which means that we'll constantly re-index everything from scratch. This might re-insert previously deleted UTxO.

If I recall correctly, we discussed that a while back and settled on simply "skipping" messages from Hydra here until the intersection point as a first 'naive' implementation and until Hydra provides more reliable mechanisms for negotiating an intersection.

HeadIsOpen{genesisTxs} ->
atomically (putHighFrequencyMessage mailbox (mkHydraBlock 0 genesisTxs))
TxValid{tx} ->
pushTx tx
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose that this will work for lack of better options, but this will cause Kupo's memory to grow possibly unbounded.

Transactions are 'popped' on snapshot confirmed, which means that the memory should likely just grow and shrink on every snapshot, but that also means that any non-confirmed transaction will sit in the memory forever.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true. It's not ideal, but necessary given the current API. We should, however, record kupo's use case for a need of a more "complete" SnapshotConfirmed message.. there is an ADR about making the Hydra API more resource based: cardano-scaling/hydra#1028.

pushTx :: PartialTransaction -> m ()
, -- | Resolves a transaction id and removes it. Throws
-- 'TransactionNotInStore' when not found.
popTxById :: MonadThrow m => TransactionId -> m PartialTransaction
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: it might be better to take a Set TransactionId to avoid the repeated readTVar/writeTVar since transactions are always removed in batches from the store.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In practice, hydra will snapshot after every transaction. So rarely we would receive a SnapshotConfirmed with more than one tx inside. So batching things up is likely not going to be of big impact and I'd argue the singular API is a bit easier to use?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue the singular API is a bit easier to use?

I'd argue for the opposite; taking a foldable makes the API more generic and thus more easily composable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've solved your argument by accepting a [PartialTransaction] for popTxByIds ;)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked at that module yet. But it seems like it's mostly about the Hydra's API and the underlying plumbing.

tx' <- popTxById txId
tx' `shouldBe` tx
popTxById txId `shouldThrow` \case
TransactionNotInStore txId' -> txId' == txId
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small note: this test is 'misplaced' as it's testing stuff from Kupo.App.ChainSync.Hydra, not from Kupo.Data.Hydra.

I can move things around eventually, so consider this is just a note for myself 😅

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's have @v0d1ch do it? :P

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to appropriate place.

  So that we remove many elements from the store in a single atomic transaction.
  We do this by skipping blocks because we don't really have much alternative. This is therefore relatively unsound for very long running head. Though is it more sound that actually replaying all events from that head onto an existing database; so it's kind of a best effort solution.
@KtorZ KtorZ merged commit b126d23 into master Oct 13, 2023
1 check passed
@ch1bo ch1bo deleted the ensemble/hydra-producer branch October 30, 2023 11:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Basic indexer integration with Kupo
3 participants