├── .editorconfig ├── .gitattributes ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ └── haddock.yml ├── .gitignore ├── .hlint.yaml ├── .readthedocs.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.adoc ├── LICENSE ├── NOTICE ├── README.adoc ├── STYLEGUIDE.adoc ├── benchmark ├── README.md └── marconi-dashboard.json ├── cabal.project ├── config └── cardano-node │ ├── mainnet │ ├── alonzo-genesis.json │ ├── byron-genesis.json │ ├── config.json │ ├── conway-genesis.json │ ├── shelley-genesis.json │ └── topology.json │ ├── preprod │ ├── alonzo-genesis.json │ ├── byron-genesis.json │ ├── config.json │ ├── conway-genesis.json │ ├── shelley-genesis.json │ └── topology.json │ └── preview │ ├── alonzo-genesis.json │ ├── byron-genesis.json │ ├── config.json │ ├── conway-genesis.json │ ├── shelley-genesis.json │ └── topology.json ├── doc └── read-the-docs-site │ ├── LICENSE │ ├── NOTICE │ ├── README.md │ ├── _static │ ├── .gitkeep │ └── theme_overrides.css │ ├── _templates │ └── .gitkeep │ ├── adr │ ├── 0001-record-architecture-decisions.rst │ ├── 0002-indexer-resuming-strategy.rst │ ├── 0003-observability.rst │ ├── 0004-marconi-sidechain-testing-strategy.rst │ └── index.rst │ ├── architecture │ ├── appendix.rst │ ├── building-blocks-view.rst │ ├── comparison-other-chain-indexing-projects.rst │ ├── index.rst │ ├── local-node-communication.rst │ ├── marconi-as-a-node.rst │ ├── marconi-core-building-blocks.rst │ ├── repository-structure.rst │ ├── runtime-view.rst │ └── system-context.rst │ ├── cardano-logo.png │ ├── conf.py │ ├── default.nix │ ├── doc │ ├── introduction.rst │ ├── marconi-as-a-library │ │ ├── additional-links.rst │ │ ├── cookbook │ │ │ └── index.rst │ │ ├── index.rst │ │ ├── overview.rst │ │ ├── quickstart.rst │ │ └── tutorials │ │ │ ├── BasicApp.hs │ │ │ ├── how-to-query-first-indexer.rst │ │ │ ├── how-to-reuse-existing-indexers.rst │ │ │ ├── how-to-run-several-indexers.rst │ │ │ ├── how-to-test-first-indexer.rst │ │ │ ├── index.rst │ │ │ └── write-first-indexer.rst │ └── marconi-cardano-chain-index │ │ ├── index.rst │ │ ├── openapi-spec.rst │ │ ├── overview.rst │ │ └── quickstart.rst │ ├── exts │ └── hs_domain.py │ ├── index.rst │ ├── marconi-doc.cabal │ └── requirements.txt ├── flake.lock ├── flake.nix ├── fourmolu.yaml ├── marconi-cardano-chain-index ├── CHANGELOG.md ├── LICENSE ├── NOTICE ├── README.md ├── README_MARCONI_DRAFT.md ├── README_alpha-release.md ├── app │ └── Main.hs ├── cbits │ ├── cbits │ │ └── rev.c │ └── rev.c ├── changelog.d │ ├── 20230403_070417_nicolas.biri_current_indexed_slot.md │ ├── 20230417_103501_nicolas.biri_error_handling.md │ ├── 20230519_180000_kayvank_deprecated_indexers.md │ ├── 20230523_082120_nicolas.biri_future_spent.md │ └── scriv.ini ├── marconi-cardano-chain-index.cabal ├── snapshot │ └── Main.hs ├── src │ └── Marconi │ │ └── Cardano │ │ └── ChainIndex │ │ ├── Api │ │ ├── HttpServer.hs │ │ ├── JsonRpc │ │ │ ├── Endpoint │ │ │ │ ├── CurrentSyncedBlock.hs │ │ │ │ ├── CurrentSyncedBlock │ │ │ │ │ └── Tip.hs │ │ │ │ ├── Echo.hs │ │ │ │ ├── EpochState.hs │ │ │ │ ├── MintBurnToken.hs │ │ │ │ ├── TargetAddresses.hs │ │ │ │ ├── Utxo.hs │ │ │ │ └── Utxo │ │ │ │ │ ├── SpentInfoResult.hs │ │ │ │ │ ├── Types.hs │ │ │ │ │ └── Wrappers.hs │ │ │ ├── Routes.hs │ │ │ └── Server.hs │ │ ├── Rest │ │ │ ├── Endpoint │ │ │ │ ├── Metrics.hs │ │ │ │ ├── Params.hs │ │ │ │ ├── TargetAddresses.hs │ │ │ │ └── Time.hs │ │ │ ├── Routes.hs │ │ │ └── Server.hs │ │ ├── Routes.hs │ │ └── Types.hs │ │ ├── CLI.hs │ │ ├── Error.hs │ │ ├── Git │ │ ├── Rev.hs │ │ └── RevFromGit.hs │ │ ├── Indexers.hs │ │ ├── Run.hs │ │ ├── SecurityParam.hs │ │ └── Snapshot │ │ └── Run.hs ├── test-lib │ └── Test │ │ └── Marconi │ │ └── Cardano │ │ └── ChainIndex │ │ ├── Api │ │ ├── HttpServer.hs │ │ └── JsonRpc.hs │ │ ├── CLI.hs │ │ └── Indexers.hs └── test │ ├── Spec.hs │ └── Spec │ ├── Golden │ ├── CLIInputValidation │ │ ├── invalidAddress-MissingBech32Prefix.golden │ │ ├── invalidAddress-badFormat.golden │ │ ├── invalidAddressPrefix-stake1.golden │ │ ├── invalidAddressPrefix-stake_test.golden │ │ ├── invalidAddress_1CharShort.golden │ │ ├── invalidAssetId-assetName-invalidBase16Format.golden │ │ ├── invalidAssetId-assetName-invalidBase16Length.golden │ │ ├── invalidAssetId-assetName-tooLong.golden │ │ ├── invalidAssetId-policyId-1ByteLong.golden │ │ ├── invalidAssetId-policyId-1ByteShort.golden │ │ ├── invalidAssetId-policyId-1HexCharLong.golden │ │ ├── invalidAssetId-policyId-1HexCharShort.golden │ │ └── invalidAssetId-policyId-badBase16Format.golden │ ├── Cli │ │ ├── marconi-cardano-chain-index___disable_address_data.help │ │ └── marconi-cardano-chain-index___help.help │ └── Routes │ │ ├── address-utxo-response.json │ │ ├── current-synced-point-response-1.json │ │ ├── current-synced-point-response-2.json │ │ ├── epoch-nonce-at-genesis-response.json │ │ ├── epoch-nonce-byron-response.json │ │ ├── epoch-nonce-response.json │ │ ├── epoch-stakepooldelegation-at-genesis-response.json │ │ ├── epoch-stakepooldelegation-response.json │ │ └── mintingpolicyhash-tx-response.json │ └── Marconi │ └── Cardano │ └── ChainIndex │ ├── Api │ ├── Gen.hs │ └── Routes.hs │ ├── CLI.hs │ └── CLIInputValidation.hs ├── marconi-cardano-core ├── CHANGELOG.md ├── LICENSE ├── NOTICE ├── cardano-api-extended │ └── src │ │ └── Cardano │ │ └── Api │ │ ├── Extended.hs │ │ └── Extended │ │ ├── Block.hs │ │ ├── ExtLedgerState.hs │ │ ├── Gen.hs │ │ ├── IPC.hs │ │ ├── Shelley.hs │ │ ├── Streaming.hs │ │ └── Streaming │ │ ├── Callback.hs │ │ └── ChainSyncEvent.hs ├── marconi-cardano-core.cabal ├── src │ └── Marconi │ │ └── Cardano │ │ └── Core │ │ ├── Extract │ │ └── WithDistance.hs │ │ ├── Indexer │ │ └── Worker.hs │ │ ├── Logger.hs │ │ ├── Node │ │ └── Client │ │ │ └── Retry.hs │ │ ├── Orphans.hs │ │ ├── Runner.hs │ │ ├── Transformer │ │ ├── WithSyncStats.hs │ │ └── WithSyncStats │ │ │ └── Backend │ │ │ ├── Printer.hs │ │ │ └── Prometheus.hs │ │ └── Types.hs ├── test-lib │ └── Test │ │ └── Gen │ │ └── Marconi │ │ └── Cardano │ │ └── Core │ │ ├── Helpers.hs │ │ ├── Mockchain.hs │ │ └── Types.hs └── test │ ├── Spec.hs │ └── Spec │ └── Marconi │ └── Cardano │ └── Core │ ├── Logger.hs │ ├── Logger │ └── Golden │ │ ├── start-from-genesis-logging.txt │ │ └── start-from-later-point-logging.txt │ └── Orphans.hs ├── marconi-cardano-indexers ├── CHANGELOG.md ├── LICENSE ├── NOTICE ├── marconi-cardano-indexers.cabal ├── src │ └── Marconi │ │ └── Cardano │ │ └── Indexers │ │ ├── BlockInfo.hs │ │ ├── ChainTip.hs │ │ ├── Coordinator.hs │ │ ├── CurrentSyncPointQuery.hs │ │ ├── Datum.hs │ │ ├── EpochNonce.hs │ │ ├── EpochSDD.hs │ │ ├── ExtLedgerStateCoordinator.hs │ │ ├── MintTokenEvent.hs │ │ ├── MintTokenEventQuery.hs │ │ ├── SnapshotBlockEvent.hs │ │ ├── Spent.hs │ │ ├── SyncHelper.hs │ │ ├── Utxo.hs │ │ └── UtxoQuery.hs ├── test-lib │ └── Test │ │ ├── Gen │ │ └── Marconi │ │ │ └── Cardano │ │ │ └── Indexers │ │ │ ├── BlockInfo.hs │ │ │ ├── Datum.hs │ │ │ ├── MintTokenEvent.hs │ │ │ ├── Spent.hs │ │ │ ├── Utxo.hs │ │ │ └── UtxoQuery.hs │ │ ├── Helpers.hs │ │ ├── Integration.hs │ │ └── Marconi │ │ └── Cardano │ │ ├── Chain │ │ └── Snapshot.hs │ │ └── DbSyncComparison │ │ ├── BlockInfoResult.hs │ │ ├── Common.hs │ │ └── SpentInfoResult.hs └── test │ ├── Spec.hs │ └── Spec │ ├── Golden │ └── Snapshot │ │ ├── mainnet │ │ ├── allegra │ │ │ ├── blockinfo-slot16588800.out.golden │ │ │ ├── blockinfo-slot18698800.out.golden │ │ │ ├── blockinfo-slot21340776.out.golden │ │ │ ├── spentinfo-slot16588800.out.golden │ │ │ └── spentinfo-slot18728839.out.golden │ │ ├── alonzo1 │ │ │ ├── blockinfo-slot39916975.out.golden │ │ │ ├── blockinfo-slot43372584.out.golden │ │ │ ├── blockinfo-slot43372792.out.golden │ │ │ └── spentinfo-slot39916975.out.golden │ │ ├── alonzo2 │ │ │ ├── blockinfo-slot43372972.out.golden │ │ │ ├── blockinfo-slot45748924.out.golden │ │ │ ├── blockinfo-slot48124747.out.golden │ │ │ ├── spentinfo-slot43372972.out.golden │ │ │ └── spentinfo-slot59438205.out.golden │ │ ├── babbage1 │ │ │ ├── blockinfo-slot72316896.out.golden │ │ │ ├── blockinfo-slot77064584.out.golden │ │ │ ├── blockinfo-slot77068768.out.golden │ │ │ └── spentinfo-slot72316896.out.golden │ │ ├── babbage2 │ │ │ ├── blockinfo-slot84844885.out.golden │ │ │ ├── blockinfo-slot87198551.out.golden │ │ │ └── blockinfo-slot89596758.out.golden │ │ ├── byron1 │ │ │ ├── blockinfo-slot0.out.golden │ │ │ ├── blockinfo-slot206938.out.golden │ │ │ └── blockinfo-slot6.out.golden │ │ ├── byron2 │ │ │ ├── blockinfo-slot3801600.out.golden │ │ │ ├── blockinfo-slot3901600.out.golden │ │ │ ├── blockinfo-slot4039199.out.golden │ │ │ ├── spentinfo-slot3801600.out.golden │ │ │ └── spentinfo-slot3901600.out.golden │ │ ├── mary │ │ │ ├── blockinfo-slot23068800.out.golden │ │ │ ├── blockinfo-slot25168800.out.golden │ │ │ ├── blockinfo-slot27820794.out.golden │ │ │ └── spentinfo-slot23068800.out.golden │ │ └── shelley │ │ │ ├── blockinfo-slot4492800.out.golden │ │ │ ├── blockinfo-slot4520900.out.golden │ │ │ ├── blockinfo-slot9244771.out.golden │ │ │ ├── spentinfo-slot4492800.out.golden │ │ │ └── spentinfo-slot4520900.out.golden │ │ ├── preprod-5-10-subchain.golden │ │ └── preprod-5-10 │ │ ├── block_just_10_19445_5c9999c1e6ba0f2ad1b8c85cf7c0a6663ddd81e4584a93041daf87deb8325d3a.cbor │ │ ├── block_just_5_8641_f5441700216e5516c6dc19e7eb616f0bf1d04dd1368add35e3a7fd114e30b880.cbor │ │ ├── block_just_6_10802_8e2e24608ab3a9ec0cbf6b2e2b88574aae84955fcd5f89c604e52e33cbee9883.cbor │ │ ├── block_just_7_12961_4d4114f03ab1bccd22182bb1106336fcab3b38f884306e1b6fda83df384a2161.cbor │ │ ├── block_just_8_15122_018a398c415639148ecf39c3a5af68ef954e7fa5acc166253975019e9b022c83.cbor │ │ ├── block_just_9_17283_a28094a6147cffac3187bb28828e0834cb355cc31beb00f0fb3546769f54e98b.cbor │ │ └── epochState_just_4_6480_ab0a64eaf8bc9e96e4df7161d3ace4d32e88cab2315f952665807509e49892eb.cbor │ ├── Marconi │ └── Cardano │ │ ├── DbSyncComparison.hs │ │ ├── Indexers.hs │ │ ├── Indexers │ │ ├── BlockInfo.hs │ │ ├── ChainTip.hs │ │ ├── Datum.hs │ │ ├── MintTokenEvent.hs │ │ ├── Spent.hs │ │ ├── Utxo.hs │ │ └── UtxoQuery.hs │ │ └── Snapshot.hs │ └── README.md ├── marconi-core-json-rpc ├── CHANGELOG.md ├── LICENSE ├── NOTICE ├── README.md ├── changelog.d │ └── scriv.ini ├── marconi-core-json-rpc.cabal └── src │ ├── Marconi │ └── Core │ │ └── JsonRpc.hs │ └── Network │ └── JsonRpc │ ├── Client │ └── Types.hs │ ├── Server │ └── Types.hs │ └── Types.hs ├── marconi-core ├── CHANGELOG.md ├── LICENSE ├── NOTICE ├── README.md ├── changelog.d │ ├── 20230417_103947_nicolas.biri_error_handling.md │ ├── 20230519_180000_kayvank_deprecated_indexers.md │ └── scriv.ini ├── marconi-core.cabal ├── src │ └── Marconi │ │ ├── Core.hs │ │ └── Core │ │ ├── Class.hs │ │ ├── Coordinator.hs │ │ ├── Indexer │ │ ├── FileIndexer.hs │ │ ├── LastEventIndexer.hs │ │ ├── LastPointIndexer.hs │ │ ├── ListIndexer.hs │ │ ├── MixedIndexer.hs │ │ ├── SQLiteAggregateQuery.hs │ │ └── SQLiteIndexer.hs │ │ ├── Preprocessor.hs │ │ ├── Preprocessor │ │ └── Resume.hs │ │ ├── Query.hs │ │ ├── Transformer │ │ ├── Class.hs │ │ ├── IndexTransformer.hs │ │ ├── WithAction.hs │ │ ├── WithCache.hs │ │ ├── WithCatchup.hs │ │ ├── WithCatchup │ │ │ └── SQLite.hs │ │ ├── WithDelay.hs │ │ ├── WithFold.hs │ │ ├── WithPruning.hs │ │ ├── WithStream.hs │ │ ├── WithStream │ │ │ └── Streamable.hs │ │ ├── WithTracer.hs │ │ └── WithTransform.hs │ │ ├── Type.hs │ │ └── Worker.hs ├── test-lib │ └── Test │ │ └── Marconi │ │ └── Core │ │ └── ModelBased.hs └── test │ ├── Marconi │ └── CoreSpec.hs │ └── Spec.hs ├── marconi-sidechain ├── CHANGELOG.md ├── LICENSE ├── NOTICE ├── README.md ├── app │ └── Main.hs ├── marconi-sidechain.cabal ├── src │ └── Marconi │ │ └── Sidechain │ │ ├── Api │ │ ├── HttpServer.hs │ │ ├── JsonRpc │ │ │ ├── Endpoint │ │ │ │ ├── BurnTokenEvent.hs │ │ │ │ ├── CurrentSyncedBlock.hs │ │ │ │ ├── Echo.hs │ │ │ │ ├── EpochActiveStakePoolDelegation.hs │ │ │ │ ├── EpochNonce.hs │ │ │ │ ├── PastAddressUtxo.hs │ │ │ │ └── TargetAddresses.hs │ │ │ ├── Routes.hs │ │ │ └── Server.hs │ │ ├── Rest │ │ │ ├── Endpoint │ │ │ │ └── Metrics.hs │ │ │ ├── Routes.hs │ │ │ └── Server.hs │ │ └── Types.hs │ │ ├── CLI.hs │ │ ├── Concurrency.hs │ │ ├── Env.hs │ │ ├── Error.hs │ │ ├── Indexers.hs │ │ ├── Run.hs │ │ └── Utils.hs └── test │ ├── Spec.hs │ └── Spec │ ├── Golden │ ├── CLIInputValidation │ │ ├── invalidAddress-MissingBech32Prefix.golden │ │ ├── invalidAddress-badFormat.golden │ │ ├── invalidAddressPrefix-stake1.golden │ │ ├── invalidAddressPrefix-stake_test.golden │ │ ├── invalidAddress_1CharShort.golden │ │ ├── invalidAssetId-assetName-invalidBase16Format.golden │ │ ├── invalidAssetId-assetName-invalidBase16Length.golden │ │ ├── invalidAssetId-assetName-tooLong.golden │ │ ├── invalidAssetId-policyId-1ByteLong.golden │ │ ├── invalidAssetId-policyId-1ByteShort.golden │ │ ├── invalidAssetId-policyId-1HexCharLong.golden │ │ ├── invalidAssetId-policyId-1HexCharShort.golden │ │ └── invalidAssetId-policyId-badBase16Format.golden │ ├── Cli │ │ ├── marconi-sidechain___help.help │ │ └── marconi-sidechain___socket.help │ └── Routes │ │ ├── address-utxo-response.json │ │ ├── current-synced-point-response-1.json │ │ ├── current-synced-point-response-2.json │ │ ├── epoch-nonce-response.json │ │ ├── epoch-stakepooldelegation-response.json │ │ └── mintingpolicyhash-tx-response.json │ └── Marconi │ └── Sidechain │ ├── Api │ └── JsonRpc │ │ └── Endpoint │ │ ├── BurnTokenEvent.hs │ │ └── PastAddressUtxo.hs │ ├── CLI.hs │ ├── CLIInputValidation.hs │ ├── Routes.hs │ └── Utils.hs ├── marconi-starter ├── LICENSE ├── NOTICE ├── app │ └── Main.hs ├── marconi-starter.cabal ├── src │ └── Marconi │ │ └── Starter │ │ ├── CLI.hs │ │ ├── Env.hs │ │ ├── HttpServer.hs │ │ ├── Indexers.hs │ │ ├── Indexers │ │ └── AddressCount.hs │ │ └── Run.hs └── test │ ├── Driver.hs │ └── Spec │ └── Marconi │ └── Starter │ └── AddressCount.hs ├── nix ├── outputs.nix ├── project.nix ├── scripts │ └── start-benchmarking-machine.nix └── shell.nix ├── scripts └── babbage │ ├── alonzo-babbage-test-genesis.json │ └── conway-babbage-test-genesis.json └── scriv.ini /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.hs] 12 | max_line_length = 100 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # haskell.nix materialized files 2 | nix/pkgs/haskell/materialized*/**/*.nix linguist-generated=true 3 | # linguist gets confused by PIR files, and thinks they make up a lot of our source! 4 | *.pir linguist-detectable=false 5 | # Large HTML files inside 'notes' are dominating our repoistory language 6 | # estimate. 7 | notes/**/*.html linguist-documentation 8 | doc/* linguist-documentation 9 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | Pre-submit checklist: 7 | - Branch 8 | - [ ] Tests are provided (if possible) 9 | - [ ] Commit sequence broadly makes sense and have useful messages 10 | - [ ] Important changes are reflected in changelog.d of the affected packages 11 | - [ ] Relevant tickets are mentioned in commit messages 12 | - PR 13 | - [ ] (For external contributions) Corresponding issue exists and is linked in the description 14 | - [ ] Targeting main unless this is a cherry-pick backport 15 | - [ ] Self-reviewed the diff 16 | - [ ] Useful pull request description 17 | - [ ] If relevant, reference the ADR in the PR and reference the PR in the ADR 18 | - [ ] Reviewer requested 19 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | - package-ecosystem: github-actions 5 | directory: "/" 6 | schedule: 7 | interval: daily 8 | time: '00:00' 9 | timezone: UTC 10 | open-pull-requests-limit: 10 11 | commit-message: 12 | prefix: "chore" 13 | include: "scope" 14 | -------------------------------------------------------------------------------- /.github/workflows/haddock.yml: -------------------------------------------------------------------------------- 1 | name: "Build and Deploy to Github Pages" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | tags: 7 | - '[0-9]+.[0-9]+.[0-9]+.[0-9]+' 8 | jobs: 9 | build-haddock-site: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | environment: 14 | name: github-pages 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: cachix/install-nix-action@v22 18 | with: 19 | extra_nix_config: | 20 | experimental-features = nix-command flakes 21 | accept-flake-config = true 22 | - name: Build haddock site 23 | run: | 24 | ghcup nuke 25 | nix develop --command bash -c ' 26 | cabal update 27 | cabal haddock marconi-core 28 | cabal haddock marconi-cardano-core:lib:marconi-cardano-core 29 | cabal haddock marconi-cardano-indexers:lib:marconi-cardano-indexers 30 | cabal haddock marconi-cardano-chain-index:lib:marconi-cardano-chain-index 31 | cabal haddock marconi-core-json-rpc 32 | ' 33 | mkdir dist 34 | mkdir dist/marconi-core 35 | mkdir dist/marconi-cardano-core 36 | mkdir dist/marconi-cardano-indexers 37 | mkdir dist/marconi-cardano-chain-index 38 | mkdir dist/marconi-core-json-rpc 39 | cp -RL ./dist-newstyle/build/x86_64-linux/ghc-9.2.8/marconi-core-1.2.0.0/doc/html/marconi-core/* ./dist/marconi-core 40 | cp -RL ./dist-newstyle/build/x86_64-linux/ghc-9.2.8/marconi-cardano-core-1.2.0.0/doc/html/marconi-cardano-core/* ./dist/marconi-cardano-core 41 | cp -RL ./dist-newstyle/build/x86_64-linux/ghc-9.2.8/marconi-cardano-indexers-1.2.0.0/doc/html/marconi-cardano-indexers/* ./dist/marconi-cardano-indexers 42 | cp -RL ./dist-newstyle/build/x86_64-linux/ghc-9.2.8/marconi-cardano-chain-index-1.2.0.0/doc/html/marconi-cardano-chain-index/* ./dist/marconi-cardano-chain-index 43 | cp -RL ./dist-newstyle/build/x86_64-linux/ghc-9.2.8/marconi-core-json-rpc-1.2.0.0/doc/html/marconi-core-json-rpc/* ./dist/marconi-core-json-rpc 44 | - uses: JamesIves/github-pages-deploy-action@v4 45 | with: 46 | folder: dist 47 | target-folder: ${{ github.ref_name }} 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Latex 2 | *.dvi 3 | *.fls 4 | *.ptb 5 | *.aux 6 | *.log 7 | *.pdf 8 | *.gz 9 | *.synctex(busy) 10 | *.bbl 11 | *.fdb_latexmk 12 | *.blg 13 | *.glg 14 | *.glo 15 | *.gls 16 | *.glsdefs 17 | *.ist 18 | *.out 19 | *.toc 20 | *.bcf 21 | *.run.xml 22 | *.thm 23 | auto 24 | 25 | # Cabal 26 | .ghc.environment* 27 | tags 28 | dist-newstyle 29 | dist 30 | cabal-dev 31 | .cabal-sandbox/ 32 | cabal.sandbox.config 33 | cabal.config 34 | cabal.project.local 35 | 36 | # Stack 37 | .stack-work 38 | stack.yaml.lock 39 | 40 | # Haskell output files 41 | *.o 42 | *.hi 43 | *.dyn_o 44 | *.dyn_hi 45 | *.chi 46 | *.chs.h 47 | *.prof 48 | .liquid/ 49 | 50 | # Python 51 | __pycache__ 52 | 53 | # Nix 54 | result* 55 | pkgs/.cabal 56 | pkgs/.cache 57 | pkgs/.stack 58 | 59 | # Editor files 60 | *~ 61 | *# 62 | *_flymake.* 63 | .psc-ide-port 64 | .dir-locals.el 65 | *.code-workspace 66 | .*.sw* 67 | 68 | # Frontend files (NPM/Yarn, Spago, etc.) 69 | # N.B. officially we use NPM; ignoring Yarn files allows the local use of Yarn for those that want 70 | **/generated-docs/ 71 | **/node_modules/ 72 | **/output/ 73 | **/yarn-error.log 74 | **/yarn.lock 75 | **/.psci_modules/ 76 | **/.spago/ 77 | **/.spago2nix/ 78 | **/.psa-stash 79 | 80 | # Generated doc files 81 | doc/read-the-docs-site/_build 82 | 83 | # Misc 84 | .history/ 85 | .virtualenv 86 | .vscode/ 87 | .hsenv 88 | TAGS 89 | .DS_Store 90 | .uuid 91 | .terraform 92 | *.tfvars 93 | gh-pages 94 | .importify/ 95 | .envrc 96 | .direnv/ 97 | *.db 98 | *.db-shm 99 | *.db-wal 100 | hie.yaml 101 | node-server.sock* 102 | node.sock 103 | **/node?/db 104 | .pre-commit-config.yaml 105 | secrets/*/.gpg-id 106 | ghcid.txt 107 | # profiling output files 108 | *.timelog 109 | *.stacks 110 | -------------------------------------------------------------------------------- /.hlint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # this is rarely an improvement 3 | - ignore: {name: Move brackets to avoid $} 4 | # this is often worse 5 | - ignore: {name: Use <$>} 6 | # this aids clarity since you can name the parameters 7 | - ignore: {name: Avoid lambda} 8 | - ignore: {name: Avoid lambda using `infix`} 9 | - ignore: {name: Replace case with fromMaybe} 10 | # whether this is better is very variable 11 | - ignore: {name: Use infix} 12 | # hlint can't handle typed TH: https://github.com/haskell-suite/haskell-src-exts/issues/383 13 | # annoyingly, 'within' doesn't seem to work if there's a parse error, so we have to blanket 14 | # ignore it 15 | - ignore: {name: Parse error} 16 | # This is rarely better, and often more confusing 17 | - ignore: {name: Use asks} 18 | # It seems clearer to use case than fromMaybe 19 | - ignore: {name: Use fromMaybe} 20 | - ignore: {name: Use section} 21 | 22 | - fixity: infixr 8 .* 23 | - fixity: infixr 3 *** 24 | - fixity: infixr 3 &&& 25 | - fixity: infixr 1 <=< 26 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: doc/read-the-docs-site/conf.py 5 | 6 | build: 7 | os: "ubuntu-22.04" 8 | tools: 9 | # This means "latest version 3", which seems fine 10 | python: "3" 11 | 12 | python: 13 | install: 14 | - requirements: doc/read-the-docs-site/requirements.txt 15 | 16 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | See https://github.com/input-output-hk/cardano-engineering-handbook/blob/main/CODE-OF-CONDUCT.md -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Input Output Global, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | # Benchmarking 2 | 3 | Steps on running `prometheus` and `grafana` locally: 4 | 5 | * Change the `enable` of `start-benchmark-machine` in `shell.nix` to `true` 6 | * Open a Nix shell with `nix develop` 7 | * Run `cardano-node` 8 | * Run `marconi-sidechain` (or `marconi-cardano-chain-index`) on port 8090 9 | * Run `start-benchmark-machine local` (available in the Nix shell) 10 | * Go to `http://localhost:8080` on your web browser. 11 | * Log in with `admin` as username and password. 12 | * Go to the home dashboard 13 | -------------------------------------------------------------------------------- /config/cardano-node/mainnet/conway-genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "genDelegs": {}, 3 | "poolVotingThresholds": { 4 | "pvtCommitteeNormal": 0.51, 5 | "pvtCommitteeNoConfidence": 0.51, 6 | "pvtHardForkInitiation": 0.51, 7 | "pvtMotionNoConfidence": 0.51 8 | }, 9 | "dRepVotingThresholds": { 10 | "dvtMotionNoConfidence": 0.51, 11 | "dvtCommitteeNormal": 0.51, 12 | "dvtCommitteeNoConfidence": 0.51, 13 | "dvtUpdateToConstitution": 0.51, 14 | "dvtHardForkInitiation": 0.51, 15 | "dvtPPNetworkGroup": 0.51, 16 | "dvtPPEconomicGroup": 0.51, 17 | "dvtPPTechnicalGroup": 0.51, 18 | "dvtPPGovGroup": 0.51, 19 | "dvtTreasuryWithdrawal": 0.51 20 | }, 21 | "minCommitteeSize": 0, 22 | "committeeTermLimit": 60, 23 | "govActionExpiration": 14, 24 | "govActionDeposit": 0, 25 | "dRepDeposit": 0, 26 | "dRepActivity": 0, 27 | "constitution": { 28 | "anchor": { 29 | "url": "", 30 | "dataHash": "0000000000000000000000000000000000000000000000000000000000000000" 31 | } 32 | }, 33 | "committee": { 34 | "members": { 35 | }, 36 | "quorum": 0 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /config/cardano-node/mainnet/topology.json: -------------------------------------------------------------------------------- 1 | { 2 | "Producers": [ 3 | { 4 | "addr": "relays-new.cardano-mainnet.iohk.io", 5 | "port": 3001, 6 | "valency": 2 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /config/cardano-node/preprod/conway-genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "genDelegs": {}, 3 | "poolVotingThresholds": { 4 | "pvtCommitteeNormal": 0.51, 5 | "pvtCommitteeNoConfidence": 0.51, 6 | "pvtHardForkInitiation": 0.51, 7 | "pvtMotionNoConfidence": 0.51 8 | }, 9 | "dRepVotingThresholds": { 10 | "dvtMotionNoConfidence": 0.51, 11 | "dvtCommitteeNormal": 0.51, 12 | "dvtCommitteeNoConfidence": 0.51, 13 | "dvtUpdateToConstitution": 0.51, 14 | "dvtHardForkInitiation": 0.51, 15 | "dvtPPNetworkGroup": 0.51, 16 | "dvtPPEconomicGroup": 0.51, 17 | "dvtPPTechnicalGroup": 0.51, 18 | "dvtPPGovGroup": 0.51, 19 | "dvtTreasuryWithdrawal": 0.51 20 | }, 21 | "minCommitteeSize": 0, 22 | "committeeTermLimit": 60, 23 | "govActionExpiration": 14, 24 | "govActionDeposit": 0, 25 | "dRepDeposit": 0, 26 | "dRepActivity": 0, 27 | "constitution": { 28 | "anchor": { 29 | "url": "", 30 | "dataHash": "0000000000000000000000000000000000000000000000000000000000000000" 31 | } 32 | }, 33 | "committee": { 34 | "members": { 35 | }, 36 | "quorum": 0 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /config/cardano-node/preprod/topology.json: -------------------------------------------------------------------------------- 1 | { 2 | "localRoots": [ 3 | { 4 | "accessPoints": [], 5 | "advertise": false, 6 | "valency": 1 7 | } 8 | ], 9 | "publicRoots": [ 10 | { 11 | "accessPoints": [ 12 | { 13 | "address": "preprod-node.world.dev.cardano.org", 14 | "port": 30000 15 | } 16 | ], 17 | "advertise": false 18 | } 19 | ], 20 | "useLedgerAfterSlot": 4642000 21 | } 22 | -------------------------------------------------------------------------------- /config/cardano-node/preview/conway-genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "genDelegs": {}, 3 | "poolVotingThresholds": { 4 | "pvtCommitteeNormal": 0.51, 5 | "pvtCommitteeNoConfidence": 0.51, 6 | "pvtHardForkInitiation": 0.51, 7 | "pvtMotionNoConfidence": 0.51 8 | }, 9 | "dRepVotingThresholds": { 10 | "dvtMotionNoConfidence": 0.51, 11 | "dvtCommitteeNormal": 0.51, 12 | "dvtCommitteeNoConfidence": 0.51, 13 | "dvtUpdateToConstitution": 0.51, 14 | "dvtHardForkInitiation": 0.51, 15 | "dvtPPNetworkGroup": 0.51, 16 | "dvtPPEconomicGroup": 0.51, 17 | "dvtPPTechnicalGroup": 0.51, 18 | "dvtPPGovGroup": 0.51, 19 | "dvtTreasuryWithdrawal": 0.51 20 | }, 21 | "minCommitteeSize": 0, 22 | "committeeTermLimit": 60, 23 | "govActionExpiration": 14, 24 | "govActionDeposit": 0, 25 | "dRepDeposit": 0, 26 | "dRepActivity": 0, 27 | "constitution": { 28 | "anchor": { 29 | "url": "", 30 | "dataHash": "0000000000000000000000000000000000000000000000000000000000000000" 31 | } 32 | }, 33 | "committee": { 34 | "members": { 35 | }, 36 | "quorum": 0 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /config/cardano-node/preview/topology.json: -------------------------------------------------------------------------------- 1 | { 2 | "localRoots": [ 3 | { 4 | "accessPoints": [], 5 | "advertise": false, 6 | "valency": 1 7 | } 8 | ], 9 | "publicRoots": [ 10 | { 11 | "accessPoints": [ 12 | { 13 | "address": "preview-node.world.dev.cardano.org", 14 | "port": 30002 15 | } 16 | ], 17 | "advertise": false 18 | } 19 | ], 20 | "useLedgerAfterSlot": 322000 21 | } 22 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Input Output Global, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/README.md: -------------------------------------------------------------------------------- 1 | # Marconi documentation site 2 | 3 | This is a sphinx site. 4 | 5 | Run `nix develop` to enter a development shell with `sphinx-build` and `sphinx-autobuild`. 6 | 7 | The following commands are also available: 8 | 9 | - `develop-rtd-site` 10 | Start a development server with live reload on `http://localhost:8000` 11 | - `build-rtd-site` 12 | Build the docs locally in `_build/index.html` 13 | - `serve-rtd-site` 14 | Build the full site with nix (including Haddock) and serve it on `http://localhost:8002` 15 | 16 | The doc site from main is built automatically and hosted [here](https://marconi.readthedocs.io/en/latest). 17 | 18 | Additionally, the site is built for all PRs, and a link to a preview can be found in the PR statuses. 19 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/_static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/marconi/d39923eb7b81156f5783e6252469b321ad89a253/doc/read-the-docs-site/_static/.gitkeep -------------------------------------------------------------------------------- /doc/read-the-docs-site/_static/theme_overrides.css: -------------------------------------------------------------------------------- 1 | /* Fix table wrapping https://github.com/readthedocs/sphinx_rtd_theme/issues/117 */ 2 | @media screen and (min-width: 768px) { 3 | .wy-table-responsive table td, .wy-table-responsive table th { 4 | white-space: normal !important; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/_templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/marconi/d39923eb7b81156f5783e6252469b321ad89a253/doc/read-the-docs-site/_templates/.gitkeep -------------------------------------------------------------------------------- /doc/read-the-docs-site/adr/0003-observability.rst: -------------------------------------------------------------------------------- 1 | .. _adr7: 2 | 3 | ADR 7: Marconi observability 4 | ============================ 5 | 6 | Date: 2023-07-19 7 | 8 | Authors 9 | ------- 10 | 11 | koslambrou 12 | 13 | Status 14 | ------ 15 | 16 | Draft 17 | 18 | Context 19 | ------- 20 | 21 | Whenever we add new features in Marconi, we inevitably end up in a state where we introduce a significant regression in sync time, CPU/RAM/Disk usage and query performance. 22 | Currently, developers who are adding new features will typically test those features by running ``marconi-cardano-chain-index`` or ``marconi-sidechain``, and manually visualize the CPU/RAM usage by running ``htop`` (or similar), checking syncing speed by reading the logs, and manually run queries after syncing is done. 23 | However, that approach does not give accurate information for detecting regressions, because we only look at the logs at specific points in time and we might miss peaks in memory usage for example. 24 | Moreover, this testing approach does not scale for delivering on time the different features. 25 | 26 | Therefore, we need a solution for observing the runtime behavior of Marconi through different metrics such blocks processed, query time, CPU/RAM/Disk usage, etc. 27 | Ideally, these observations would be done through graphical visualizations. 28 | Eventually, they would be used for external reporting and as part of our QA practices. 29 | 30 | Decision 31 | -------- 32 | 33 | * We will expose Prometheus metrics for our chain-indexers, more specifically, ``marconi-sidechain``. 34 | The metrics we should provide are: 35 | 36 | * CPU, memory and disk usage 37 | * Total number of processed blocks 38 | * The number of processed queries 39 | * The time spent processing queries 40 | 41 | Except the CPU/RAM/Disk usage which are provided by the systemd process, the application-specific metrics will be exposed in an HTTP endpoint such as ``/metrics``. 42 | 43 | * We will expose Grafana dashboards to visualize each of the metrics. 44 | 45 | Argument 46 | -------- 47 | 48 | Grafana and Prometheus seem to be the go-to framekwork for observing behavior of long-running applications, and it is used by other projects in IOG, most notably by the Sidechain Tribe. 49 | As we're delivering ``marconi-sidechain`` for them, it makes sense to use the same framework. 50 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/adr/index.rst: -------------------------------------------------------------------------------- 1 | Architectural Decision Records 2 | ============================== 3 | 4 | We document our architectural and design decisions for Marconi. 5 | In order to do that, there is practice called architectural decision records ("ADR"), that we can integrate into our workflow. 6 | An architectural decision record (ADR) is a document that captures an important architectural decision made along with its context and consequences. 7 | 8 | The goals are: 9 | 10 | * making decisions transparent to internal/external stakeholders and contributors. 11 | 12 | * getting feeback on decisions that we're about to make or have made 13 | 14 | * providing external contributors a framework to propose architectural changes 15 | 16 | * providing a big picture of all major decisions that were made 17 | 18 | The general process for creating an ADR is: 19 | 20 | 1. cloning the repository 21 | 22 | 2. creating a new file with the format `-.rst` in the directory `doc/adr` 23 | 24 | 3. adding the ADR in the table of content tree of the documentation website 25 | 26 | 4. committing and pushing to the repository 27 | 28 | .. toctree:: 29 | :maxdepth: 2 30 | :titlesonly: 31 | 32 | 0001-record-architecture-decisions 33 | 0002-indexer-resuming-strategy 34 | 0003-observability 35 | 0004-marconi-sidechain-testing-strategy 36 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/architecture/appendix.rst: -------------------------------------------------------------------------------- 1 | Appendix 2 | ======== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :titlesonly: 7 | 8 | ../adr/index 9 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/architecture/building-blocks-view.rst: -------------------------------------------------------------------------------- 1 | Building blocks 2 | =============== 3 | 4 | The `Marconi Github repository <https://github.com/input-output-hk/marconi>`_ contains a set of Haskell packages which satisfy two main use cases: 5 | 6 | * building your own chain-indexer in Haskell 7 | * reusing predefined indexers 8 | 9 | In this section of the documentation, we will describe these different packages as well the main building blocks for defining Marconi indexers. 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | :titlesonly: 14 | 15 | ./repository-structure.rst 16 | ./marconi-core-building-blocks.rst 17 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/architecture/index.rst: -------------------------------------------------------------------------------- 1 | Core architecture 2 | ================= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :titlesonly: 7 | 8 | system-context 9 | building-blocks-view 10 | runtime-view 11 | local-node-communication 12 | marconi-as-a-node 13 | ../adr/index 14 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/architecture/local-node-communication.rst: -------------------------------------------------------------------------------- 1 | Local node communication 2 | ======================== 3 | 4 | Nothing yet. Coming eventually! 5 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/architecture/marconi-as-a-node.rst: -------------------------------------------------------------------------------- 1 | Marconi as a node 2 | ================= 3 | 4 | Nothing yet. Coming eventually! 5 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/cardano-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/marconi/d39923eb7b81156f5783e6252469b321ad89a253/doc/read-the-docs-site/cardano-logo.png -------------------------------------------------------------------------------- /doc/read-the-docs-site/default.nix: -------------------------------------------------------------------------------- 1 | { stdenv, lib, pythonPackages, sphinxcontrib-domaintools, sphinxcontrib-haddock, sphinx-markdown-tables, sphinxemoji, combined-haddock, ... }: 2 | stdenv.mkDerivation { 3 | name = "plutus-docs"; 4 | src = lib.sourceFilesBySuffices ./. [ ".py" ".rst" ".md" ".hs" ".png" ".svg" ".bib" ".csv" ".css" ".html" "txt" ]; 5 | buildInputs = with pythonPackages; [ 6 | sphinx 7 | sphinx_rtd_theme 8 | sphinxcontrib-domaintools 9 | sphinxcontrib-haddock 10 | sphinx-markdown-tables 11 | sphinxcontrib_plantuml 12 | sphinxcontrib-bibtex 13 | sphinxemoji 14 | recommonmark 15 | ]; 16 | buildPhase = '' 17 | sphinx-build -n -W . $out 18 | ''; 19 | dontInstall = true; 20 | } 21 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/introduction.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | Nothing yet. Coming eventually! 5 | 6 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-as-a-library/additional-links.rst: -------------------------------------------------------------------------------- 1 | Additionnal links 2 | ================= 3 | 4 | Coming eventually! 5 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-as-a-library/cookbook/index.rst: -------------------------------------------------------------------------------- 1 | Cookbook 2 | ======== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-as-a-library/index.rst: -------------------------------------------------------------------------------- 1 | .. _marconi_as_a_library: 2 | 3 | Building your own chain-indexer 4 | =============================== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | overview 10 | quickstart 11 | tutorials/index 12 | cookbook/index 13 | additional-links 14 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-as-a-library/overview.rst: -------------------------------------------------------------------------------- 1 | .. _overview: 2 | 3 | Overview 4 | ======== 5 | 6 | Nothing yet. Coming eventually! 7 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-as-a-library/quickstart.rst: -------------------------------------------------------------------------------- 1 | .. _quickstart: 2 | 3 | Quick start 4 | =========== 5 | 6 | Nothing yet. Coming soon! 7 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-as-a-library/tutorials/how-to-reuse-existing-indexers.rst: -------------------------------------------------------------------------------- 1 | How to reuse existing indexers 2 | ============================== 3 | 4 | We'll show you how to reuse and extend existing indexers (indexers build by another party) and integrate it in your indexer stack. 5 | 6 | There are some predefined indexers in `marconi-cardano-indexers` which we'll show how to reuse them. 7 | 8 | Coming eventually! 9 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-as-a-library/tutorials/how-to-run-several-indexers.rst: -------------------------------------------------------------------------------- 1 | How to run several indexers 2 | =========================== 3 | 4 | We'll show you how to run multiple indexers. 5 | 6 | Coming eventually! 7 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-as-a-library/tutorials/how-to-test-first-indexer.rst: -------------------------------------------------------------------------------- 1 | How to test your first indexer 2 | ============================== 3 | 4 | Coming eventually!! 5 | 6 | * Show how to test only the indexer by comparing the ListIndexer version with the SQLite version 7 | * Show how to do an integration test using `cardano-node-emulator` 8 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-as-a-library/tutorials/index.rst: -------------------------------------------------------------------------------- 1 | Tutorials 2 | ========= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | write-first-indexer 8 | how-to-query-first-indexer 9 | how-to-test-first-indexer 10 | how-to-run-several-indexers 11 | how-to-reuse-existing-indexers 12 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-cardano-chain-index/index.rst: -------------------------------------------------------------------------------- 1 | Running `marconi-cardano-chain-index` 2 | ===================================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | overview 8 | quickstart 9 | openapi-spec 10 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-cardano-chain-index/openapi-spec.rst: -------------------------------------------------------------------------------- 1 | OpenAPI specification 2 | ===================== 3 | 4 | Nothing yet. Coming eventually! 5 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-cardano-chain-index/overview.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | 4 | Nothing yet. Coming eventually! 5 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/doc/marconi-cardano-chain-index/quickstart.rst: -------------------------------------------------------------------------------- 1 | Quick start 2 | =========== 3 | 4 | Nothing yet. Coming eventually! 5 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/exts/hs_domain.py: -------------------------------------------------------------------------------- 1 | # This is a copy of the one from the module, it's here so that 2 | # it's easier for people not using Nix to build the site. 3 | 4 | from sphinxcontrib.domaintools import * 5 | 6 | def hs_domain(): 7 | return custom_domain('HaskellDomain', 8 | name = 'hs', 9 | label = "Haskell", 10 | 11 | elements = dict( 12 | hsobj = dict( 13 | objname = "Haskell entity", 14 | ), 15 | hstype = dict( 16 | objname = "Haskell type", 17 | ), 18 | hsval = dict( 19 | objname = "Haskell value", 20 | ), 21 | hsmod = dict( 22 | objname = "Haskell module", 23 | ), 24 | ) 25 | ) 26 | 27 | def setup(app): 28 | app.add_domain(hs_domain()) 29 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/index.rst: -------------------------------------------------------------------------------- 1 | Marconi 2 | ======= 3 | 4 | .. toctree:: 5 | :caption: User guide 6 | :maxdepth: 2 7 | 8 | doc/introduction.rst 9 | doc/marconi-as-a-library/index 10 | doc/marconi-cardano-chain-index/index 11 | 12 | .. toctree:: 13 | :caption: Design and Architecture 14 | :maxdepth: 2 15 | 16 | architecture/index 17 | architecture/comparison-other-chain-indexing-projects 18 | architecture/appendix 19 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/marconi-doc.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 3.8 2 | name: marconi-doc 3 | version: 1.2.0.0 4 | license: Apache-2.0 5 | license-files: 6 | LICENSE 7 | NOTICE 8 | 9 | build-type: Simple 10 | 11 | source-repository head 12 | type: git 13 | location: https://github.com/input-output-hk/marconi 14 | 15 | common lang 16 | default-language: Haskell2010 17 | default-extensions: 18 | DeriveFoldable 19 | DeriveFunctor 20 | DeriveGeneric 21 | DeriveLift 22 | DeriveTraversable 23 | ExplicitForAll 24 | GeneralizedNewtypeDeriving 25 | ImportQualifiedPost 26 | OverloadedStrings 27 | ScopedTypeVariables 28 | StandaloneDeriving 29 | 30 | ghc-options: 31 | -Wall -Widentities -Wincomplete-record-updates 32 | -Wincomplete-uni-patterns -Wmissing-import-lists 33 | -Wnoncanonical-monad-instances -Wredundant-constraints 34 | -Wunused-packages 35 | 36 | executable marconi-doc 37 | import: lang 38 | hs-source-dirs: doc/marconi-as-a-library/tutorials 39 | main-is: BasicApp.hs 40 | 41 | -------------------- 42 | -- Local components 43 | -------------------- 44 | build-depends: 45 | , marconi-cardano-chain-index >=1.2.0 46 | , marconi-cardano-core >=1.2.0 47 | , marconi-cardano-indexers >=1.2.0 48 | , marconi-core >=1.2.0 49 | , marconi-core-json-rpc >=1.2.0 50 | 51 | -------------------------- 52 | -- Other IOG dependencies 53 | -------------------------- 54 | build-depends: 55 | , cardano-api 56 | , iohk-monitoring 57 | 58 | ------------------------ 59 | -- Non-IOG dependencies 60 | ------------------------ 61 | build-depends: 62 | , aeson 63 | , async 64 | , base >=4.9 && <5 65 | , lens 66 | , mtl 67 | , servant-server 68 | , sqlite-simple 69 | , warp 70 | -------------------------------------------------------------------------------- /doc/read-the-docs-site/requirements.txt: -------------------------------------------------------------------------------- 1 | Sphinx==3.1.1 2 | sphinx-intl==2.0.1 3 | transifex-client==0.14.4 4 | testresources==2.0.1 5 | -e git+https://github.com/input-output-hk/sphinx_rtd_theme.git#egg=sphinx_rtd_theme 6 | recommonmark==0.6 7 | ## The following requirements were added by pip freeze: 8 | alabaster==0.7.12 9 | Babel==2.9.1 10 | certifi>=2023.7.22 11 | chardet==3.0.4 12 | click==7.1.2 13 | sphinxcontrib-domaintools>=0.3 14 | sphinxemoji==0.1.6 15 | sphinx_markdown_tables==0.0.15 16 | sphinxcontrib-plantuml==0.20.1 17 | sphinxcontrib-bibtex==2.2.0 18 | CommonMark==0.9.1 19 | docutils==0.16 20 | future==0.18.3 21 | idna==2.9 22 | imagesize==1.2.0 23 | Jinja2==2.11.3 24 | jsonpointer==2.0 25 | jsonref==0.2 26 | MarkupSafe==1.1.1 27 | markdown==3.3.7 28 | Pygments>=2.15.0 29 | pytz==2020.1 30 | requests==2.31.0 31 | six==1.15.0 32 | snowballstemmer==2.0.0 33 | sphinxcontrib-websupport==1.2.2 34 | urllib3==1.26.5 35 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Marconi Chain Indexer"; 3 | 4 | 5 | inputs = { 6 | 7 | iogx = { 8 | url = "github:input-output-hk/iogx"; 9 | inputs.haskell-nix.follows = "haskell-nix"; 10 | inputs.hackage.follows = "hackage"; 11 | inputs.CHaP.follows = "CHaP"; 12 | inputs.nixpkgs.follows = "nixpkgs"; 13 | }; 14 | 15 | nixpkgs.follows = "haskell-nix/nixpkgs"; 16 | 17 | hackage = { 18 | url = "github:input-output-hk/hackage.nix"; 19 | flake = false; 20 | }; 21 | 22 | CHaP = { 23 | url = "github:input-output-hk/cardano-haskell-packages?ref=repo"; 24 | flake = false; 25 | }; 26 | 27 | haskell-nix = { 28 | url = "github:input-output-hk/haskell.nix"; 29 | inputs.hackage.follows = "hackage"; 30 | }; 31 | 32 | # Used to provide the cardano-node and cardano-cli executables. 33 | cardano-node.url = "github:input-output-hk/cardano-node?ref=8.4.0-pre"; 34 | 35 | mithril.url = "github:input-output-hk/mithril"; 36 | }; 37 | 38 | 39 | outputs = inputs: inputs.iogx.lib.mkFlake { 40 | inherit inputs; 41 | repoRoot = ./.; 42 | systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ]; 43 | outputs = import ./nix/outputs.nix; 44 | }; 45 | 46 | 47 | nixConfig = { 48 | extra-substituters = [ 49 | "https://cache.iog.io" 50 | ]; 51 | extra-trusted-public-keys = [ 52 | "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" 53 | ]; 54 | allow-import-from-derivation = true; 55 | accept-flake-config = true; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /fourmolu.yaml: -------------------------------------------------------------------------------- 1 | # Number of spaces per indentation step 2 | indentation: 2 3 | 4 | # Max line length for automatic line breaking 5 | column-limit: 100 6 | 7 | # Styling of arrows in type signatures (choices: trailing, leading, or leading-args) 8 | function-arrows: leading 9 | 10 | # How to place commas in multi-line lists, records, etc. (choices: leading or trailing) 11 | comma-style: leading 12 | 13 | # Styling of import/export lists (choices: leading, trailing, or diff-friendly) 14 | import-export-style: diff-friendly 15 | 16 | # Whether to full-indent or half-indent 'where' bindings past the preceding body 17 | indent-wheres: true 18 | 19 | # Whether to leave a space before an opening record brace 20 | record-brace-space: false 21 | 22 | # Number of spaces between top-level declarations 23 | newlines-between-decls: 1 24 | 25 | # How to print Haddock comments (choices: single-line, multi-line, or multi-line-compact) 26 | haddock-style: multi-line 27 | 28 | # How to print module docstring 29 | haddock-style-module: null 30 | 31 | # Styling of let blocks (choices: auto, inline, newline, or mixed) 32 | let-style: auto 33 | 34 | # How to align the 'in' keyword with respect to the 'let' keyword (choices: left-align, right-align, or no-space) 35 | in-style: right-align 36 | 37 | # Whether to put parentheses around a single constraint (choices: auto, always, or never) 38 | single-constraint-parens: always 39 | 40 | # Output Unicode syntax (choices: detect, always, or never) 41 | unicode: never 42 | 43 | # Give the programmer more choice on where to insert blank lines 44 | respectful: true 45 | 46 | # Fixity information for operators 47 | fixities: [] 48 | 49 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | <a id='changelog-1.2.0'></a> 3 | # 1.2.0 — 2023-03-03 4 | 5 | ## Added 6 | 7 | - Epoch stakepool size indexer; the data indexed is roughly equivalent to the `epoch_stake` table in cardano-db-sync. 8 | 9 | - Mint/burn indexer: indexes mint/burn events of custom tokens in transactions. 10 | 11 | - Address-Datum indexer: indexes all datums for any given address in Cardano. 12 | 13 | - CLI flag to allow disabling the Address-Datum indexer. 14 | 15 | - Added ToJSON/FromJSON instances for the UTXO indexer types like `UtxoRow` and `Utxo` 16 | 17 | - Bootstrapped a benchmark suite for evaluating performance of `marconi-chain-index` 18 | 19 | - Added a simple benchmark for evaluating query performance for the address with the most UTXOs. 20 | 21 | - Adding 'PRAGMA journal_mode=WAL' for all indexers that are opening a SQLite database 22 | 23 | - Added golden tests for the CLI parameter, PLT-1513 24 | 25 | ## Changed 26 | 27 | - Renamed the `marconi` package name to `marconi-chain-index` 28 | - Renamed the module names of the `jsonrpc` lib from Marconi.JsonRpc` to `Network.JsonRpc` 29 | 30 | - `UtxoRow` doesn't store the `BlockNo` anymore 31 | 32 | ## Fixed 33 | 34 | - Fixed the resuming of the UTXO indexer 35 | - Fixed the UtxoEvent generator to generate unique utxos and only spend utxos which have been 36 | previously created. 37 | 38 | - Fixed lint errors and warnings 39 | 40 | - minor changes, mainly adding `show` instances for debugging purposes 41 | 42 | * Fixed a bug in the datum extraction in the Utxo indexer 43 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Input Output Global, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/app/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | import Marconi.Cardano.ChainIndex.Run (run) 4 | 5 | main :: IO () 6 | main = run "marconi-cardano-chain-index" 7 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/cbits/cbits/rev.c: -------------------------------------------------------------------------------- 1 | // <magic: fe> <marker: gitrev> <size: padded to 20 bytes> <space for gitrev (40 char)> 2 | char _marconi_git_rev[68] 3 | = "fe" 4 | "gitrev" 5 | "0000000000" 6 | "0000000040" 7 | "0000000000" 8 | "0000000000" 9 | "0000000000" 10 | "0000000000" 11 | ; 12 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/cbits/rev.c: -------------------------------------------------------------------------------- 1 | // <magic: fe> <marker: gitrev> <size: padded to 20 bytes> <space for gitrev (40 char)> 2 | char _marconi_git_rev[68] 3 | = "fe" 4 | "gitrev" 5 | "0000000000" 6 | "0000000040" 7 | "0000000000" 8 | "0000000000" 9 | "0000000000" 10 | "0000000000" 11 | ; 12 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/changelog.d/20230403_070417_nicolas.biri_current_indexed_slot.md: -------------------------------------------------------------------------------- 1 | ### Added 2 | 3 | - The utxo indexer has a second quey in its query type: 'LastSyncPoint', which 4 | returns the last sync point that was processed by all the indexers. 5 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/changelog.d/20230417_103501_nicolas.biri_error_handling.md: -------------------------------------------------------------------------------- 1 | ### Added 2 | 3 | - Add an `IndexerError` type to capture errors that may happen when we perform an 4 | action (`insert`, `query`, `rewind`) on an indexer 5 | 6 | 7 | ### Changed 8 | 9 | - Most `StorableMonad` are now of the form `ExceptT IndexerError IO` instead of 10 | `IO` 11 | 12 | --> 13 | <!-- 14 | ### Deprecated 15 | 16 | - A bullet item for the Deprecated category. 17 | 18 | --> 19 | <!-- 20 | ### Fixed 21 | 22 | - A bullet item for the Fixed category. 23 | 24 | --> 25 | <!-- 26 | ### Security 27 | 28 | - A bullet item for the Security category. 29 | 30 | --> 31 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/changelog.d/20230519_180000_kayvank_deprecated_indexers.md: -------------------------------------------------------------------------------- 1 | ### Changed 2 | 3 | - `deprecated-indexers` removed deprecated indexer, Datum indexer 4 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/changelog.d/20230523_082120_nicolas.biri_future_spent.md: -------------------------------------------------------------------------------- 1 | ### Added 2 | 3 | - The `Utxo` indexer now keep track of when an Utxo is spent and in which Tx. 4 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/changelog.d/scriv.ini: -------------------------------------------------------------------------------- 1 | ../../scriv.ini -------------------------------------------------------------------------------- /marconi-cardano-chain-index/snapshot/Main.hs: -------------------------------------------------------------------------------- 1 | import Marconi.Cardano.ChainIndex.Snapshot.Run (run) 2 | 3 | main :: IO () 4 | main = run 5 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/HttpServer.hs: -------------------------------------------------------------------------------- 1 | module Marconi.Cardano.ChainIndex.Api.HttpServer where 2 | 3 | import Control.Lens ((^.)) 4 | import Control.Monad.Reader (ReaderT, ask, lift) 5 | import Data.Proxy (Proxy (Proxy)) 6 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Server (jsonRpcServer) 7 | import Marconi.Cardano.ChainIndex.Api.Rest.Server (restApiServer) 8 | import Marconi.Cardano.ChainIndex.Api.Routes ( 9 | API, 10 | ) 11 | import Marconi.Cardano.ChainIndex.Api.Types ( 12 | HttpServerConfig, 13 | configPort, 14 | configTrace, 15 | ) 16 | import Marconi.Core.JsonRpc ( 17 | ReaderHandler, 18 | ReaderServer, 19 | catchHttpHandlerExceptions, 20 | hoistReaderHandler, 21 | mkHttpRequestTracer, 22 | ) 23 | import Network.JsonRpc.Server.Types () 24 | import Network.Wai.Handler.Warp (defaultSettings, runSettings, setPort) 25 | import Servant.API ((:<|>) ((:<|>))) 26 | import Servant.Server ( 27 | Application, 28 | Handler, 29 | hoistServer, 30 | serve, 31 | ) 32 | 33 | -- | Bootstraps the HTTP server 34 | runHttpServer :: ReaderT HttpServerConfig IO () 35 | runHttpServer = do 36 | config <- ask 37 | let settings = setPort (config ^. configPort) defaultSettings 38 | requestTracer <- mkHttpRequestTracer (config ^. configTrace) 39 | lift $ runSettings settings $ requestTracer $ marconiApp config 40 | 41 | marconiApp :: HttpServerConfig -> Application 42 | marconiApp config = do 43 | let trace = config ^. configTrace 44 | wrapHandler :: ReaderHandler HttpServerConfig a -> Handler a 45 | wrapHandler = catchHttpHandlerExceptions trace . hoistReaderHandler config 46 | serve (Proxy @API) $ 47 | hoistServer (Proxy @API) wrapHandler httpRpcServer 48 | 49 | httpRpcServer :: ReaderServer HttpServerConfig API 50 | httpRpcServer = jsonRpcServer :<|> restApiServer 51 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/JsonRpc/Endpoint/CurrentSyncedBlock/Tip.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE NamedFieldPuns #-} 3 | {-# LANGUAGE TemplateHaskell #-} 4 | 5 | module Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.CurrentSyncedBlock.Tip ( 6 | Tip (..), 7 | fromChainTip, 8 | ) where 9 | 10 | import Cardano.Api qualified as C 11 | import Data.Aeson.TH (defaultOptions, deriveJSON) 12 | 13 | data Tip = Tip 14 | { blockNo :: C.BlockNo 15 | , blockHeaderHash :: C.Hash C.BlockHeader 16 | , slotNo :: C.SlotNo 17 | } 18 | deriving (Eq, Ord, Show) 19 | 20 | $(deriveJSON defaultOptions ''Tip) 21 | 22 | fromChainTip :: C.ChainTip -> Maybe Tip 23 | fromChainTip C.ChainTipAtGenesis = Nothing 24 | fromChainTip (C.ChainTip slotNo blockHeaderHash blockNo) = 25 | Just $ Tip{blockNo, blockHeaderHash, slotNo} 26 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/JsonRpc/Endpoint/Echo.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | 3 | module Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.Echo ( 4 | RpcEchoMethod, 5 | echo, 6 | ) where 7 | 8 | import Marconi.Cardano.ChainIndex.Api.Types (HttpServerConfig) 9 | import Marconi.Core.JsonRpc (ReaderHandler) 10 | import Network.JsonRpc.Types (JsonRpc, JsonRpcErr) 11 | 12 | ------------------ 13 | -- Method types -- 14 | ------------------ 15 | 16 | type RpcEchoMethod = JsonRpc "echo" String String String 17 | 18 | -------------- 19 | -- Handlers -- 20 | -------------- 21 | 22 | -- | Echo a message back as a JSON-RPC response. Used for testing the server. 23 | echo 24 | :: String 25 | -> ReaderHandler HttpServerConfig (Either (JsonRpcErr String) String) 26 | echo = return . Right 27 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/JsonRpc/Endpoint/TargetAddresses.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | 3 | module Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.TargetAddresses ( 4 | RpcTargetAddressesMethod, 5 | getTargetAddressesQueryHandler, 6 | ) where 7 | 8 | import Cardano.Api (SerialiseAddress (serialiseAddress)) 9 | import Control.Lens (view) 10 | import Data.Text (Text) 11 | import Marconi.Cardano.ChainIndex.Api.Types (HttpServerConfig, configTrackedAddresses) 12 | import Marconi.Core.JsonRpc (ReaderHandler) 13 | import Network.JsonRpc.Types (JsonRpc, JsonRpcErr, UnusedRequestParams) 14 | 15 | ------------------ 16 | -- Method types -- 17 | ------------------ 18 | 19 | type RpcTargetAddressesMethod = 20 | JsonRpc 21 | "getTargetAddresses" 22 | UnusedRequestParams 23 | String 24 | [Text] 25 | 26 | -------------- 27 | -- Handlers -- 28 | -------------- 29 | 30 | -- | Return the list of TargetAddresses in Bech32 representation. 31 | getTargetAddressesQueryHandler 32 | :: UnusedRequestParams 33 | -- ^ Will be an empty string, empty object, or null, as we are ignoring this param, and returning everything 34 | -> ReaderHandler HttpServerConfig (Either (JsonRpcErr String) [Text]) 35 | getTargetAddressesQueryHandler _ = do 36 | addresses <- view configTrackedAddresses 37 | pure $ Right $ serialiseAddress <$> addresses 38 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/JsonRpc/Endpoint/Utxo/SpentInfoResult.hs: -------------------------------------------------------------------------------- 1 | module Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.Utxo.SpentInfoResult where 2 | 3 | import Cardano.Api (FromJSON, ToJSON) 4 | import Cardano.Api qualified as C 5 | import GHC.Generics (Generic) 6 | 7 | -- This module only exists because of record name clashes 8 | 9 | data SpentInfoResult = SpentInfoResult 10 | { slotNo :: !C.SlotNo 11 | , txId :: !C.TxId 12 | } 13 | deriving (Eq, Ord, Show, Generic, FromJSON, ToJSON) 14 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/JsonRpc/Routes.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TypeOperators #-} 3 | 4 | module Marconi.Cardano.ChainIndex.Api.JsonRpc.Routes (JsonRpcAPI) where 5 | 6 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.CurrentSyncedBlock ( 7 | RpcGetCurrentSyncedBlock, 8 | ) 9 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.Echo (RpcEchoMethod) 10 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.EpochState ( 11 | RpcEpochActiveStakePoolDelegationMethod, 12 | RpcEpochNonceMethod, 13 | ) 14 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.MintBurnToken ( 15 | RpcGetBurnTokenEventsMethod, 16 | ) 17 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.TargetAddresses ( 18 | RpcTargetAddressesMethod, 19 | ) 20 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.Utxo (RpcGetUtxosFromAddressMethod) 21 | import Network.JsonRpc.Types (RawJsonRpc) 22 | import Servant ((:<|>), (:>)) 23 | 24 | -- | JSON-RPC API endpoint 25 | type JsonRpcAPI = "json-rpc" :> RawJsonRpc RpcAPI 26 | 27 | -- | JSON-RPC methods 28 | type RpcAPI = 29 | RpcEchoMethod 30 | :<|> RpcTargetAddressesMethod 31 | :<|> RpcEpochActiveStakePoolDelegationMethod 32 | :<|> RpcEpochNonceMethod 33 | :<|> RpcGetBurnTokenEventsMethod 34 | :<|> RpcGetCurrentSyncedBlock 35 | :<|> RpcGetUtxosFromAddressMethod 36 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/JsonRpc/Server.hs: -------------------------------------------------------------------------------- 1 | module Marconi.Cardano.ChainIndex.Api.JsonRpc.Server (jsonRpcServer) where 2 | 3 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.CurrentSyncedBlock ( 4 | getCurrentSyncedBlockHandler, 5 | ) 6 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.Echo (echo) 7 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.EpochState ( 8 | getEpochNonceHandler, 9 | getEpochStakePoolDelegationHandler, 10 | ) 11 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.MintBurnToken ( 12 | getBurnTokenEventsHandler, 13 | ) 14 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.TargetAddresses ( 15 | getTargetAddressesQueryHandler, 16 | ) 17 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.Utxo (getUtxosFromAddressQueryHandler) 18 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Routes (JsonRpcAPI) 19 | import Marconi.Cardano.ChainIndex.Api.Types (HttpServerConfig) 20 | import Marconi.Core.JsonRpc (ReaderServer) 21 | import Servant.API ((:<|>) ((:<|>))) 22 | 23 | -- | Handlers for the JSON-RPC 24 | jsonRpcServer :: ReaderServer HttpServerConfig JsonRpcAPI 25 | jsonRpcServer = 26 | echo 27 | :<|> getTargetAddressesQueryHandler 28 | :<|> getEpochStakePoolDelegationHandler 29 | :<|> getEpochNonceHandler 30 | :<|> getBurnTokenEventsHandler 31 | :<|> getCurrentSyncedBlockHandler 32 | :<|> getUtxosFromAddressQueryHandler 33 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/Rest/Endpoint/Metrics.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TypeOperators #-} 3 | 4 | module Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.Metrics where 5 | 6 | import Control.Monad.Trans (liftIO) 7 | import Data.ByteString qualified as BS 8 | import Data.Text (Text) 9 | import Data.Text.Encoding qualified as Text 10 | import Marconi.Cardano.ChainIndex.Api.Types (HttpServerConfig) 11 | import Marconi.Core.JsonRpc (ReaderHandler) 12 | import Prometheus qualified as P 13 | import Servant (Get, PlainText, (:>)) 14 | 15 | ------------------ 16 | -- Method types -- 17 | ------------------ 18 | 19 | type GetMetrics = "metrics" :> Get '[PlainText] Text 20 | 21 | ---------------------------- 22 | -- Query and result types -- 23 | ---------------------------- 24 | 25 | getMetricsHandler :: ReaderHandler HttpServerConfig Text 26 | getMetricsHandler = liftIO $ Text.decodeUtf8 . BS.toStrict <$> P.exportMetricsAsText 27 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/Rest/Endpoint/Params.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TypeOperators #-} 3 | 4 | module Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.Params where 5 | 6 | import Control.Lens (view) 7 | import Data.Aeson.Types qualified as Aeson 8 | import Marconi.Cardano.ChainIndex.Api.Types (HttpServerConfig, configParams) 9 | import Marconi.Core.JsonRpc (ReaderHandler) 10 | import Servant (Get, JSON, (:>)) 11 | 12 | ------------------ 13 | -- Method types -- 14 | ------------------ 15 | 16 | type GetParams = "params" :> Get '[JSON] Aeson.Value 17 | 18 | -------------- 19 | -- Handlers -- 20 | -------------- 21 | 22 | -- | Returns params given through CLI as a JSON REST response. 23 | getParamsHandler :: ReaderHandler HttpServerConfig Aeson.Value 24 | getParamsHandler = view configParams 25 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/Rest/Endpoint/TargetAddresses.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TypeOperators #-} 3 | 4 | module Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.TargetAddresses where 5 | 6 | import Cardano.Api (serialiseAddress) 7 | import Control.Lens (view) 8 | import Data.Text (Text) 9 | import Marconi.Cardano.ChainIndex.Api.Types (HttpServerConfig, configTrackedAddresses) 10 | import Marconi.Core.JsonRpc (ReaderHandler) 11 | import Servant (Get, JSON, (:>)) 12 | 13 | ------------------ 14 | -- Method types -- 15 | ------------------ 16 | 17 | type GetTargetAddresses = "addresses" :> Get '[JSON] [Text] 18 | 19 | ---------------------------- 20 | -- Query and result types -- 21 | ---------------------------- 22 | 23 | -- | Prints TargetAddresses Bech32 representation to the console 24 | getTargetAddressesHandler :: ReaderHandler HttpServerConfig [Text] 25 | getTargetAddressesHandler = do 26 | addresses <- view configTrackedAddresses 27 | pure $ serialiseAddress <$> addresses 28 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/Rest/Endpoint/Time.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TypeOperators #-} 3 | 4 | module Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.Time where 5 | 6 | import Control.Monad.Trans (liftIO) 7 | import Data.Time (defaultTimeLocale, formatTime, getCurrentTime) 8 | import Marconi.Cardano.ChainIndex.Api.Types (HttpServerConfig) 9 | import Marconi.Core.JsonRpc (ReaderHandler) 10 | import Servant (Get, PlainText, (:>)) 11 | 12 | ------------------ 13 | -- Method types -- 14 | ------------------ 15 | 16 | type GetTime = "time" :> Get '[PlainText] String 17 | 18 | ---------------------------- 19 | -- Query and result types -- 20 | ---------------------------- 21 | 22 | {- | Returns current time as REST response. Used for testing the http server outside of jsonrpc 23 | protocol. 24 | -} 25 | getTimeHandler :: ReaderHandler HttpServerConfig String 26 | getTimeHandler = timeString <$> liftIO getCurrentTime 27 | where 28 | timeString = formatTime defaultTimeLocale "%T" 29 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/Rest/Routes.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeOperators #-} 2 | 3 | module Marconi.Cardano.ChainIndex.Api.Rest.Routes (RestAPI) where 4 | 5 | import Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.Metrics (GetMetrics) 6 | import Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.Params (GetParams) 7 | import Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.TargetAddresses (GetTargetAddresses) 8 | import Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.Time (GetTime) 9 | import Servant ((:<|>)) 10 | 11 | -- | Routes for the REST API 12 | type RestAPI = 13 | GetTime 14 | :<|> GetParams 15 | :<|> GetTargetAddresses 16 | :<|> GetMetrics 17 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/Rest/Server.hs: -------------------------------------------------------------------------------- 1 | module Marconi.Cardano.ChainIndex.Api.Rest.Server (restApiServer) where 2 | 3 | import Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.Metrics (getMetricsHandler) 4 | import Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.Params (getParamsHandler) 5 | import Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.TargetAddresses (getTargetAddressesHandler) 6 | import Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.Time (getTimeHandler) 7 | import Marconi.Cardano.ChainIndex.Api.Rest.Routes (RestAPI) 8 | import Marconi.Cardano.ChainIndex.Api.Types (HttpServerConfig) 9 | import Marconi.Core.JsonRpc (ReaderServer) 10 | import Servant ((:<|>) ((:<|>))) 11 | 12 | -- | Handlers for the REST API 13 | restApiServer :: ReaderServer HttpServerConfig RestAPI 14 | restApiServer = 15 | getTimeHandler 16 | :<|> getParamsHandler 17 | :<|> getTargetAddressesHandler 18 | :<|> getMetricsHandler 19 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/Routes.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE DerivingStrategies #-} 3 | {-# LANGUAGE TypeOperators #-} 4 | 5 | -- | Defines REST and JSON-RPC routes 6 | module Marconi.Cardano.ChainIndex.Api.Routes ( 7 | module Marconi.Cardano.ChainIndex.Api.Routes, 8 | ) where 9 | 10 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Routes (JsonRpcAPI) 11 | import Marconi.Cardano.ChainIndex.Api.Rest.Routes (RestAPI) 12 | import Marconi.Cardano.Core.Orphans () 13 | import Servant.API ((:<|>)) 14 | 15 | -- | marconi-cardano-chain-index APIs 16 | type API = JsonRpcAPI :<|> RestAPI 17 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Api/Types.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | 3 | module Marconi.Cardano.ChainIndex.Api.Types ( 4 | HttpServerConfig (HttpServerConfig), 5 | configTrace, 6 | configPort, 7 | configSecurityParam, 8 | configTrackedAddresses, 9 | configParams, 10 | configQueryables, 11 | ) where 12 | 13 | import Cardano.Api (AddressAny) 14 | import Cardano.BM.Trace (Trace) 15 | import Control.Lens (makeLenses) 16 | import Data.Aeson qualified as Aeson 17 | import Data.Text (Text) 18 | import Marconi.Cardano.ChainIndex.Indexers (MarconiCardanoQueryables) 19 | import Marconi.Cardano.Core.Types (SecurityParam) 20 | 21 | data HttpServerConfig = HttpServerConfig 22 | { _configTrace :: !(Trace IO Text) 23 | , _configPort :: !Int 24 | , _configSecurityParam :: !SecurityParam 25 | , _configTrackedAddresses :: ![AddressAny] 26 | , _configParams :: !Aeson.Value 27 | , _configQueryables :: !MarconiCardanoQueryables 28 | } 29 | 30 | makeLenses 'HttpServerConfig 31 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Git/Rev.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | {-# LANGUAGE ForeignFunctionInterface #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | {-# LANGUAGE TemplateHaskell #-} 5 | 6 | module Marconi.Cardano.ChainIndex.Git.Rev ( 7 | gitRev, 8 | ) where 9 | 10 | import Data.Text (Text) 11 | import Data.Text qualified as T 12 | 13 | import Foreign.C.String (CString) 14 | import GHC.Foreign (peekCStringLen) 15 | import Marconi.Cardano.ChainIndex.Git.RevFromGit (gitRevFromGit) 16 | import System.IO (utf8) 17 | import System.IO.Unsafe (unsafeDupablePerformIO) 18 | 19 | foreign import ccall "&_marconi_git_rev" c_gitrev :: CString 20 | 21 | gitRev :: Text 22 | gitRev 23 | | gitRevEmbed /= zeroRev = gitRevEmbed 24 | | T.null fromGit = zeroRev 25 | | otherwise = fromGit 26 | where 27 | -- Git revision embedded after compilation using 28 | -- Data.FileEmbed.injectWith. If nothing has been injected, 29 | -- this will be filled with 0 characters. 30 | gitRevEmbed :: Text 31 | gitRevEmbed = T.pack $ drop 28 $ unsafeDupablePerformIO (peekCStringLen utf8 (c_gitrev, 68)) 32 | 33 | fromGit :: Text 34 | 35 | -- Git revision found during compilation by running git. If 36 | -- git could not be run, then this will be empty. 37 | -- cross compiling to arm fails; due to a linker bug 38 | #if defined(arm_HOST_ARCH) 39 | fromGit = "" 40 | #else 41 | fromGit = T.strip (T.pack $(gitRevFromGit)) 42 | #endif 43 | 44 | zeroRev :: Text 45 | zeroRev = "0000000000000000000000000000000000000000" 46 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/src/Marconi/Cardano/ChainIndex/Git/RevFromGit.hs: -------------------------------------------------------------------------------- 1 | module Marconi.Cardano.ChainIndex.Git.RevFromGit ( 2 | gitRevFromGit, 3 | ) where 4 | 5 | import Control.Exception (catch) 6 | import Language.Haskell.TH qualified as TH 7 | import System.Exit (ExitCode (ExitFailure, ExitSuccess)) 8 | import System.IO qualified as IO 9 | import System.IO.Error (isDoesNotExistError) 10 | import System.Process (readProcessWithExitCode) 11 | 12 | {- | Git revision found by running git rev-parse. If git could not be 13 | executed, then this will be an empty string. 14 | -} 15 | gitRevFromGit :: TH.Q TH.Exp 16 | gitRevFromGit = 17 | TH.LitE . TH.StringL <$> TH.runIO runGitRevParse 18 | where 19 | runGitRevParse :: IO String 20 | runGitRevParse = do 21 | (exitCode, output, errorMessage) <- 22 | readProcessWithExitCode_ "git" ["rev-parse", "--verify", "HEAD"] "" 23 | case exitCode of 24 | ExitSuccess -> pure output 25 | ExitFailure _ -> do 26 | IO.hPutStrLn IO.stderr $ "WARNING: " ++ errorMessage 27 | pure "" 28 | 29 | readProcessWithExitCode_ :: FilePath -> [String] -> String -> IO (ExitCode, String, String) 30 | readProcessWithExitCode_ cmd args input = 31 | catch (readProcessWithExitCode cmd args input) $ \e -> 32 | if isDoesNotExistError e 33 | then return (ExitFailure 127, "", show e) 34 | else return (ExitFailure 999, "", show e) 35 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test-lib/Test/Marconi/Cardano/ChainIndex/Api/HttpServer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE NumericUnderscores #-} 2 | 3 | module Test.Marconi.Cardano.ChainIndex.Api.HttpServer where 4 | 5 | import Cardano.Api qualified as C 6 | import Control.Concurrent (threadDelay) 7 | import Control.Exception (bracket, throwIO) 8 | import Control.Monad.Except (runExceptT) 9 | import Control.Monad.Reader (runReaderT) 10 | import Marconi.Core.JsonRpc (ReaderHandler) 11 | import Network.JsonRpc.Types (JsonRpcErr) 12 | import Test.Gen.Marconi.Cardano.Core.Mockchain qualified as Mockchain 13 | import Test.Marconi.Cardano.ChainIndex.Indexers qualified as Test.Indexers 14 | 15 | {- | Wrapper for instantiating indexers, indexing mockchain events, querying via a handler, 16 | and closing the indexers. 17 | -} 18 | queryHandlerWithIndexers 19 | :: Mockchain.MockchainWithInfoAndDistance C.BabbageEra 20 | -> IO (config, Test.Indexers.TestBuildIndexersResult) 21 | -> ReaderHandler config (Either (JsonRpcErr String) result) 22 | -> IO result 23 | queryHandlerWithIndexers chain buildAction queryAction = bracket buildAction (Test.Indexers.closeIndexers . snd) $ 24 | \(httpConfig, indexersConfig) -> do 25 | Test.Indexers.indexAllWithMockchain indexersConfig chain 26 | threadDelay 500_000 27 | runReaderT (runExceptT queryAction) httpConfig 28 | >>= either throwIO pure 29 | >>= either (fail . show) pure 30 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Main (main) where 4 | 5 | import Spec.Marconi.Cardano.ChainIndex.Api.Routes qualified as Api.Routes 6 | import Spec.Marconi.Cardano.ChainIndex.Api.Routes qualified as Routes 7 | import Spec.Marconi.Cardano.ChainIndex.CLI qualified as CLI 8 | import Spec.Marconi.Cardano.ChainIndex.CLIInputValidation qualified as CLIInput 9 | import Test.Tasty (TestTree, defaultMain, testGroup) 10 | 11 | main :: IO () 12 | main = defaultMain tests 13 | 14 | tests :: TestTree 15 | tests = 16 | testGroup 17 | "Marconi" 18 | [ CLIInput.tests 19 | , CLI.tests 20 | , Routes.tests 21 | , Api.Routes.tests 22 | ] 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAddress-MissingBech32Prefix.golden: -------------------------------------------------------------------------------- 1 | option --addresses-to-index: Invalid address (not a valid Bech32 address representation): "x8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gt7r0vd4msrxnuwnccdxlhdjar77j6lg0wypcc9uar5d2shskhj42g" 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAddress-badFormat.golden: -------------------------------------------------------------------------------- 1 | option --addresses-to-index: Invalid address (not a valid Bech32 address representation): "notAnAddress" 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAddressPrefix-stake1.golden: -------------------------------------------------------------------------------- 1 | option --addresses-to-index: Invalid address (not a valid Bech32 address representation): "stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw" 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAddressPrefix-stake_test.golden: -------------------------------------------------------------------------------- 1 | option --addresses-to-index: Invalid address (not a valid Bech32 address representation): "stake_test17rphkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcljw6kf" 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAddress_1CharShort.golden: -------------------------------------------------------------------------------- 1 | option --addresses-to-index: Invalid address (not a valid Bech32 address representation): "addr1x8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gt7r0vd4msrxnuwnccdxlhdjar77j6lg0wypcc9uar5d2shskhj42" 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAssetId-assetName-invalidBase16Format.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Expected Base16-encoded bytestring, but got 0000notAToken0; invalid character at offset: 4 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAssetId-assetName-invalidBase16Length.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Expected Base16-encoded bytestring, but got deadbeeef; invalid bytestring size 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAssetId-assetName-tooLong.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Failed to deserialise deadbeef0000000000000000000000000000000000000000000000000000000000 as AssetName. Unable to deserialise AssetName (the bytestring should be no longer than 32 bytes long which corresponds to a hex representation of 64 characters) 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAssetId-policyId-1ByteLong.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Failed to deserialise 1cdc58c3b6d1ab11dd047ac9e3a2ec26aabf0839abe37b791cb810f2f2 as PolicyId. Incorrect number of bytes 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAssetId-policyId-1ByteShort.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Failed to deserialise 1cdc58c3b6d1ab11dd047ac9e3a2ec26aabf0839abe37b791cb810 as PolicyId. Incorrect number of bytes 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAssetId-policyId-1HexCharLong.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Expected Base16-encoded bytestring, but got 1cdc58c3b6d1ab11dd047ac9e3a2ec26aabf0839abe37b791cb810f2f; invalid bytestring size 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAssetId-policyId-1HexCharShort.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Expected Base16-encoded bytestring, but got 1cdc58c3b6d1ab11dd047ac9e3a2ec26aabf0839abe37b791cb810f; invalid bytestring size 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/CLIInputValidation/invalidAssetId-policyId-badBase16Format.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Expected Base16-encoded bytestring, but got 00aPolicyIdOfCorrectLength000000000000000000000000000000000000; invalid character at offset: 3 2 | 3 | Usage: marconi-cardano-chain-index [--version] [--debug] 4 | (-s|--socket-path FILE-PATH) 5 | (--mainnet | --testnet-magic NATURAL) 6 | [--start-from-genesis | 7 | --start-from-last-sync-points | 8 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | 11 | --max-retry-time NATURAL] 12 | [--batch-size INT] (-d|--db-dir DIR) 13 | [--enable-txoutref] [--disable-utxo] 14 | [--disable-address-datum] 15 | [--disable-script-tx] 16 | [--disable-epoch-stakepool-size] 17 | [--disable-mintburn] [--http-port INT] 18 | [(-a|--addresses-to-index BECH32-ADDRESS)] 19 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 20 | [--node-config-path ARG] 21 | 22 | marconi 23 | -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/Cli/marconi-cardano-chain-index___disable_address_data.help: -------------------------------------------------------------------------------- 1 | Invalid option `--disable-address-data' 2 | 3 | Did you mean this? 4 | --disable-address-datum 5 | 6 | Usage: marconi-cardano-chain-index [--version] [--debug] 7 | (-s|--socket-path FILE-PATH) 8 | (--mainnet | --testnet-magic NATURAL) 9 | [--start-from-genesis | 10 | --start-from-last-sync-points | 11 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 12 | [--initial-retry-time NATURAL] 13 | [--no-max-retry-time | 14 | --max-retry-time NATURAL] 15 | [--batch-size INT] (-d|--db-dir DIR) 16 | [--enable-txoutref] [--disable-utxo] 17 | [--disable-address-datum] 18 | [--disable-script-tx] 19 | [--disable-epoch-stakepool-size] 20 | [--disable-mintburn] [--http-port INT] 21 | [(-a|--addresses-to-index BECH32-ADDRESS)] 22 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 23 | [--node-config-path ARG] 24 | 25 | marconi -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/Routes/address-utxo-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "blockHeaderHash": "6161616161616161616161616161616161616161616161616161616161616161", 4 | "blockNo": 1, 5 | "datum": null, 6 | "datumHash": null, 7 | "epochNo": 0, 8 | "slotNo": 1, 9 | "spentBy": null, 10 | "txId": "ec7d3bd7c6a3a31368093b077af0db46ceac77956999eb842373e08c6420f000", 11 | "txIndexInBlock": 0, 12 | "txInputs": [ 13 | { 14 | "txId": "2f1f574c0365afd9865332eec4ff75e599d80c525afc7b7d6e38d27d0a01bf47", 15 | "txIx": 1 16 | } 17 | ], 18 | "txIx": 0, 19 | "value": { 20 | "": { 21 | "": 10 22 | } 23 | } 24 | }, 25 | { 26 | "blockHeaderHash": "6161616161616161616161616161616161616161616161616161616161616161", 27 | "blockNo": 1, 28 | "datum": { 29 | "int": 34 30 | }, 31 | "datumHash": "6d683f9bd0c3b874cdf3da1793b5eb0ea73a074d3e4b66bc62279b09d387fa8d", 32 | "epochNo": 0, 33 | "slotNo": 1, 34 | "spentBy": { 35 | "slotNo": 12, 36 | "txId": "2e19f40cdf462444234d0de049163d5269ee1150feda868560315346dd12807d" 37 | }, 38 | "txId": "ec7d3bd7c6a3a31368093b077af0db46ceac77956999eb842373e08c6420f000", 39 | "txIndexInBlock": 0, 40 | "txInputs": [ 41 | { 42 | "txId": "ec7d3bd7c6a3a31368093b077af0db46ceac77956999eb842373e08c6420f000", 43 | "txIx": 0 44 | } 45 | ], 46 | "txIx": 0, 47 | "value": { 48 | "": { 49 | "": 1 50 | } 51 | } 52 | } 53 | ] -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/Routes/current-synced-point-response-1.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/Routes/current-synced-point-response-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "blockHeaderHash": "6161616161616161616161616161616161616161616161616161616161616161", 3 | "blockNo": 64903, 4 | "blockTimestamp": 0, 5 | "epochNo": 6, 6 | "nodeTip": { 7 | "blockHeaderHash": "6161616161616161616161616161616161616161616161616161616161616161", 8 | "blockNo": 64903, 9 | "slotNo": 1 10 | }, 11 | "slotNo": 1 12 | } -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/Routes/epoch-nonce-at-genesis-response.json: -------------------------------------------------------------------------------- 1 | { 2 | "blockHeaderHash": null, 3 | "blockNo": null, 4 | "epochNo": null, 5 | "nonce": null, 6 | "slotNo": null 7 | } -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/Routes/epoch-nonce-byron-response.json: -------------------------------------------------------------------------------- 1 | { 2 | "blockHeaderHash": "578f3cb70f4153e1622db792fea9005c80ff80f83df028210c7a914fb780a6f6", 3 | "blockNo": 21645, 4 | "epochNo": 2, 5 | "nonce": null, 6 | "slotNo": 1382422 7 | } -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/Routes/epoch-nonce-response.json: -------------------------------------------------------------------------------- 1 | { 2 | "blockHeaderHash": "578f3cb70f4153e1622db792fea9005c80ff80f83df028210c7a914fb780a6f6", 3 | "blockNo": 21645, 4 | "epochNo": 2, 5 | "nonce": "ce4a80f49c44c21d7114d93fe5f992a2f9de6bad4a03a5df7e7403004ebe16fc", 6 | "slotNo": 1382422 7 | } -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/Routes/epoch-stakepooldelegation-at-genesis-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "blockHeaderHash": null, 4 | "blockNo": 0, 5 | "epochNo": 0, 6 | "lovelace": 100000000000000, 7 | "poolId": "pool1z22x50lqsrwent6en0llzzs9e577rx7n3mv9kfw7udwa2rf42fa", 8 | "slotNo": null 9 | } 10 | ] -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/Routes/epoch-stakepooldelegation-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "blockHeaderHash": "578f3cb70f4153e1622db792fea9005c80ff80f83df028210c7a914fb780a6f6", 4 | "blockNo": 64903, 5 | "epochNo": 123, 6 | "lovelace": 100000000000000, 7 | "poolId": "pool1z22x50lqsrwent6en0llzzs9e577rx7n3mv9kfw7udwa2rf42fa", 8 | "slotNo": 1382422 9 | }, 10 | { 11 | "blockHeaderHash": "578f3cb70f4153e1622db792fea9005c80ff80f83df028210c7a914fb780a6f6", 12 | "blockNo": 64903, 13 | "epochNo": 123, 14 | "lovelace": 100000000000000, 15 | "poolId": "pool1547tew8vmuj0g6vj3k5jfddudextcw6hsk2hwgg6pkhk7lwphe6", 16 | "slotNo": 1382422 17 | }, 18 | { 19 | "blockHeaderHash": "578f3cb70f4153e1622db792fea9005c80ff80f83df028210c7a914fb780a6f6", 20 | "blockNo": 64903, 21 | "epochNo": 123, 22 | "lovelace": 100000000000000, 23 | "poolId": "pool174mw7e20768e8vj4fn8y6p536n8rkzswsapwtwn354dckpjqzr8", 24 | "slotNo": 1382422 25 | } 26 | ] -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Golden/Routes/mintingpolicyhash-tx-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "assetName": "", 4 | "blockHeaderHash": "6161616161616161616161616161616161616161616161616161616161616161", 5 | "blockNo": 1047, 6 | "burnAmount": 10, 7 | "isStable": true, 8 | "redeemer": { 9 | "int": 34 10 | }, 11 | "redeemerHash": "6d683f9bd0c3b874cdf3da1793b5eb0ea73a074d3e4b66bc62279b09d387fa8d", 12 | "slotNo": 1, 13 | "txId": "ec7d3bd7c6a3a31368093b077af0db46ceac77956999eb842373e08c6420f000" 14 | } 15 | ] -------------------------------------------------------------------------------- /marconi-cardano-chain-index/test/Spec/Marconi/Cardano/ChainIndex/Api/Gen.hs: -------------------------------------------------------------------------------- 1 | module Spec.Marconi.Cardano.ChainIndex.Api.Gen where 2 | 3 | import Cardano.Api qualified as C 4 | import Data.String (fromString) 5 | import Hedgehog ( 6 | Gen, 7 | ) 8 | import Hedgehog.Gen qualified as Gen 9 | import Hedgehog.Range qualified as Range 10 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.MintBurnToken ( 11 | BurnTokenEventResult (BurnTokenEventResult), 12 | GetBurnTokenEventsParams (GetBurnTokenEventsParams), 13 | ) 14 | import Test.Gen.Cardano.Api.Typed qualified as CGen 15 | import Test.Gen.Marconi.Cardano.Core.Types qualified as Gen 16 | 17 | genBurnTokenEventResult :: Maybe C.HashableScriptData -> Gen BurnTokenEventResult 18 | genBurnTokenEventResult hsd = 19 | BurnTokenEventResult 20 | <$> (Just <$> Gen.genSlotNo) 21 | <*> (Just <$> Gen.genHashBlockHeader) 22 | <*> Gen.genBlockNo 23 | <*> CGen.genTxId 24 | <*> pure (fmap C.hashScriptDataBytes hsd) 25 | <*> pure (fmap C.getScriptData hsd) 26 | <*> CGen.genAssetName 27 | <*> Gen.genQuantity (Range.linear 0 10) 28 | <*> pure True 29 | 30 | genGetBurnTokenEventsParams :: Gen GetBurnTokenEventsParams 31 | genGetBurnTokenEventsParams = 32 | GetBurnTokenEventsParams 33 | <$> (C.PolicyId <$> CGen.genScriptHash) 34 | <*> (fmap fromString <$> Gen.maybe (Gen.string (Range.linear 1 10) Gen.alphaNum)) 35 | <*> (Gen.maybe $ C.SlotNo . fromInteger <$> Gen.integral (Range.linear 1 10)) 36 | <*> Gen.maybe CGen.genTxId 37 | -------------------------------------------------------------------------------- /marconi-cardano-core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | <a id='changelog-0.0.1'></a> 3 | # 1.2.0.0 — 2023-11-22 4 | 5 | ## Added 6 | 7 | - Initialized the package 8 | 9 | ## Changed 10 | 11 | ## Fixed 12 | -------------------------------------------------------------------------------- /marconi-cardano-core/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Input Output Global, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /marconi-cardano-core/cardano-api-extended/src/Cardano/Api/Extended.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-missing-import-lists #-} 2 | 3 | module Cardano.Api.Extended ( 4 | module Cardano.Api, 5 | 6 | -- * ExtLedgerState 7 | mkExtLedgerConfig, 8 | mkInitExtLedgerState, 9 | applyBlockExtLedgerState, 10 | 11 | -- * Block 12 | bimSlotNo, 13 | bimBlockHeaderHash, 14 | bimBlockNo, 15 | ) 16 | where 17 | 18 | import Cardano.Api 19 | import Cardano.Api.Extended.Block (bimBlockHeaderHash, bimBlockNo, bimSlotNo) 20 | import Cardano.Api.Extended.ExtLedgerState ( 21 | applyBlockExtLedgerState, 22 | mkExtLedgerConfig, 23 | mkInitExtLedgerState, 24 | ) 25 | -------------------------------------------------------------------------------- /marconi-cardano-core/cardano-api-extended/src/Cardano/Api/Extended/Block.hs: -------------------------------------------------------------------------------- 1 | module Cardano.Api.Extended.Block where 2 | 3 | import Cardano.Api qualified as C 4 | 5 | bimBlockNo :: C.BlockInMode C.CardanoMode -> C.BlockNo 6 | bimBlockNo (C.BlockInMode (C.Block (C.BlockHeader _ _ blockNo) _) _) = blockNo 7 | 8 | bimSlotNo :: C.BlockInMode C.CardanoMode -> C.SlotNo 9 | bimSlotNo (C.BlockInMode (C.Block (C.BlockHeader slotNo _ _) _) _) = slotNo 10 | 11 | bimBlockHeaderHash :: C.BlockInMode C.CardanoMode -> C.Hash C.BlockHeader 12 | bimBlockHeaderHash (C.BlockInMode (C.Block (C.BlockHeader _ bhh _) _) _) = bhh 13 | 14 | chainTipBlockNo :: C.ChainTip -> C.BlockNo 15 | chainTipBlockNo C.ChainTipAtGenesis = 0 16 | chainTipBlockNo (C.ChainTip _ _ bn) = bn 17 | -------------------------------------------------------------------------------- /marconi-cardano-core/cardano-api-extended/src/Cardano/Api/Extended/Gen.hs: -------------------------------------------------------------------------------- 1 | module Cardano.Api.Extended.Gen where 2 | 3 | import Cardano.Api qualified as C 4 | import Cardano.Crypto.Hash.Class qualified as Crypto 5 | import Hedgehog qualified as H 6 | import Hedgehog.Gen qualified as H.Gen 7 | import Hedgehog.Range qualified as H.Range 8 | 9 | {- | Temporary utility to generate @C.'TxId'@ whose values reasonably can be expected 10 | - to be unique over small collections of generated events. It's only purpose is to 11 | provide unique ids for testing. 12 | -} 13 | genTxId :: H.Gen C.TxId 14 | genTxId = C.TxId . Crypto.castHash . Crypto.hashWith id <$> H.Gen.bytes (H.Range.singleton 30) 15 | -------------------------------------------------------------------------------- /marconi-cardano-core/cardano-api-extended/src/Cardano/Api/Extended/IPC.hs: -------------------------------------------------------------------------------- 1 | module Cardano.Api.Extended.IPC ( 2 | mkLocalNodeConnectInfo, 3 | ) where 4 | 5 | import Cardano.Api qualified as C 6 | 7 | mkLocalNodeConnectInfo :: C.NetworkId -> FilePath -> C.LocalNodeConnectInfo C.CardanoMode 8 | mkLocalNodeConnectInfo networkId socketPath = 9 | C.LocalNodeConnectInfo 10 | { C.localConsensusModeParams = C.CardanoModeParams epochSlots 11 | , C.localNodeNetworkId = networkId 12 | , C.localNodeSocketPath = C.File socketPath 13 | } 14 | where 15 | -- This a parameter needed only for the Byron era. Since the Byron 16 | -- era is over and the parameter has never changed it is ok to 17 | -- hardcode this. See comment on `Cardano.Api.ConsensusModeParams` in 18 | -- cardano-node. 19 | epochSlots = C.EpochSlots 21600 -- TODO: is this configurable? see below 20 | -------------------------------------------------------------------------------- /marconi-cardano-core/cardano-api-extended/src/Cardano/Api/Extended/Shelley.hs: -------------------------------------------------------------------------------- 1 | module Cardano.Api.Extended.Shelley where 2 | 3 | import Cardano.Api qualified as C 4 | 5 | -- | Converts a 'ShelleyBasedEra' to 'EraInMode'. 6 | toShelleyEraInCardanoMode :: C.ShelleyBasedEra era -> C.EraInMode era C.CardanoMode 7 | toShelleyEraInCardanoMode C.ShelleyBasedEraShelley = C.ShelleyEraInCardanoMode 8 | toShelleyEraInCardanoMode C.ShelleyBasedEraAllegra = C.AllegraEraInCardanoMode 9 | toShelleyEraInCardanoMode C.ShelleyBasedEraMary = C.MaryEraInCardanoMode 10 | toShelleyEraInCardanoMode C.ShelleyBasedEraAlonzo = C.AlonzoEraInCardanoMode 11 | toShelleyEraInCardanoMode C.ShelleyBasedEraBabbage = C.BabbageEraInCardanoMode 12 | toShelleyEraInCardanoMode C.ShelleyBasedEraConway = C.ConwayEraInCardanoMode 13 | 14 | {- | Converts a 'CardanoEra' to the more specific 'ShelleyBasedEra'. Compare to 15 | @C.'shelleyBasedToCardanoEra'@, which takes a 'ShelleyBasedEra' as input. 16 | -} 17 | cardanoEraToShelleyBasedEra :: C.CardanoEra era -> Maybe (C.ShelleyBasedEra era) 18 | cardanoEraToShelleyBasedEra C.ByronEra = Nothing 19 | cardanoEraToShelleyBasedEra C.ShelleyEra = Just C.ShelleyBasedEraShelley 20 | cardanoEraToShelleyBasedEra C.AllegraEra = Just C.ShelleyBasedEraAllegra 21 | cardanoEraToShelleyBasedEra C.MaryEra = Just C.ShelleyBasedEraMary 22 | cardanoEraToShelleyBasedEra C.AlonzoEra = Just C.ShelleyBasedEraAlonzo 23 | cardanoEraToShelleyBasedEra C.BabbageEra = Just C.ShelleyBasedEraBabbage 24 | cardanoEraToShelleyBasedEra C.ConwayEra = Just C.ShelleyBasedEraConway 25 | -------------------------------------------------------------------------------- /marconi-cardano-core/src/Marconi/Cardano/Core/Extract/WithDistance.hs: -------------------------------------------------------------------------------- 1 | -- | Attach distance to the tip to an event 2 | module Marconi.Cardano.Core.Extract.WithDistance ( 3 | WithDistance (WithDistance), 4 | attachDistance, 5 | chainDistance, 6 | getEvent, 7 | ) where 8 | 9 | import Cardano.Api qualified as C 10 | import Data.Word (Word64) 11 | import Marconi.Core qualified as Core 12 | 13 | -- | A type to store the distance, in blocks, to the tip of the chain 14 | data WithDistance event = WithDistance Word64 event 15 | deriving (Show, Functor, Foldable, Traversable) 16 | 17 | chainDistance :: WithDistance event -> Word64 18 | chainDistance (WithDistance distance _) = distance 19 | 20 | getEvent :: WithDistance event -> event 21 | getEvent (WithDistance _ event) = event 22 | 23 | type instance Core.Point (WithDistance event) = C.ChainPoint 24 | 25 | -- | Attach the distance (in blocks) to the tip to an event 26 | attachDistance 27 | :: C.BlockNo 28 | -> C.ChainTip 29 | -> event 30 | -> WithDistance event 31 | attachDistance currentBlockNo tip = 32 | let tipBlockNo = case tip of 33 | C.ChainTipAtGenesis -> 0 34 | C.ChainTip _ _ no -> no 35 | distance = C.unBlockNo $ tipBlockNo - currentBlockNo 36 | in WithDistance distance 37 | -------------------------------------------------------------------------------- /marconi-cardano-core/src/Marconi/Cardano/Core/Logger.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DerivingStrategies #-} 2 | {-# LANGUAGE FlexibleInstances #-} 3 | 4 | -- | Common loggers for marconi 5 | module Marconi.Cardano.Core.Logger ( 6 | BM.nullTracer, 7 | defaultStdOutLogger, 8 | mkMarconiTrace, 9 | marconiFormatting, 10 | MarconiTrace, 11 | ) where 12 | 13 | import Cardano.BM.Backend.Switchboard qualified as BM 14 | import Cardano.BM.Configuration qualified as BM 15 | import Cardano.BM.Data.Trace (Trace) 16 | import Cardano.BM.Setup qualified as BM 17 | import Cardano.BM.Tracing (contramap) 18 | import Cardano.BM.Tracing qualified as BM 19 | import Data.Text (Text) 20 | import Marconi.Cardano.Core.Orphans () 21 | import Prettyprinter (Doc) 22 | import Prettyprinter qualified as Pretty 23 | import Prettyprinter.Render.Text qualified as Pretty 24 | 25 | -- | Alias for a 'Doc' tracer, it is the 'Trace' used throughout the Marconi application 26 | type MarconiTrace m = Trace m (Doc ()) 27 | 28 | -- | StdOut logger, only log stuff above the Info level 29 | defaultStdOutLogger :: Text -> BM.Severity -> IO (Trace IO Text, BM.Switchboard Text) 30 | defaultStdOutLogger appName logLevel = do 31 | cfg <- BM.defaultConfigStdout 32 | BM.setMinSeverity cfg logLevel 33 | BM.setupTrace_ cfg appName 34 | 35 | -- | Builds a 'MarconiTrace' from a base tracer. 36 | mkMarconiTrace :: Trace m Text -> MarconiTrace m 37 | mkMarconiTrace = 38 | contramap . fmap . fmap $ marconiFormatting 39 | 40 | marconiFormatting :: Pretty.Doc ann -> Text 41 | marconiFormatting = 42 | Pretty.renderStrict 43 | . Pretty.layoutPretty Pretty.defaultLayoutOptions 44 | -------------------------------------------------------------------------------- /marconi-cardano-core/src/Marconi/Cardano/Core/Transformer/WithSyncStats/Backend/Prometheus.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Marconi.Cardano.Core.Transformer.WithSyncStats.Backend.Prometheus where 4 | 5 | import Data.Time (NominalDiffTime) 6 | import Marconi.Cardano.Core.Transformer.WithSyncStats ( 7 | LastSyncStats (LastSyncStats), 8 | StatsBackend (StatsBackend), 9 | emptyLastSyncStats, 10 | ) 11 | import Prometheus qualified as P 12 | 13 | {- | Creates a simple Prometheus backend which keeps track of the number of processed blocks and 14 | the number of rollbacks. 15 | 16 | Takes a @NominalDiffTime@ which determines how frequently we send stats to Prometheus. 17 | -} 18 | mkPrometheusBackend :: NominalDiffTime -> IO StatsBackend 19 | mkPrometheusBackend timeBetween = do 20 | processedBlocksCounter <- 21 | P.register $ 22 | P.counter (P.Info "processed_blocks_counter" "Number of processed blocks") 23 | processedRollbacksCounter <- 24 | P.register $ 25 | P.counter (P.Info "processed_rollbacks_counter" "Number of processed rollbacks") 26 | pure $ 27 | StatsBackend 28 | (updateMetrics processedBlocksCounter processedRollbacksCounter) 29 | timeBetween 30 | emptyLastSyncStats 31 | where 32 | updateMetrics :: P.Counter -> P.Counter -> LastSyncStats -> IO () 33 | updateMetrics rollforwards rollbacks (LastSyncStats fw bw _ _ _) = do 34 | _ <- P.addCounter rollforwards (fromInteger . toInteger $ fw) 35 | _ <- P.addCounter rollbacks (fromInteger . toInteger $ bw) 36 | pure () 37 | -------------------------------------------------------------------------------- /marconi-cardano-core/test/Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ExplicitNamespaces #-} 2 | 3 | module Main (main) where 4 | 5 | import Spec.Marconi.Cardano.Core.Logger qualified as Logger 6 | import Spec.Marconi.Cardano.Core.Orphans qualified as Orphans 7 | import Test.Tasty (TestTree, defaultMain, testGroup) 8 | 9 | main :: IO () 10 | main = defaultMain tests 11 | 12 | tests :: TestTree 13 | tests = 14 | testGroup 15 | "marconi-cardano-core" 16 | [Logger.tests, Orphans.tests] 17 | -------------------------------------------------------------------------------- /marconi-cardano-core/test/Spec/Marconi/Cardano/Core/Logger/Golden/start-from-later-point-logging.txt: -------------------------------------------------------------------------------- 1 | Starting from ChainPoint(Slot 50000, BlockHash 6161616161616161616161616161616161616161616161616161616161616161). 2 | Synchronising (80.00%). Current synced point is ChainPoint(Slot 80000, BlockHash 6161616161616161616161616161616161616161616161616161616161616161) and current node tip is ChainTip(Slot 100000, BlockHash 6161616161616161616161616161616161616161616161616161616161616161, BlockNo 100000). Processed 30 blocks and 0 rollbacks in the last 10s (3 blocks/s). 3 | Synchronising (90.00%). Current synced point is ChainPoint(Slot 90000, BlockHash 6161616161616161616161616161616161616161616161616161616161616161) and current node tip is ChainTip(Slot 100000, BlockHash 6161616161616161616161616161616161616161616161616161616161616161, BlockNo 100000). Processed 10 blocks and 0 rollbacks in the last 10s (1 blocks/s). 4 | Synchronising (95.00%). Current synced point is ChainPoint(Slot 95000, BlockHash 6161616161616161616161616161616161616161616161616161616161616161) and current node tip is ChainTip(Slot 100000, BlockHash 6161616161616161616161616161616161616161616161616161616161616161, BlockNo 100000). Processed 5 blocks and 2 rollbacks in the last 10s (0 blocks/s). 5 | Synchronising (almost synced). Current synced point is ChainPoint(Slot 99995, BlockHash 6161616161616161616161616161616161616161616161616161616161616161) and current node tip is ChainTip(Slot 100000, BlockHash 6161616161616161616161616161616161616161616161616161616161616161, BlockNo 100000). Processed 5 blocks and 1 rollbacks in the last 10s (0 blocks/s). 6 | Synchronising (almost synced). Current synced point is ChainPoint(Slot 99999, BlockHash 6161616161616161616161616161616161616161616161616161616161616161) and current node tip is ChainTip(Slot 100000, BlockHash 6161616161616161616161616161616161616161616161616161616161616161, BlockNo 100000). Processed 5 blocks and 1 rollbacks in the last 10s (0 blocks/s). 7 | Fully synchronised. Current synced point is ChainPoint(Slot 100000, BlockHash 6161616161616161616161616161616161616161616161616161616161616161) and current node tip is ChainTip(Slot 100000, BlockHash 6161616161616161616161616161616161616161616161616161616161616161, BlockNo 100000). Processed 5 blocks and 3 rollbacks in the last 10s. -------------------------------------------------------------------------------- /marconi-cardano-indexers/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | <a id='changelog-0.0.1'></a> 3 | # 1.2.0.0 — 2023-11-29 4 | 5 | ## Added 6 | 7 | - Initialized the package 8 | 9 | ## Changed 10 | 11 | ## Fixed 12 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Input Output Global, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/src/Marconi/Cardano/Indexers/Coordinator.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ExistentialQuantification #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | 4 | -- | Helper to create a worker for a Coordinator 5 | module Marconi.Cardano.Indexers.Coordinator ( 6 | coordinatorWorker, 7 | standardCoordinator, 8 | syncStatsCoordinator, 9 | ) where 10 | 11 | import Cardano.BM.Tracing (Trace) 12 | import Control.Monad.IO.Class (MonadIO (liftIO)) 13 | import Control.Monad.Trans (lift) 14 | import Data.Text (Text) 15 | import Marconi.Cardano.Core.Logger (MarconiTrace) 16 | import Marconi.Cardano.Core.Orphans qualified () 17 | import Marconi.Cardano.Core.Transformer.WithSyncStats ( 18 | WithSyncStats, 19 | withSyncStats, 20 | ) 21 | import Marconi.Cardano.Core.Transformer.WithSyncStats.Backend.Printer (mkLogBackend) 22 | import Marconi.Cardano.Core.Transformer.WithSyncStats.Backend.Prometheus (mkPrometheusBackend) 23 | import Marconi.Core qualified as Core 24 | 25 | standardCoordinator 26 | :: (Ord (Core.Point event)) 27 | => Trace IO (Core.IndexerEvent (Core.Point event)) 28 | -> [Core.Worker event (Core.Point event)] 29 | -> IO (Core.WithTrace IO Core.Coordinator event) 30 | standardCoordinator logger = Core.withTraceM logger . Core.mkCoordinator 31 | 32 | syncStatsCoordinator 33 | :: (Ord (Core.Point event)) 34 | => Trace IO (Core.IndexerEvent (Core.Point event)) 35 | -> MarconiTrace IO 36 | -> [Core.Worker event (Core.Point event)] 37 | -> IO (WithSyncStats (Core.WithTrace IO Core.Coordinator) event) 38 | syncStatsCoordinator logger prettyLogger workers = do 39 | prometheus <- mkPrometheusBackend 60 40 | fmap (withSyncStats [mkLogBackend prettyLogger 10, prometheus]) 41 | . Core.withTraceM logger 42 | . Core.mkCoordinator 43 | $ workers 44 | 45 | coordinatorWorker 46 | :: (MonadIO m, Ord (Core.Point b)) 47 | => Text 48 | -> Trace IO (Core.IndexerEvent (Core.Point b)) 49 | -> (a -> IO (Maybe b)) 50 | -> [Core.Worker b (Core.Point b)] 51 | -> m (Core.WorkerIndexer IO a b (Core.WithTrace IO Core.Coordinator)) 52 | coordinatorWorker name logger extract workers = liftIO $ do 53 | coordinator <- standardCoordinator logger workers 54 | Core.createWorkerWithPreprocessing name (Core.traverseMaybeEvent $ lift . extract) coordinator 55 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test-lib/Test/Gen/Marconi/Cardano/Indexers/Datum.hs: -------------------------------------------------------------------------------- 1 | -- | Helpers and generators for testing indexers of @Marconi.Cardano.Indexers.Datum@. 2 | module Test.Gen.Marconi.Cardano.Indexers.Datum where 3 | 4 | import Cardano.Api qualified as C 5 | import Data.List.NonEmpty (NonEmpty, nonEmpty) 6 | import Hedgehog qualified 7 | import Marconi.Cardano.Core.Extract.WithDistance (WithDistance (WithDistance)) 8 | import Marconi.Cardano.Indexers.Datum qualified as Datum 9 | import Marconi.Core qualified as Core 10 | import Test.Gen.Cardano.Api.Typed qualified as CGen 11 | import Test.Gen.Marconi.Cardano.Core.Mockchain qualified as Mockchain 12 | 13 | -- | Generate events for indexing with @Datum.'DatumIndexer'@. 14 | genDatumInfoEvents :: Hedgehog.Gen [Core.Timed C.ChainPoint (Maybe (NonEmpty Datum.DatumInfo))] 15 | genDatumInfoEvents = getTimedDatumsEvents <$> Mockchain.genMockchain 16 | 17 | -- | Generate a list of events from a mock chain with distance 18 | getTimedDatumsEventsWithDistance 19 | :: Mockchain.MockchainWithDistance era 20 | -> [Core.Timed C.ChainPoint (WithDistance (Maybe (NonEmpty Datum.DatumInfo)))] 21 | getTimedDatumsEventsWithDistance = map op 22 | where 23 | op (WithDistance d block) = WithDistance d <$> getBlockDatumsEvent block 24 | 25 | -- | Generate a list of events from a mock chain 26 | getTimedDatumsEvents 27 | :: Mockchain.Mockchain era 28 | -> [Core.Timed C.ChainPoint (Maybe (NonEmpty Datum.DatumInfo))] 29 | getTimedDatumsEvents = map getBlockDatumsEvent 30 | 31 | getBlockDatumsEvent 32 | :: Mockchain.MockBlock era 33 | -> Core.Timed C.ChainPoint (Maybe (NonEmpty Datum.DatumInfo)) 34 | getBlockDatumsEvent (Mockchain.MockBlock (C.BlockHeader slotNo bhh _) txs) = 35 | Core.Timed (C.ChainPoint slotNo bhh) $ 36 | nonEmpty $ 37 | concatMap (Datum.getDataFromTxBody . C.getTxBody) txs 38 | 39 | genDatumInfo :: Hedgehog.Gen Datum.DatumInfo 40 | genDatumInfo = 41 | Datum.DatumInfo 42 | <$> CGen.genHashScriptData 43 | <*> (C.getScriptData <$> CGen.genHashableScriptData) 44 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test-lib/Test/Gen/Marconi/Cardano/Indexers/Spent.hs: -------------------------------------------------------------------------------- 1 | -- | Helpers and generators for testing @Marconi.Cardano.Indexers.Spent@. 2 | module Test.Gen.Marconi.Cardano.Indexers.Spent where 3 | 4 | import Cardano.Api qualified as C 5 | import Data.List.NonEmpty (NonEmpty, nonEmpty) 6 | import Hedgehog qualified 7 | import Marconi.Cardano.Core.Extract.WithDistance (WithDistance (WithDistance)) 8 | import Marconi.Cardano.Indexers.Spent qualified as Spent 9 | import Marconi.Core qualified as Core 10 | import Test.Gen.Cardano.Api.Typed qualified as CGen 11 | import Test.Gen.Marconi.Cardano.Core.Mockchain qualified as Mockchain 12 | 13 | -- | Generate events for indexing with @Spent.'SpentIndexer'@. 14 | genSpentInfoEvents :: Hedgehog.Gen [Core.Timed C.ChainPoint (Maybe (NonEmpty Spent.SpentInfo))] 15 | genSpentInfoEvents = getTimedSpentsEvents <$> Mockchain.genMockchain 16 | 17 | -- | Generate a list of events from a mock chain with distance 18 | getTimedSpentsEventsWithDistance 19 | :: Mockchain.MockchainWithDistance era 20 | -> [Core.Timed C.ChainPoint (WithDistance (Maybe (NonEmpty Spent.SpentInfo)))] 21 | getTimedSpentsEventsWithDistance = map op 22 | where 23 | op (WithDistance d block) = WithDistance d <$> getBlockSpentsEvent block 24 | 25 | -- | Generate a list of events from a mock chain 26 | getTimedSpentsEvents 27 | :: Mockchain.Mockchain era 28 | -> [Core.Timed C.ChainPoint (Maybe (NonEmpty Spent.SpentInfo))] 29 | getTimedSpentsEvents = map getBlockSpentsEvent 30 | 31 | getBlockSpentsEvent 32 | :: Mockchain.MockBlock era 33 | -> Core.Timed C.ChainPoint (Maybe (NonEmpty Spent.SpentInfo)) 34 | getBlockSpentsEvent (Mockchain.MockBlock (C.BlockHeader slotNo blockHeaderHash _) txs) = 35 | Core.Timed (C.ChainPoint slotNo blockHeaderHash) $ 36 | nonEmpty $ 37 | concatMap (Spent.getInputs . Mockchain.getTxBody) txs 38 | 39 | genSpent :: Hedgehog.Gen Spent.SpentInfo 40 | genSpent = Spent.SpentInfo <$> CGen.genTxIn <*> CGen.genTxId 41 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec.hs: -------------------------------------------------------------------------------- 1 | module Main (main) where 2 | 3 | import Spec.Marconi.Cardano.DbSyncComparison qualified as DbSyncComparison 4 | import Spec.Marconi.Cardano.Indexers qualified as Indexers 5 | import Spec.Marconi.Cardano.Snapshot qualified as Snapshot 6 | import Test.Tasty (TestTree, defaultMain, testGroup) 7 | 8 | main :: IO () 9 | main = defaultMain tests 10 | 11 | tests :: TestTree 12 | tests = 13 | testGroup 14 | "marconi-cardano-indexers" 15 | [ Indexers.tests 16 | , Snapshot.tests 17 | , DbSyncComparison.tests 18 | ] 19 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/allegra/blockinfo-slot16588800.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":5086524,"time":"2020-12-16T21:44:51","epoch_no":236}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/allegra/blockinfo-slot18698800.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":5190357,"time":"2021-01-10T07:51:31","epoch_no":240}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/allegra/blockinfo-slot21340776.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":5321515,"time":"2021-02-09T21:44:27","epoch_no":246}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/allegra/spentinfo-slot16588800.out.golden: -------------------------------------------------------------------------------- 1 | [{"spentAtTxId" : "14bc6b152e3cc20fd40a08a224095ab53d6ee3c9a0e6abb9ce09f82ab5acf2a3", "spentTxOutRef" : "2ec7c4d2557f2498528491c8e7fa2fb2457000002b71d9c7978e32398a8a8211#0"}, {"spentAtTxId" : "1a71cf02a0a1fad32cb7c10a2564e3a5c463b73c40395301f454979aa7442bed", "spentTxOutRef" : "6d2b63ca61449c4ba5783b1c0236d136409b5738875ed1a8fcb02aec7c5d2169#1"}, {"spentAtTxId" : "c3a3877906b10027aec76e47c42a7956c579dc1f26b9edddf047a55770c633e3", "spentTxOutRef" : "7c685f9d47332cee3c54afba19b6e2bb8ab402c4bb29cef2353ec178ced2501b#1"}, {"spentAtTxId" : "fc0fae1c7a6019183e0f71df5a339baf5ccab458a14991caffe31c58df62c283", "spentTxOutRef" : "8483e307c335a843ea3668e567f88ee0be8ff97d2a0a168ffeb93e8d82704d61#1"}, {"spentAtTxId" : "ff796f7fc2a64e7571bd8c32f34c72ebf2db70cc415bdfe9a50aa06ca98e10d7", "spentTxOutRef" : "bb3b06d98e4e70a785a5e4ffc8ad77eb443ddf0c468d2adaa3764e44a072d652#1"}, {"spentAtTxId" : "ff796f7fc2a64e7571bd8c32f34c72ebf2db70cc415bdfe9a50aa06ca98e10d7", "spentTxOutRef" : "c717be8b2dd9ff54e90c96387a6f6722cd5a0d58f34e71a273b0c1c7fb614fc1#1"}, {"spentAtTxId" : "48d161e453c8a9567dda7913fb6430781a12249e25fb14b5f74f1272f88df47e", "spentTxOutRef" : "cc04c34ace0af80631f01aa604ea479f64cf8ee1d26b4d572ed34a971b61d08f#1"}, {"spentAtTxId" : "ff796f7fc2a64e7571bd8c32f34c72ebf2db70cc415bdfe9a50aa06ca98e10d7", "spentTxOutRef" : "dea2bab714e1631fb9f4dc51abea5d40145d417af09b80a14d10875f8bbfd083#0"}, {"spentAtTxId" : "7f4ffbd4512557e82fb2bf1436be747a76aa697be14b3670915c03074b85325d", "spentTxOutRef" : "f8756b99863bb5ed24612daa0f732b045c2f24be6a561d95a9c86a9c70a496bc#0"}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/allegra/spentinfo-slot18728839.out.golden: -------------------------------------------------------------------------------- 1 | [{"spentAtTxId" : "4fc1c90b8ee3e2e252d3fd74472f461809aa523e64502d08cd319709ad03826e", "spentTxOutRef" : "10994456e6486f9ebe6bab373129595b397c75fa1883fd79140f95d5abab4b4a#0"}, {"spentAtTxId" : "4fc1c90b8ee3e2e252d3fd74472f461809aa523e64502d08cd319709ad03826e", "spentTxOutRef" : "2351136f7cb6f97f653c9d1b05e8f0633529c91623a211a9347bcba534bd3a0b#0"}, {"spentAtTxId" : "444c4306a69cfe1e3733422b3e0f02eb602de3afecb956885bb4afb01f11d0a8", "spentTxOutRef" : "47503cf310d167b5bfbe119543954d71916204e65169be0f47e718d62a8e8063#0"}, {"spentAtTxId" : "444c4306a69cfe1e3733422b3e0f02eb602de3afecb956885bb4afb01f11d0a8", "spentTxOutRef" : "8399276806d7cd125339ce280ce03ba52457871211a2c46f931ceee821739b40#0"}, {"spentAtTxId" : "5fa6c737500a0473e8878b079378838b67f870c0e9a487270e0008f774badcd8", "spentTxOutRef" : "88600f287af5bb6d2c6b347713025b9829516d85272b36c38dc8594bcc19d898#0"}, {"spentAtTxId" : "5fa6c737500a0473e8878b079378838b67f870c0e9a487270e0008f774badcd8", "spentTxOutRef" : "88600f287af5bb6d2c6b347713025b9829516d85272b36c38dc8594bcc19d898#1"}, {"spentAtTxId" : "e2fb077b3beecdcd79ccbb5a1a9b82b52ff3090de237a908760406b0b4de2a6e", "spentTxOutRef" : "8c135a510f93e6e391735dd252b4943fc248473b0fd475d8c857d624a4e2c002#0"}, {"spentAtTxId" : "e2fb077b3beecdcd79ccbb5a1a9b82b52ff3090de237a908760406b0b4de2a6e", "spentTxOutRef" : "8c135a510f93e6e391735dd252b4943fc248473b0fd475d8c857d624a4e2c002#1"}, {"spentAtTxId" : "4fc1c90b8ee3e2e252d3fd74472f461809aa523e64502d08cd319709ad03826e", "spentTxOutRef" : "90d56028775796f3b8e6d24c11390505b1fe5289e7ae4c957d8d3c97a57a49e7#0"}, {"spentAtTxId" : "2c2ae600c43cff1ba3636aec737a9b3ce2673a77a0658951f44eafddbdb82b56", "spentTxOutRef" : "943d490694518bb3da7ad36d34f027bbc4a9071f0a5f3699c5026679bc59433c#0"}, {"spentAtTxId" : "65e84325f731569113ea3b1e85429bc472d5a81539084e4f552c59e3b58a7e09", "spentTxOutRef" : "962fc6b492f99491decc0634e4a5ed8117cb94f3e9268d32b9727d8566db671a#1"}, {"spentAtTxId" : "444c4306a69cfe1e3733422b3e0f02eb602de3afecb956885bb4afb01f11d0a8", "spentTxOutRef" : "97695c95122f8498dbb6fa89d0501a6a37278b755472fad18fa68313e04234a4#0"}, {"spentAtTxId" : "444c4306a69cfe1e3733422b3e0f02eb602de3afecb956885bb4afb01f11d0a8", "spentTxOutRef" : "97695c95122f8498dbb6fa89d0501a6a37278b755472fad18fa68313e04234a4#1"}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/alonzo1/blockinfo-slot39916975.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":6236060,"time":"2021-09-12T21:47:46","epoch_no":290}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/alonzo1/blockinfo-slot43372584.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":6405199,"time":"2021-10-22T21:41:15","epoch_no":297}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/alonzo1/blockinfo-slot43372792.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":6405205,"time":"2021-10-22T21:44:43","epoch_no":297}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/alonzo2/blockinfo-slot43372972.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":6405206,"time":"2021-10-22T21:47:43","epoch_no":298}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/alonzo2/blockinfo-slot45748924.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":6520890,"time":"2021-11-19T09:46:55","epoch_no":303}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/alonzo2/blockinfo-slot48124747.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":6636596,"time":"2021-12-16T21:43:58","epoch_no":308}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/babbage1/blockinfo-slot72316896.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":7791699,"time":"2022-09-22T21:46:27","epoch_no":365}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/babbage1/blockinfo-slot77064584.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":8022613,"time":"2022-11-16T20:34:35","epoch_no":375}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/babbage1/blockinfo-slot77068768.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":8022829,"time":"2022-11-16T21:44:19","epoch_no":375}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/babbage2/blockinfo-slot84844885.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":8400208,"time":"2023-02-14T21:46:16","epoch_no":394}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/babbage2/blockinfo-slot87198551.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":8514548,"time":"2023-03-14T03:34:02","epoch_no":399}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/babbage2/blockinfo-slot89596758.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":8630911,"time":"2023-04-10T21:44:09","epoch_no":404}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/byron1/blockinfo-slot0.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":1,"time":"2017-09-23T21:44:51","epoch_no":0}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/byron1/blockinfo-slot206938.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":206906,"time":"2017-11-10T19:24:11","epoch_no":9}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/byron1/blockinfo-slot6.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":7,"time":"2017-09-23T21:46:51","epoch_no":0}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/byron2/blockinfo-slot3801600.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":3799518,"time":"2020-02-20T21:44:51","epoch_no":176}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/byron2/blockinfo-slot3901600.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":3899515,"time":"2020-03-15T01:18:11","epoch_no":180}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/byron2/blockinfo-slot4039199.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":4037101,"time":"2020-04-15T21:44:31","epoch_no":186}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/byron2/spentinfo-slot3801600.out.golden: -------------------------------------------------------------------------------- 1 | [{"spentAtTxId" : "5214abb15941354cc48c0287c2a46da2561b113119a9f54a66e57c4fd50549b0", "spentTxOutRef" : "7efb3a280d86bdaf3e585c60cead92c022ef45c8e24520dbba46f2d0cbb39d54#0"}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/byron2/spentinfo-slot3901600.out.golden: -------------------------------------------------------------------------------- 1 | [{"spentAtTxId" : "1082db40f8e53ff0040d386b95df49f6a7eb5417d428bb4484c03858626653c2", "spentTxOutRef" : "47da15d4b907a9e217cd0ef310cc946a398fa4a7619f7a789bb70600bf688928#0"}, {"spentAtTxId" : "1082db40f8e53ff0040d386b95df49f6a7eb5417d428bb4484c03858626653c2", "spentTxOutRef" : "6d5327aa77d2a16705093d2a57d8b232eef1a15965efd1dd88199ca0d4f1cb5a#1"}, {"spentAtTxId" : "f573c486fea1a12d07da77d54d0b133ecd83e9ffc501703980266e113c71c639", "spentTxOutRef" : "876c255af512694dfb009f2beec80983193ccf8e318cbbe74960eb7ba8b68068#1"}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/mary/blockinfo-slot23068800.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":5406747,"time":"2021-03-01T21:44:51","epoch_no":251}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/mary/blockinfo-slot25168800.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":5509119,"time":"2021-03-26T05:04:51","epoch_no":255}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/mary/blockinfo-slot27820794.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":5637904,"time":"2021-04-25T21:44:45","epoch_no":261}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/mary/spentinfo-slot23068800.out.golden: -------------------------------------------------------------------------------- 1 | [{"spentAtTxId" : "9672463f469bf4b7893a15fb631da72b41e6f08d5370dfcdeafe52b5027fc934", "spentTxOutRef" : "6bc58161425c32397246610be5ebf5110deca132df8874d2d1d5bf45718e1e6f#1"}, {"spentAtTxId" : "ed54427209ac9e57938c933b734650693be686ed6583151e80910aa36c79b532", "spentTxOutRef" : "892ac658e19ca2ec0cb2546d48e3efff312ae49e8e9a9952e78664f211f30e1e#0"}, {"spentAtTxId" : "be4cf1ca8253b924dd0792efda0784a309c136432f54165871e00c404527e68f", "spentTxOutRef" : "c6febf8cabc7f873eb57a38100f8835e2c25bd6072602b2bada58a175a11ab89#0"}, {"spentAtTxId" : "ae6755fb45f015ca2cd2e3b174a03b8ab79621f470268f83b9f1b1cebc13c30d", "spentTxOutRef" : "d68efd5e99a42b86fc8145b3d31d295e1ff9d29379dc170dde46c4f3568a9d6d#0"}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/shelley/blockinfo-slot4492800.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":4490511,"time":"2020-07-29T21:44:51","epoch_no":208}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/shelley/blockinfo-slot4520900.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":4491915,"time":"2020-07-30T05:33:11","epoch_no":208}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/shelley/blockinfo-slot9244771.out.golden: -------------------------------------------------------------------------------- 1 | [{"block_no":4724700,"time":"2020-09-22T21:44:22","epoch_no":218}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/shelley/spentinfo-slot4492800.out.golden: -------------------------------------------------------------------------------- 1 | \N 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/mainnet/shelley/spentinfo-slot4520900.out.golden: -------------------------------------------------------------------------------- 1 | [{"spentAtTxId" : "31cfda93c09ff988c7196daee2a984ef416e8427c48352b37f835858e292aa7a", "spentTxOutRef" : "23eec20c369c0c85d0072a985ec9bc837ca0ffad8a62cf070dfb8aa0a5c81d34#1"}, {"spentAtTxId" : "31cfda93c09ff988c7196daee2a984ef416e8427c48352b37f835858e292aa7a", "spentTxOutRef" : "4261daf598f7f07a9d55f80a3a88f99cbcbadd001cb8ad37958dc2a0fe3ca852#1"}, {"spentAtTxId" : "31cfda93c09ff988c7196daee2a984ef416e8427c48352b37f835858e292aa7a", "spentTxOutRef" : "43b5dcf63508423c17d8b4438aefa8e69efcdfe319cbabee54caa5edadb77fbb#1"}, {"spentAtTxId" : "06a9dd04705f118ec1d3d00714a6ff605064035d7644e0d67ab8f5bb8ea1333c", "spentTxOutRef" : "45a3fb65afb7ad1442446c53775828fbb979a050b778308acf9d869c71c879c5#2"}, {"spentAtTxId" : "d1f0c42ef964b1b214e3fca806c73f5e1840a77f8ab2bee291466c5190cedd4c", "spentTxOutRef" : "7f7b77cdd121d455a3fd880b16234396caeec11be4eadb0681602d42134771bf#4"}, {"spentAtTxId" : "31cfda93c09ff988c7196daee2a984ef416e8427c48352b37f835858e292aa7a", "spentTxOutRef" : "fd18764f5c773d711b5c860c9ce7c7adb649084d0ea970a3538431d6443e7623#0"}] 2 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_10_19445_5c9999c1e6ba0f2ad1b8c85cf7c0a6663ddd81e4584a93041daf87deb8325d3a.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/marconi/d39923eb7b81156f5783e6252469b321ad89a253/marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_10_19445_5c9999c1e6ba0f2ad1b8c85cf7c0a6663ddd81e4584a93041daf87deb8325d3a.cbor -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_5_8641_f5441700216e5516c6dc19e7eb616f0bf1d04dd1368add35e3a7fd114e30b880.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/marconi/d39923eb7b81156f5783e6252469b321ad89a253/marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_5_8641_f5441700216e5516c6dc19e7eb616f0bf1d04dd1368add35e3a7fd114e30b880.cbor -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_6_10802_8e2e24608ab3a9ec0cbf6b2e2b88574aae84955fcd5f89c604e52e33cbee9883.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/marconi/d39923eb7b81156f5783e6252469b321ad89a253/marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_6_10802_8e2e24608ab3a9ec0cbf6b2e2b88574aae84955fcd5f89c604e52e33cbee9883.cbor -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_7_12961_4d4114f03ab1bccd22182bb1106336fcab3b38f884306e1b6fda83df384a2161.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/marconi/d39923eb7b81156f5783e6252469b321ad89a253/marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_7_12961_4d4114f03ab1bccd22182bb1106336fcab3b38f884306e1b6fda83df384a2161.cbor -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_8_15122_018a398c415639148ecf39c3a5af68ef954e7fa5acc166253975019e9b022c83.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/marconi/d39923eb7b81156f5783e6252469b321ad89a253/marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_8_15122_018a398c415639148ecf39c3a5af68ef954e7fa5acc166253975019e9b022c83.cbor -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_9_17283_a28094a6147cffac3187bb28828e0834cb355cc31beb00f0fb3546769f54e98b.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/marconi/d39923eb7b81156f5783e6252469b321ad89a253/marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/block_just_9_17283_a28094a6147cffac3187bb28828e0834cb355cc31beb00f0fb3546769f54e98b.cbor -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/epochState_just_4_6480_ab0a64eaf8bc9e96e4df7161d3ace4d32e88cab2315f952665807509e49892eb.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/marconi/d39923eb7b81156f5783e6252469b321ad89a253/marconi-cardano-indexers/test/Spec/Golden/Snapshot/preprod-5-10/epochState_just_4_6480_ab0a64eaf8bc9e96e4df7161d3ace4d32e88cab2315f952665807509e49892eb.cbor -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Marconi/Cardano/Indexers.hs: -------------------------------------------------------------------------------- 1 | module Spec.Marconi.Cardano.Indexers (tests) where 2 | 3 | import Test.Tasty (TestTree, localOption, testGroup) 4 | 5 | import Spec.Marconi.Cardano.Indexers.BlockInfo qualified as BlockInfo 6 | import Spec.Marconi.Cardano.Indexers.ChainTip qualified as ChainTip 7 | import Spec.Marconi.Cardano.Indexers.Datum qualified as Datum 8 | import Spec.Marconi.Cardano.Indexers.MintTokenEvent qualified as MintTokenEvent 9 | import Spec.Marconi.Cardano.Indexers.Spent qualified as Spent 10 | import Spec.Marconi.Cardano.Indexers.Utxo qualified as Utxo 11 | import Spec.Marconi.Cardano.Indexers.UtxoQuery qualified as UtxoQuery 12 | import Test.Tasty.Hedgehog (HedgehogTestLimit (HedgehogTestLimit)) 13 | 14 | tests :: TestTree 15 | tests = 16 | testGroup 17 | "Spec.Marconi.Cardano.Indexers" 18 | [ unitTests 19 | , propTests 20 | ] 21 | 22 | -- | Genuine property tests, with an arbitrary number of test runs. 23 | propTests :: TestTree 24 | propTests = 25 | localOption (HedgehogTestLimit $ Just 200) $ 26 | testGroup 27 | "Spec.Marconi.Cardano.Indexers.propTests" 28 | [ Utxo.tests 29 | , Spent.tests 30 | , Datum.tests 31 | , BlockInfo.propTests 32 | , ChainTip.tests 33 | , UtxoQuery.tests 34 | , MintTokenEvent.propTests 35 | ] 36 | 37 | -- | Tests whose number of runs is set in the test definition, e.g. to 1. 38 | unitTests :: TestTree 39 | unitTests = 40 | testGroup 41 | "Spec.Marconi.Cardano.Indexers.unitTests" 42 | [ BlockInfo.unitTests 43 | , MintTokenEvent.unitTests 44 | ] 45 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/Marconi/Cardano/Indexers/Datum.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE AllowAmbiguousTypes #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | {-# OPTIONS_GHC -Wno-orphans #-} 5 | 6 | module Spec.Marconi.Cardano.Indexers.Datum ( 7 | tests, 8 | ) where 9 | 10 | import Control.Lens ((^.), (^..)) 11 | import Data.Maybe (mapMaybe) 12 | 13 | import Control.Lens qualified as Lens 14 | import Data.Aeson qualified as Aeson 15 | import Hedgehog ((===)) 16 | import Hedgehog qualified 17 | import Hedgehog.Gen qualified 18 | import Marconi.Cardano.Indexers.Datum qualified as Datum 19 | import Marconi.Core qualified as Core 20 | import Test.Gen.Marconi.Cardano.Indexers.Datum qualified as Test.Datum 21 | import Test.Tasty (TestTree, testGroup) 22 | import Test.Tasty.Hedgehog (testPropertyNamed) 23 | 24 | tests :: TestTree 25 | tests = 26 | testGroup 27 | "Spec.Marconi.Cardano.Indexers.Datum" 28 | [ testGroup 29 | "Indexer" 30 | [ testPropertyNamed 31 | "DatumIndexer can retrieve all the data it stores" 32 | "propResolveDatum" 33 | propResolveDatum 34 | ] 35 | , testPropertyNamed 36 | "JSON event tripping test" 37 | "propTrippingDatumJSON" 38 | propTrippingDatumJSON 39 | ] 40 | 41 | -- | We can retrieve datum by hash if we query at its insertion slot 42 | propResolveDatum :: Hedgehog.Property 43 | propResolveDatum = Hedgehog.property $ do 44 | events <- Hedgehog.forAll Test.Datum.genDatumInfoEvents 45 | event <- 46 | Hedgehog.forAll $ 47 | Hedgehog.Gen.element 48 | =<< Hedgehog.Gen.filter (not . null) (pure $ mapMaybe sequenceA events) 49 | datumInfo <- Hedgehog.forAll $ Hedgehog.Gen.element $ event ^.. Core.event . Lens.folded 50 | emptyIndexer <- Hedgehog.evalExceptT $ Datum.mkDatumIndexer Core.inMemoryDB 51 | indexer <- Hedgehog.evalExceptT $ Core.indexAll events emptyIndexer 52 | retrievedEvents <- 53 | Hedgehog.evalExceptT $ 54 | Core.query 55 | (event ^. Core.point) 56 | (Datum.ResolveDatumQuery $ datumInfo ^. Datum.datumHash) 57 | indexer 58 | Just (datumInfo ^. Datum.datum) === retrievedEvents 59 | 60 | -- | Standard tripping property for JSON 61 | propTrippingDatumJSON :: Hedgehog.Property 62 | propTrippingDatumJSON = Hedgehog.property $ do 63 | event <- Hedgehog.forAll Test.Datum.genDatumInfo 64 | Hedgehog.tripping event Aeson.encode Aeson.eitherDecode 65 | -------------------------------------------------------------------------------- /marconi-cardano-indexers/test/Spec/README.md: -------------------------------------------------------------------------------- 1 | # Writing Db-Sync Comparison Tests 2 | 3 | These are instructions on how to write tests to the `Spec.Marconi.Cardano.DbSyncComparison.hs` test suite. 4 | 5 | Db-Sync Comparison tests are tests which compare the behavior of `marconi` to the behavior of `cardano-db-sync`, 6 | where `cardano-db-sync` is the one providing the standard expected behavior. 7 | 8 | We have provided a framework which should make these kinds of tests easier to write and as lightweight as possible for CI. 9 | 10 | ## Sub-Chain Snapshots 11 | 12 | We decided to avoid running `cardano-node` directly in CI in order to avoid the large resource overhead and complications which would arise from such a solution. 13 | Therefore, our solution is to serialise parts of the block-chain (sub-chains) to disk. These are what we call _snapshots of the chain_, or just _snapshots_. The tests 14 | run on these snapshots. 15 | 16 | ### Remark: snapshots are generated before-hand, and added to the environment by TODO. 17 | 18 | For the purpose of the Db-Sync Comparison tests we have generated 9 snapshots, each containing the blocks from the first 10 epochs from each Cardano era. [This table](https://github.com/cardano-foundation/CIPs/blob/master/CIP-0059/feature-table.md) was used as reference. 19 | 20 | ## How to add a Db-Sync Comparison test 21 | 22 | For the same reason as above, we cannot run `cardano-db-sync` in CI, but we can store its outputs to specific queries in our repository as golden files. 23 | 24 | This section will explain the steps required to add a new comparison test, for any new indexer `I` with an associated query `Q`. 25 | 26 | 1. Run `cardano-db-sync` locally, and research how queries similar to `Q` can be created for `cardano-db-sync`. You can find the instructions for doing this [here](https://input-output-rnd.slack.com/docs/T0N639Z4N/F06AXV0BUSZ). 27 | 2. You will have to write a module similar to `Test.Marconi.Cardano.DbSyncComparison.BlockInfoResult.hs`. Note that the expected result from Db-Sync should be serialised to JSON. 28 | 3. Add your tests to `Spec.Marconi.Cardano.DbSyncComparison.hs`. 29 | -------------------------------------------------------------------------------- /marconi-core-json-rpc/CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/input-output-hk/marconi/d39923eb7b81156f5783e6252469b321ad89a253/marconi-core-json-rpc/CHANGELOG.md -------------------------------------------------------------------------------- /marconi-core-json-rpc/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Input Output Global, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /marconi-core-json-rpc/README.md: -------------------------------------------------------------------------------- 1 | # marconi-core-json-rpc 2 | -------------------------------------------------------------------------------- /marconi-core-json-rpc/changelog.d/scriv.ini: -------------------------------------------------------------------------------- 1 | ../../scriv.ini -------------------------------------------------------------------------------- /marconi-core-json-rpc/marconi-core-json-rpc.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 3.8 2 | name: marconi-core-json-rpc 3 | version: 1.2.0.0 4 | synopsis: 5 | Utilities for wrapping the query interface of Marconi indexers in a JSON-RPC HTTP server. 6 | 7 | description: 8 | Please see the README on GitHub at <https://github.com/input-output-hk/marconi-core-json-rpc#README.md> 9 | 10 | category: Testing 11 | homepage: 12 | https://github.com/input-output-hk/marconi-core-json-rpc/README.md 13 | 14 | bug-reports: https://github.com/input-output-hk/marconi/issues 15 | author: IOHK 16 | maintainer: operations@iohk.io 17 | license: Apache-2.0 18 | license-files: 19 | LICENSE 20 | NOTICE 21 | 22 | build-type: Simple 23 | 24 | source-repository head 25 | type: git 26 | location: https://github.com/input-output-hk/marconi 27 | 28 | common lang 29 | default-language: Haskell2010 30 | default-extensions: 31 | DeriveAnyClass 32 | DeriveGeneric 33 | DerivingStrategies 34 | ExplicitForAll 35 | FlexibleContexts 36 | FlexibleInstances 37 | GADTs 38 | GeneralizedNewtypeDeriving 39 | ImportQualifiedPost 40 | InstanceSigs 41 | MultiParamTypeClasses 42 | NamedFieldPuns 43 | OverloadedStrings 44 | PatternSynonyms 45 | ScopedTypeVariables 46 | StandaloneDeriving 47 | Strict 48 | TemplateHaskell 49 | TupleSections 50 | TypeApplications 51 | TypeFamilies 52 | 53 | ghc-options: 54 | -Wall -Wnoncanonical-monad-instances -Wunused-packages 55 | -Wincomplete-uni-patterns -Wincomplete-record-updates 56 | -Wredundant-constraints -Widentities -Wmissing-import-lists 57 | 58 | library 59 | import: lang 60 | exposed-modules: 61 | Marconi.Core.JsonRpc 62 | Network.JsonRpc.Client.Types 63 | Network.JsonRpc.Server.Types 64 | Network.JsonRpc.Types 65 | 66 | hs-source-dirs: src 67 | 68 | ------------------------ 69 | -- Local dependencies 70 | ------------------------ 71 | build-depends: marconi-core 72 | 73 | ------------------------ 74 | -- Other dependencies 75 | ------------------------ 76 | build-depends: 77 | , aeson 78 | , base >=4.7 && <5 79 | , containers 80 | , data-default 81 | , fast-logger 82 | , http-media 83 | , http-types 84 | , iohk-monitoring 85 | , lens 86 | , mtl 87 | , servant 88 | , servant-client-core 89 | , servant-server 90 | , text 91 | , wai 92 | , wai-extra 93 | -------------------------------------------------------------------------------- /marconi-core-json-rpc/src/Network/JsonRpc/Client/Types.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleInstances #-} 2 | {-# LANGUAGE MultiParamTypeClasses #-} 3 | {-# LANGUAGE ScopedTypeVariables #-} 4 | {-# LANGUAGE TypeApplications #-} 5 | {-# LANGUAGE TypeFamilies #-} 6 | {-# OPTIONS_GHC -fno-warn-orphans #-} 7 | 8 | {- | 9 | This module provides support for generating JSON-RPC clients in the Servant framework. 10 | 11 | Note: This client implementation runs over HTTP and the semantics of HTTP 12 | remove the need for the message id. 13 | -} 14 | module Network.JsonRpc.Client.Types where 15 | 16 | import Data.Aeson (FromJSON, ToJSON) 17 | import Data.Proxy (Proxy (Proxy)) 18 | import GHC.TypeLits (KnownSymbol, symbolVal) 19 | import Servant.API (NoContent) 20 | import Servant.Client.Core (Client, HasClient (clientWithRoute, hoistClientMonad), RunClient) 21 | 22 | import Network.JsonRpc.Types ( 23 | JsonRpc, 24 | JsonRpcEndpoint, 25 | JsonRpcNotification, 26 | JsonRpcResponse, 27 | RawJsonRpc, 28 | Request (Request), 29 | ) 30 | 31 | -- | The 'RawJsonRpc' is transparent to clients 32 | instance (HasClient m api) => HasClient m (RawJsonRpc api) where 33 | type Client m (RawJsonRpc api) = Client m api 34 | clientWithRoute pxm _ = clientWithRoute pxm (Proxy @api) 35 | hoistClientMonad pxm _ = hoistClientMonad pxm (Proxy @api) 36 | 37 | instance 38 | (RunClient m, KnownSymbol method, ToJSON p, FromJSON e, FromJSON r) 39 | => HasClient m (JsonRpc method p e r) 40 | where 41 | type 42 | Client m (JsonRpc method p e r) = 43 | p -> m (JsonRpcResponse e r) 44 | 45 | clientWithRoute _ _ req p = 46 | client req jsonRpcRequest 47 | where 48 | client = clientWithRoute (Proxy @m) endpoint 49 | jsonRpcRequest = Request (symbolVal $ Proxy @method) p (Just 0) 50 | 51 | endpoint = Proxy @(JsonRpcEndpoint (JsonRpc method p e r)) 52 | 53 | hoistClientMonad _ _ f x p = f $ x p 54 | 55 | instance 56 | (RunClient m, KnownSymbol method, ToJSON p) 57 | => HasClient m (JsonRpcNotification method p) 58 | where 59 | type 60 | Client m (JsonRpcNotification method p) = 61 | p -> m NoContent 62 | 63 | clientWithRoute _ _ req p = 64 | client req jsonRpcRequest 65 | where 66 | client = clientWithRoute (Proxy @m) endpoint 67 | jsonRpcRequest = Request (symbolVal $ Proxy @method) p Nothing 68 | 69 | endpoint = Proxy @(JsonRpcEndpoint (JsonRpcNotification method p)) 70 | 71 | hoistClientMonad _ _ f x p = f $ x p 72 | -------------------------------------------------------------------------------- /marconi-core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | <a id='changelog-1.2.0'></a> 3 | # 1.2.0 — 2023-03-03 4 | 5 | ## Changed 6 | 7 | - Renamed the rewindable-index package name to marconi-core 8 | - Modified the module names to reflect the new package name 9 | -------------------------------------------------------------------------------- /marconi-core/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Input Output Global, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /marconi-core/README.md: -------------------------------------------------------------------------------- 1 | # marconi-core 2 | 3 | `marconi-core` is a Haskell library to ease indexing of distributed ledger 4 | that has a settlement time 5 | (ie. rollback can happen and invalidates the last indexed blocks). 6 | 7 | While `marconi` was designed to target the Cardano blockchain, 8 | the content of `marconi-core` is totally chain agnostic and does not rely on 9 | anything Cardano specific type. 10 | 11 | If you want to develop your own indexing solution for Cardano using `marconi`, 12 | you'll probably want to use other specific libraries aside `marconi-core` that 13 | will ease the integration on Cardano 14 | (like [marconi-caradano-core](../marconi-cardano-core) that provides content to 15 | ease events extraction and connection to a node or 16 | [marconi-cardano-indexers](../marconi-cardano-indexers)). 17 | 18 | ## Philosophy 19 | 20 | `Marconi` eased the definition and composition of atomic indexers, 21 | each of them tailored to answer efficiently specific questions. 22 | The underlying idea is that a dApp usually requires specific information 23 | and in the fastest possible way. 24 | To address this, we need tailored indexers, extracting exactly what is needed to 25 | provide this information and store most efficiently. 26 | 27 | ## What's in there? 28 | 29 | A description of `marconi-core` key concepts is available in 30 | [the documentation](../doc/read-the-docs-site/architecture/marconi-core-building-blocks.rst). 31 | -------------------------------------------------------------------------------- /marconi-core/changelog.d/20230417_103947_nicolas.biri_error_handling.md: -------------------------------------------------------------------------------- 1 | ### Changed 2 | 3 | - `rewind` now always returns an indexer instead of a `Maybe`, invalid cases, 4 | if any, must be captured in the monad stack. 5 | 6 | --> 7 | <!-- 8 | ### Deprecated 9 | 10 | - A bullet item for the Deprecated category. 11 | 12 | --> 13 | <!-- 14 | ### Fixed 15 | 16 | - A bullet item for the Fixed category. 17 | 18 | --> 19 | <!-- 20 | ### Security 21 | 22 | - A bullet item for the Security category. 23 | 24 | --> 25 | -------------------------------------------------------------------------------- /marconi-core/changelog.d/20230519_180000_kayvank_deprecated_indexers.md: -------------------------------------------------------------------------------- 1 | ### Changed 2 | 3 | - `deprecated-indexers` removed VSplit and VSqlite modules that were supporting legacy, deprecated indexers 4 | -------------------------------------------------------------------------------- /marconi-core/changelog.d/scriv.ini: -------------------------------------------------------------------------------- 1 | ../../scriv.ini -------------------------------------------------------------------------------- /marconi-core/src/Marconi/Core/Indexer/LastPointIndexer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE UndecidableInstances #-} 2 | 3 | {- | 4 | On-disk indexer backed by a sqlite database. 5 | 6 | See "Marconi.Core" for documentation. 7 | -} 8 | module Marconi.Core.Indexer.LastPointIndexer ( 9 | LastPointIndexer, 10 | lastPointIndexer, 11 | ) where 12 | 13 | import Control.Lens (makeLenses, view) 14 | import Control.Lens.Operators ((.~), (^.)) 15 | import Data.Foldable (Foldable (toList)) 16 | import Marconi.Core.Class ( 17 | HasGenesis (genesis), 18 | IsIndex (index, indexAllDescending, rollback, setLastStablePoint), 19 | IsSync (lastStablePoint, lastSyncPoint), 20 | ) 21 | import Marconi.Core.Type (Point, point) 22 | 23 | {- | LastPointIndexer. 24 | An indexer that does nothing except keeping track of the last point. 25 | While it may sound useless, 26 | it can be usefull when you want to benefit of the capabilities of a transformer. 27 | -} 28 | data LastPointIndexer event = LastPointIndexer 29 | { _latestSyncPoint :: Point event 30 | , _latestStablePoint :: Point event 31 | } 32 | 33 | deriving stock instance (Show event, Show (Point event)) => Show (LastPointIndexer event) 34 | 35 | makeLenses 'LastPointIndexer 36 | 37 | -- | A smart constructor for 'LastPointIndexer' 38 | lastPointIndexer :: (HasGenesis (Point event)) => LastPointIndexer event 39 | lastPointIndexer = LastPointIndexer genesis genesis 40 | 41 | instance 42 | (HasGenesis (Point event), Monad m) 43 | => IsIndex m event LastPointIndexer 44 | where 45 | index timedEvent = pure . (latestSyncPoint .~ timedEvent ^. point) 46 | 47 | indexAllDescending evts = case toList evts of 48 | [] -> pure 49 | (evt : _) -> pure . (latestSyncPoint .~ evt ^. point) 50 | 51 | rollback p = pure . (latestSyncPoint .~ p) 52 | setLastStablePoint p = pure . (latestStablePoint .~ p) 53 | 54 | instance (Applicative m) => IsSync m event LastPointIndexer where 55 | lastSyncPoint = pure . view latestSyncPoint 56 | lastStablePoint = pure . view latestStablePoint 57 | -------------------------------------------------------------------------------- /marconi-core/src/Marconi/Core/Transformer/Class.hs: -------------------------------------------------------------------------------- 1 | {- | Typeclass for indexer transformers, 2 | 3 | It allows the generalisation of properties amongst all indexer transformers. 4 | -} 5 | module Marconi.Core.Transformer.Class ( 6 | IndexerTrans (unwrap), 7 | IndexerMapTrans (unwrapMap), 8 | ) where 9 | 10 | import Control.Lens (Lens') 11 | 12 | -- | An indexer transformer: it adds a configurable capability to a tranformer 13 | class IndexerTrans t where 14 | -- | Unwrap the underlying indexer 15 | unwrap :: Lens' (t indexer event) (indexer event) 16 | 17 | {- | An indexer transformer: it adds a configurable capability to a tranformer 18 | 19 | This one allow also the transformation of the event, contrary to 'IndexerTrans'. 20 | -} 21 | class IndexerMapTrans t where 22 | -- | Unwrap the underlying indexer 23 | unwrapMap :: Lens' (t indexer output event) (indexer output) 24 | -------------------------------------------------------------------------------- /marconi-core/src/Marconi/Core/Transformer/WithCatchup/SQLite.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | 3 | module Marconi.Core.Transformer.WithCatchup.SQLite ( 4 | createIndexTable, 5 | ) where 6 | 7 | import Cardano.BM.Data.Trace (Trace) 8 | import Cardano.BM.Trace (logInfo) 9 | import Control.Monad (unless) 10 | import Control.Monad.IO.Class (liftIO) 11 | import Data.String (fromString) 12 | import Data.Text (Text) 13 | import Data.Text qualified as Text 14 | import Database.SQLite.Simple qualified as SQL 15 | import Database.SQLite.Simple.QQ (sql) 16 | 17 | {- | Utility function for creating an SQLite database index. 18 | 19 | Typically used with 'Marconi.Core.Tranformer.WithCatchup' as a way to create SQLite indexes when 20 | fully synchronized. This function includes some logging so that the user is informed when the 21 | creation of the SQLite indexes is done. 22 | 23 | An example would look like: 24 | 25 | @ 26 | let stdoutTrace = ... 27 | c = ... 28 | slotNoIndexName = "utxo_slotNo" 29 | createSlotNoIndexStatement = 30 | "CREATE INDEX IF NOT EXISTS " 31 | <> fromString slotNoIndexName 32 | <> " ON utxo (slotNo)" 33 | createIndexTable "Utxo" stdoutTrace c slotNoIndexName createSlotNoIndexStatement 34 | @ 35 | -} 36 | createIndexTable 37 | :: Text 38 | -- ^ Pretty name of the indexer 39 | -> Trace IO Text 40 | -- ^ Trace for logging. 41 | -> SQL.Connection 42 | -- ^ SQLite database connection 43 | -> String 44 | -- ^ SQLite index name 45 | -> SQL.Query 46 | -- ^ SQL statement for creating the index table 47 | -> IO () 48 | createIndexTable indexerName stdoutTrace c indexName createIndexStatement = do 49 | (doesIndexExist :: Bool) <- 50 | liftIO $ 51 | fmap ((> (0 :: Int)) . sum . concat) $ 52 | SQL.query_ 53 | c 54 | ( [sql|SELECT COUNT(*) 55 | FROM sqlite_master 56 | WHERE type='index' AND name='|] 57 | <> fromString indexName 58 | <> "'" 59 | ) 60 | unless doesIndexExist $ do 61 | logInfo stdoutTrace $ 62 | "Creating SQL index table '" 63 | <> Text.pack indexName 64 | <> "' for " 65 | <> indexerName 66 | <> " indexer..." 67 | liftIO $ SQL.execute_ c createIndexStatement 68 | logInfo stdoutTrace $ 69 | "Finished creating SQL index table '" 70 | <> Text.pack indexName 71 | <> "' for " 72 | <> indexerName 73 | <> " indexer..." 74 | -------------------------------------------------------------------------------- /marconi-core/src/Marconi/Core/Transformer/WithStream.hs: -------------------------------------------------------------------------------- 1 | module Marconi.Core.Transformer.WithStream where 2 | 3 | import Marconi.Core.Transformer.IndexTransformer (IndexTransformer (IndexTransformer)) 4 | import Marconi.Core.Transformer.WithAction ( 5 | WithAction (WithAction), 6 | WithActionConfig (WithActionConfig), 7 | ) 8 | import Marconi.Core.Transformer.WithStream.Streamable ( 9 | Streamable (streamTo), 10 | ) 11 | import Marconi.Core.Type (Point, Timed) 12 | 13 | {- | A smart constructor for @WithAction@, using any streamable. 14 | 15 | Be aware: depending on the implementation you choose, this can lead to the indexer blocking, given 16 | a slow (or non-existent) consumer! 17 | -} 18 | withStream 19 | :: (Streamable a r) 20 | => (Timed (Point event) event -> r) 21 | -> a 22 | -> indexer event 23 | -> WithAction indexer event 24 | withStream mapping container = 25 | WithAction . IndexTransformer (WithActionConfig $ streamTo mapping container) 26 | -------------------------------------------------------------------------------- /marconi-core/src/Marconi/Core/Transformer/WithStream/Streamable.hs: -------------------------------------------------------------------------------- 1 | module Marconi.Core.Transformer.WithStream.Streamable where 2 | 3 | import Control.Concurrent.STM (TBQueue, atomically, readTBQueue, writeTBQueue) 4 | import Control.Monad (unless) 5 | import Control.Monad.IO.Class (liftIO) 6 | import Data.Binary (Binary, decodeOrFail, encode) 7 | import Data.ByteString qualified as BS 8 | import Data.ByteString.Lazy qualified as BL 9 | import Marconi.Core.Type (Point, Timed) 10 | import Network.Socket (Socket) 11 | import Network.Socket.ByteString qualified as SBS 12 | import Network.Socket.ByteString.Lazy (sendAll) 13 | import Streaming (Of, Stream) 14 | import Streaming.Prelude (repeatM, yield) 15 | 16 | -- | Something that can have mapped events streamed to or from it 17 | class Streamable a r where 18 | streamFrom :: a -> Stream (Of r) IO () 19 | streamTo :: (Timed (Point event) event -> r) -> a -> Timed (Point event) event -> IO () 20 | 21 | {- | A streaming implementation using @Socket@. For an example of how 22 | to use it, please see: test/Marconi.CoreSpec.hs/propWithStreamSocket 23 | -} 24 | instance (Binary r) => Streamable Socket r where 25 | streamFrom sock = loop BL.empty 26 | where 27 | loop buffer = do 28 | chunk <- liftIO $ SBS.recv sock 4096 29 | unless (BS.null chunk) $ do 30 | let newBuffer = buffer <> BS.fromStrict chunk 31 | processBuffer newBuffer 32 | 33 | processBuffer buffer = 34 | case decodeOrFail buffer of 35 | Left _ -> loop buffer 36 | Right (rest, _, res) -> do 37 | yield res 38 | if BL.null rest 39 | then loop BL.empty 40 | else processBuffer rest 41 | streamTo mapping sock = sendAll sock . encode . mapping 42 | 43 | {- | An experimental streaming implementation using @TBQueue@. 44 | 45 | IMPORTANT: Note that this will block when used with @withStream@ if you have slow or 46 | non-existent consumers! 47 | 48 | For an example of how to use it, please see: test/Marconi.CoreSpec.hs/propWithStreamTBQueue 49 | -} 50 | instance Streamable (TBQueue r) r where 51 | streamFrom = repeatM . liftIO . atomically . readTBQueue 52 | streamTo mapping q = atomically . writeTBQueue q . mapping 53 | -------------------------------------------------------------------------------- /marconi-core/test/Spec.hs: -------------------------------------------------------------------------------- 1 | import Test.Tasty (TestTree, defaultMain, testGroup) 2 | 3 | import Marconi.CoreSpec qualified as Core 4 | 5 | main :: IO () 6 | main = defaultMain tests 7 | 8 | tests :: TestTree 9 | tests = 10 | testGroup 11 | "Marconi.Core" 12 | [ testGroup 13 | "Indexing" 14 | [ Core.indexingTestGroup "ListIndexer" Core.listIndexerRunner 15 | , Core.indexingTestGroup "SQLiteIndexer" Core.mkSqliteIndexerRunner 16 | , Core.indexingTestGroup "MixedIndexer - low memory" Core.mixedLowMemoryIndexerRunner 17 | , Core.indexingTestGroup "MixedIndexer - high memory" Core.mixedHighMemoryIndexerRunner 18 | , Core.indexingTestGroup "WithTracer" $ Core.withTracerRunner Core.listIndexerRunner 19 | , Core.indexingTestGroup "Coordinator" $ Core.coordinatorIndexerRunner Core.listIndexerRunner 20 | ] 21 | , Core.cacheTestGroup 22 | , testGroup "Query modification" [Core.withStabilityTestGroup] 23 | , testGroup "Action hijacking" [Core.withStreamTestGroup] 24 | , testGroup 25 | "WithDelay" 26 | [ Core.delayTestGroup "ListIndexer" Core.listIndexerRunner 27 | , Core.delayTestGroup "SQLiteIndexer" Core.mkSqliteIndexerRunner 28 | ] 29 | , testGroup 30 | "WithCatchup" 31 | [ Core.catchupTestGroup "ListIndexer" Core.listIndexerRunner 32 | , Core.catchupTestGroup "SQLiteIndexer" Core.mkSqliteIndexerRunner 33 | ] 34 | , testGroup 35 | "WithTransform" 36 | [ Core.withTransformTest 37 | ] 38 | , testGroup 39 | "WithAggregate" 40 | [ Core.withAggregateTest 41 | ] 42 | , testGroup 43 | "WithResume" 44 | [ Core.withResumeTest 45 | ] 46 | , testGroup 47 | "Performance" 48 | [ Core.indexingPerformanceTest "ListIndexer" Core.listIndexerRunner 49 | , Core.indexingPerformanceTest "MixedIndexer" Core.mixedHighMemoryIndexerRunner 50 | ] 51 | , testGroup 52 | "Error handling" 53 | [ Core.stopCoordinatorTest Core.listIndexerRunner 54 | , Core.withRollbackFailureTest 55 | ] 56 | , testGroup 57 | "Resuming last synced points" 58 | [ Core.resumeSQLiteLastSyncTest 59 | , Core.resumeMixedLastSyncTest 60 | ] 61 | , testGroup 62 | "Configuration" 63 | [ Core.memorySizeUpdateTest 64 | ] 65 | ] 66 | -------------------------------------------------------------------------------- /marconi-sidechain/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | <a id='changelog-0.0.1'></a> 3 | # 0.0.1 — 2023-11-10 4 | 5 | ## Added 6 | 7 | - Initialized the package 8 | - Added JSON-RPC endpoints for echo, epoch nonce, and target address queries. 9 | 10 | ## Changed 11 | 12 | ## Fixed 13 | -------------------------------------------------------------------------------- /marconi-sidechain/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Input Output Global, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /marconi-sidechain/app/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Marconi.Sidechain.Run (run) 4 | 5 | main :: IO () 6 | main = run 7 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/HttpServer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeApplications #-} 2 | {-# LANGUAGE TypeOperators #-} 3 | 4 | -- | Run the HTTP server for the JSON-RPC API. 5 | module Marconi.Sidechain.Api.HttpServer where 6 | 7 | import Control.Lens ((^.)) 8 | import Control.Monad.Reader (ReaderT, ask, lift) 9 | import Data.Proxy (Proxy (Proxy)) 10 | import Marconi.Core.JsonRpc ( 11 | ReaderHandler, 12 | ReaderServer, 13 | catchHttpHandlerExceptions, 14 | hoistReaderHandler, 15 | mkHttpRequestTracer, 16 | ) 17 | import Marconi.Sidechain.Api.JsonRpc.Routes (JsonRpcAPI) 18 | import Marconi.Sidechain.Api.JsonRpc.Server (jsonRpcServer) 19 | import Marconi.Sidechain.Api.Rest.Routes (RestAPI) 20 | import Marconi.Sidechain.Api.Rest.Server (restApiServer) 21 | import Marconi.Sidechain.Api.Types ( 22 | SidechainHttpServerConfig, 23 | chainIndexHttpServerConfig, 24 | configPort, 25 | configTrace, 26 | ) 27 | import Network.Wai.Handler.Warp (defaultSettings, runSettings, setPort) 28 | import Servant.API ((:<|>) ((:<|>))) 29 | import Servant.Server ( 30 | Application, 31 | Handler, 32 | hoistServer, 33 | serve, 34 | ) 35 | 36 | -- | Bootstraps the HTTP server. 37 | runHttpServer :: ReaderT SidechainHttpServerConfig IO () 38 | runHttpServer = do 39 | config <- ask 40 | let settings = setPort (config ^. chainIndexHttpServerConfig . configPort) defaultSettings 41 | requestTracer <- mkHttpRequestTracer (config ^. chainIndexHttpServerConfig . configTrace) 42 | lift $ runSettings settings $ requestTracer $ sidechainApp config 43 | 44 | sidechainApp :: SidechainHttpServerConfig -> Application 45 | sidechainApp config = do 46 | let trace = config ^. chainIndexHttpServerConfig . configTrace 47 | wrapHandler :: ReaderHandler SidechainHttpServerConfig a -> Handler a 48 | wrapHandler = catchHttpHandlerExceptions trace . hoistReaderHandler config 49 | serve (Proxy @API) $ 50 | hoistServer (Proxy @API) wrapHandler httpServer 51 | 52 | httpServer :: ReaderServer SidechainHttpServerConfig API 53 | httpServer = jsonRpcServer :<|> restApiServer 54 | 55 | type API = JsonRpcAPI :<|> RestAPI 56 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/JsonRpc/Endpoint/BurnTokenEvent.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE DeriveAnyClass #-} 3 | {-# LANGUAGE DeriveGeneric #-} 4 | {-# LANGUAGE DerivingStrategies #-} 5 | {-# LANGUAGE DuplicateRecordFields #-} 6 | {-# LANGUAGE RecordWildCards #-} 7 | {-# LANGUAGE StrictData #-} 8 | 9 | module Marconi.Sidechain.Api.JsonRpc.Endpoint.BurnTokenEvent ( 10 | RpcGetBurnTokenEventsMethod, 11 | GetBurnTokenEventsParams (..), 12 | ChainIndex.GetBurnTokenEventsResult (..), 13 | ChainIndex.BurnTokenEventResult (..), 14 | getBurnTokenEventsHandler, 15 | ) where 16 | 17 | import Cardano.Api qualified as C 18 | import Data.Aeson (FromJSON, ToJSON) 19 | import Data.Word (Word64) 20 | import GHC.Generics (Generic) 21 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.MintBurnToken qualified as ChainIndex 22 | import Marconi.Cardano.Core.Orphans () 23 | import Marconi.Core.JsonRpc (ReaderHandler) 24 | import Marconi.Sidechain.Api.Types (SidechainHttpServerConfig, withChainIndexHandler) 25 | import Network.JsonRpc.Types (JsonRpc, JsonRpcErr) 26 | 27 | {- METHOD -} 28 | 29 | type RpcGetBurnTokenEventsMethod = 30 | JsonRpc 31 | "getBurnTokenEvents" 32 | GetBurnTokenEventsParams 33 | String 34 | ChainIndex.GetBurnTokenEventsResult 35 | 36 | {- TYPES -} 37 | 38 | -- | Parameter type defining the JSON shape. 39 | data GetBurnTokenEventsParams = GetBurnTokenEventsParams 40 | { policyId :: C.PolicyId 41 | , assetName :: Maybe C.AssetName 42 | , createdBeforeSlotNo :: Maybe Word64 43 | , createdAfterTx :: Maybe C.TxId 44 | } 45 | deriving stock (Eq, Show, Generic) 46 | deriving anyclass (ToJSON, FromJSON) 47 | 48 | {- | Convert the sidechain param shape to the one from 'marconi-cardano-chain-index', 49 | for reuse in the handler borrowed from that package. 50 | -} 51 | sidechainParamsToChainIndexParams 52 | :: GetBurnTokenEventsParams -> ChainIndex.GetBurnTokenEventsParams 53 | sidechainParamsToChainIndexParams GetBurnTokenEventsParams{..} = 54 | ChainIndex.GetBurnTokenEventsParams 55 | policyId 56 | assetName 57 | (C.SlotNo <$> createdBeforeSlotNo) 58 | createdAfterTx 59 | 60 | {- HANDLER -} 61 | 62 | getBurnTokenEventsHandler 63 | :: GetBurnTokenEventsParams 64 | -> ReaderHandler 65 | SidechainHttpServerConfig 66 | (Either (JsonRpcErr String) ChainIndex.GetBurnTokenEventsResult) 67 | getBurnTokenEventsHandler = 68 | withChainIndexHandler . ChainIndex.getBurnTokenEventsHandler . sidechainParamsToChainIndexParams 69 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/JsonRpc/Endpoint/CurrentSyncedBlock.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE DerivingStrategies #-} 3 | {-# LANGUAGE DuplicateRecordFields #-} 4 | 5 | module Marconi.Sidechain.Api.JsonRpc.Endpoint.CurrentSyncedBlock ( 6 | RpcCurrentSyncedBlockMethod, 7 | getCurrentSyncedBlockHandler, 8 | ChainIndex.GetCurrentSyncedBlockResult (..), 9 | ChainIndex.Tip (..), 10 | ) where 11 | 12 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.CurrentSyncedBlock qualified as ChainIndex 13 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.CurrentSyncedBlock.Tip qualified as ChainIndex 14 | import Marconi.Cardano.Core.Orphans () 15 | import Marconi.Core.JsonRpc (ReaderHandler) 16 | import Marconi.Sidechain.Api.Types (SidechainHttpServerConfig, withChainIndexHandler) 17 | import Network.JsonRpc.Types (JsonRpc, JsonRpcErr, UnusedRequestParams) 18 | 19 | {- METHOD -} 20 | 21 | type RpcCurrentSyncedBlockMethod = 22 | JsonRpc 23 | "getCurrentSyncedBlock" 24 | UnusedRequestParams 25 | String 26 | ChainIndex.GetCurrentSyncedBlockResult 27 | 28 | {- HANDLER -} 29 | 30 | getCurrentSyncedBlockHandler 31 | :: UnusedRequestParams 32 | -- ^ Will be an empty string, empty object, or null, as we are ignoring this param, and returning everything 33 | -> ReaderHandler 34 | SidechainHttpServerConfig 35 | (Either (JsonRpcErr String) ChainIndex.GetCurrentSyncedBlockResult) 36 | getCurrentSyncedBlockHandler = withChainIndexHandler . ChainIndex.getCurrentSyncedBlockHandler 37 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/JsonRpc/Endpoint/Echo.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | 3 | module Marconi.Sidechain.Api.JsonRpc.Endpoint.Echo where 4 | 5 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.Echo qualified as ChainIndex.Echo 6 | import Marconi.Core.JsonRpc (ReaderHandler) 7 | import Marconi.Sidechain.Api.Types (SidechainHttpServerConfig, withChainIndexHandler) 8 | import Network.JsonRpc.Types (JsonRpc, JsonRpcErr) 9 | 10 | {- METHOD -} 11 | 12 | type RpcEchoMethod = JsonRpc "echo" String String String 13 | 14 | {- HANDLER -} 15 | 16 | -- | Echoes message back as a JSON-RPC response. Used for testing the server. 17 | echo 18 | :: String 19 | -> ReaderHandler SidechainHttpServerConfig (Either (JsonRpcErr String) String) 20 | echo = withChainIndexHandler . ChainIndex.Echo.echo 21 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/JsonRpc/Endpoint/EpochNonce.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE DeriveAnyClass #-} 3 | {-# LANGUAGE DeriveGeneric #-} 4 | {-# LANGUAGE DerivingStrategies #-} 5 | {-# LANGUAGE StrictData #-} 6 | 7 | module Marconi.Sidechain.Api.JsonRpc.Endpoint.EpochNonce where 8 | 9 | import Cardano.Api qualified as C 10 | import Cardano.Crypto.Hash qualified as Crypto 11 | import Cardano.Ledger.BaseTypes qualified as Ledger 12 | import Data.Aeson (FromJSON, ToJSON) 13 | import Data.Word (Word64) 14 | import GHC.Generics (Generic) 15 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.EpochState qualified as ChainIndex.EpochState 16 | import Marconi.Core.JsonRpc (ReaderHandler) 17 | import Marconi.Sidechain.Api.Types (SidechainHttpServerConfig, withChainIndexHandler) 18 | import Network.JsonRpc.Types (JsonRpc, JsonRpcErr) 19 | 20 | {- METHOD -} 21 | 22 | type RpcEpochNonceMethod = 23 | JsonRpc 24 | "getNonceByEpoch" 25 | Word64 26 | String 27 | GetEpochNonceResult 28 | 29 | {- TYPES -} 30 | 31 | type GetEpochNonceResult = Maybe NonceResult 32 | 33 | -- | Result type that determines the JSON shape. 34 | data NonceResult = NonceResult 35 | { nonce :: NonceWrapper 36 | , slotNo :: Maybe C.SlotNo 37 | , blockHeaderHash :: Maybe (C.Hash C.BlockHeader) 38 | , blockNo :: C.BlockNo 39 | } 40 | deriving stock (Eq, Ord, Show, Generic) 41 | deriving anyclass (ToJSON, FromJSON) 42 | 43 | {- | Type equivalent to @Ledger.'Nonce'@ that has the desired JSON shape, with 'Nothing' 44 | corresponding to @Ledger.'NeutralNonce'@. 45 | -} 46 | type NonceWrapper = Maybe (Crypto.Hash Crypto.Blake2b_256 Ledger.Nonce) 47 | 48 | {- HANDLER -} 49 | 50 | {- | Call @ChainIndex.EpochState.'getEpochNonceHandler'@, mapping the configuration 51 | and result types to match the ones of this package. 52 | -} 53 | getEpochNonceHandler 54 | :: Word64 55 | -- ^ EpochNo 56 | -> ReaderHandler SidechainHttpServerConfig (Either (JsonRpcErr String) GetEpochNonceResult) 57 | getEpochNonceHandler = withChainIndexHandler . fmap (fmap mapResult) . ChainIndex.EpochState.getEpochNonceHandler 58 | where 59 | mapResult :: ChainIndex.EpochState.EpochNonceResult -> GetEpochNonceResult 60 | mapResult (ChainIndex.EpochState.EpochNonceResult hash bn _ sn nc) = NonceResult nc sn hash <$> bn 61 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/JsonRpc/Endpoint/PastAddressUtxo.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE DerivingStrategies #-} 3 | {-# LANGUAGE DuplicateRecordFields #-} 4 | {-# LANGUAGE StrictData #-} 5 | 6 | module Marconi.Sidechain.Api.JsonRpc.Endpoint.PastAddressUtxo ( 7 | RpcPastAddressUtxoMethod, 8 | getPastAddressUtxoHandler, 9 | ) where 10 | 11 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.Utxo qualified as ChainIndex 12 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.Utxo.Types qualified as ChainIndex 13 | import Marconi.Cardano.Core.Orphans () 14 | import Marconi.Core.JsonRpc (ReaderHandler) 15 | import Marconi.Sidechain.Api.Types (SidechainHttpServerConfig, withChainIndexHandler) 16 | import Network.JsonRpc.Types (JsonRpc, JsonRpcErr) 17 | 18 | {- METHOD -} 19 | 20 | type RpcPastAddressUtxoMethod = 21 | JsonRpc 22 | "getUtxosFromAddress" 23 | ChainIndex.GetUtxosFromAddressParams 24 | String 25 | ChainIndex.GetUtxosFromAddressResult 26 | 27 | {- HANDLER -} 28 | 29 | getPastAddressUtxoHandler 30 | :: ChainIndex.GetUtxosFromAddressParams 31 | -> ReaderHandler 32 | SidechainHttpServerConfig 33 | (Either (JsonRpcErr String) ChainIndex.GetUtxosFromAddressResult) 34 | getPastAddressUtxoHandler = withChainIndexHandler . ChainIndex.getUtxosFromAddressQueryHandler 35 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/JsonRpc/Endpoint/TargetAddresses.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | 3 | module Marconi.Sidechain.Api.JsonRpc.Endpoint.TargetAddresses where 4 | 5 | import Data.Text (Text) 6 | import Marconi.Cardano.ChainIndex.Api.JsonRpc.Endpoint.TargetAddresses qualified as ChainIndex.TargetAddresses 7 | import Marconi.Core.JsonRpc (ReaderHandler) 8 | import Marconi.Sidechain.Api.Types (SidechainHttpServerConfig, withChainIndexHandler) 9 | import Network.JsonRpc.Types (JsonRpc, JsonRpcErr, UnusedRequestParams) 10 | 11 | {- METHOD -} 12 | type RpcTargetAddressesMethod = 13 | JsonRpc 14 | "getTargetAddresses" 15 | UnusedRequestParams 16 | String 17 | [Text] 18 | 19 | {- HANDLER -} 20 | getTargetAddressesQueryHandler 21 | :: UnusedRequestParams 22 | -- ^ Will be an empty string, empty object, or null, as we are ignoring this param, and returning everything 23 | -> ReaderHandler SidechainHttpServerConfig (Either (JsonRpcErr String) [Text]) 24 | getTargetAddressesQueryHandler = withChainIndexHandler . ChainIndex.TargetAddresses.getTargetAddressesQueryHandler 25 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/JsonRpc/Routes.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TypeOperators #-} 3 | 4 | module Marconi.Sidechain.Api.JsonRpc.Routes where 5 | 6 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.BurnTokenEvent ( 7 | RpcGetBurnTokenEventsMethod, 8 | ) 9 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.CurrentSyncedBlock ( 10 | RpcCurrentSyncedBlockMethod, 11 | ) 12 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.Echo (RpcEchoMethod) 13 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.EpochActiveStakePoolDelegation ( 14 | RpcEpochActiveStakePoolDelegationMethod, 15 | ) 16 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.EpochNonce (RpcEpochNonceMethod) 17 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.PastAddressUtxo ( 18 | RpcPastAddressUtxoMethod, 19 | ) 20 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.TargetAddresses ( 21 | RpcTargetAddressesMethod, 22 | ) 23 | import Network.JsonRpc.Types (RawJsonRpc) 24 | import Servant.API ((:<|>), (:>)) 25 | 26 | type JsonRpcAPI = "json-rpc" :> RawJsonRpc RpcAPI 27 | 28 | -- | JSON-RPC methods 29 | type RpcAPI = 30 | RpcEchoMethod 31 | :<|> RpcTargetAddressesMethod 32 | :<|> RpcCurrentSyncedBlockMethod 33 | :<|> RpcPastAddressUtxoMethod 34 | :<|> RpcGetBurnTokenEventsMethod 35 | :<|> RpcEpochActiveStakePoolDelegationMethod 36 | :<|> RpcEpochNonceMethod 37 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/JsonRpc/Server.hs: -------------------------------------------------------------------------------- 1 | module Marconi.Sidechain.Api.JsonRpc.Server where 2 | 3 | import Marconi.Core.JsonRpc (ReaderServer) 4 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.BurnTokenEvent ( 5 | getBurnTokenEventsHandler, 6 | ) 7 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.CurrentSyncedBlock ( 8 | getCurrentSyncedBlockHandler, 9 | ) 10 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.Echo (echo) 11 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.EpochActiveStakePoolDelegation ( 12 | getEpochActiveStakePoolDelegationHandler, 13 | ) 14 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.EpochNonce (getEpochNonceHandler) 15 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.PastAddressUtxo ( 16 | getPastAddressUtxoHandler, 17 | ) 18 | import Marconi.Sidechain.Api.JsonRpc.Endpoint.TargetAddresses ( 19 | getTargetAddressesQueryHandler, 20 | ) 21 | import Marconi.Sidechain.Api.JsonRpc.Routes (JsonRpcAPI) 22 | import Marconi.Sidechain.Api.Types (SidechainHttpServerConfig) 23 | import Servant.API ((:<|>) ((:<|>))) 24 | 25 | -- | Handlers for the JSON-RPC 26 | jsonRpcServer :: ReaderServer SidechainHttpServerConfig JsonRpcAPI 27 | jsonRpcServer = 28 | echo 29 | :<|> getTargetAddressesQueryHandler 30 | :<|> getCurrentSyncedBlockHandler 31 | :<|> getPastAddressUtxoHandler 32 | :<|> getBurnTokenEventsHandler 33 | :<|> getEpochActiveStakePoolDelegationHandler 34 | :<|> getEpochNonceHandler 35 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/Rest/Endpoint/Metrics.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE TypeOperators #-} 3 | 4 | module Marconi.Sidechain.Api.Rest.Endpoint.Metrics where 5 | 6 | import Control.Monad.Trans (liftIO) 7 | import Data.ByteString qualified as BS 8 | import Data.Text (Text) 9 | import Data.Text.Encoding qualified as Text 10 | import Marconi.Core.JsonRpc (ReaderHandler) 11 | import Marconi.Sidechain.Api.Types (SidechainHttpServerConfig) 12 | import Prometheus qualified as P 13 | import Servant (Get, PlainText, (:>)) 14 | 15 | ------------------ 16 | -- Method types -- 17 | ------------------ 18 | 19 | type GetMetrics = "metrics" :> Get '[PlainText] Text 20 | 21 | ---------------------------- 22 | -- Query and result types -- 23 | ---------------------------- 24 | 25 | getMetricsHandler :: ReaderHandler SidechainHttpServerConfig Text 26 | getMetricsHandler = liftIO $ Text.decodeUtf8 . BS.toStrict <$> P.exportMetricsAsText 27 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/Rest/Routes.hs: -------------------------------------------------------------------------------- 1 | module Marconi.Sidechain.Api.Rest.Routes (RestAPI) where 2 | 3 | import Marconi.Cardano.ChainIndex.Api.Rest.Endpoint.Metrics (GetMetrics) 4 | 5 | -- | Routes for the REST API 6 | type RestAPI = GetMetrics 7 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/Rest/Server.hs: -------------------------------------------------------------------------------- 1 | module Marconi.Sidechain.Api.Rest.Server (restApiServer) where 2 | 3 | import Marconi.Core.JsonRpc (ReaderServer) 4 | import Marconi.Sidechain.Api.Rest.Endpoint.Metrics (getMetricsHandler) 5 | import Marconi.Sidechain.Api.Rest.Routes (RestAPI) 6 | import Marconi.Sidechain.Api.Types ( 7 | SidechainHttpServerConfig, 8 | ) 9 | 10 | -- | Handlers for the REST API 11 | restApiServer :: ReaderServer SidechainHttpServerConfig RestAPI 12 | restApiServer = getMetricsHandler 13 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Api/Types.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | 3 | module Marconi.Sidechain.Api.Types ( 4 | Types.configPort, 5 | Types.configTrace, 6 | module Marconi.Sidechain.Api.Types, 7 | ) where 8 | 9 | import Cardano.Api qualified as C 10 | import Control.Lens (makeLenses, (^.)) 11 | import Data.List.NonEmpty (NonEmpty) 12 | import Marconi.Cardano.ChainIndex.Api.Types qualified as Types 13 | import Marconi.Core.JsonRpc (ReaderHandler, withReaderHandler) 14 | 15 | {- SERVER CONFIG -} 16 | 17 | {- | 'SidechainHttpServerConfig' augments @Types.'HttpServerConfig'@ with 18 | - additional fields containing anything needed for sidechain 19 | - queries not included in @Types.'HttpServerConfig'@. 20 | -} 21 | data SidechainHttpServerConfig = SidechainHttpServerConfig 22 | { _chainIndexHttpServerConfig :: !Types.HttpServerConfig 23 | , _sidechainTargetAssets :: Maybe TargetAssets 24 | } 25 | 26 | {- SIDECHAIN EXTRA CONFIG -} 27 | 28 | type TargetAssets = NonEmpty (C.PolicyId, Maybe C.AssetName) 29 | 30 | {- LENSES -} 31 | makeLenses ''SidechainHttpServerConfig 32 | 33 | {- UTILITIES -} 34 | 35 | {- | Specialization of a composition of 'mapError' and 'withReaderT' to facilitate 36 | mappings between handlers from `marconi-cardano-chain-index` using @Types.'HttpServerConfig'@ 37 | and handlers of this package using 'SidechainHttpServerConfig'. 38 | -} 39 | withChainIndexHandler 40 | :: ReaderHandler Types.HttpServerConfig a 41 | -> ReaderHandler SidechainHttpServerConfig a 42 | withChainIndexHandler = withReaderHandler (^. chainIndexHttpServerConfig) 43 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Error.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DerivingStrategies #-} 2 | {-# LANGUAGE FlexibleInstances #-} 3 | 4 | module Marconi.Sidechain.Error ( 5 | HasExit (toExit), 6 | Exit (TimedOut, Errored, Killed, Terminated, Interrupted), 7 | withSignalHandling, 8 | toExitCode, 9 | ) where 10 | 11 | import Control.Concurrent.Async (race) 12 | import Control.Concurrent.MVar (newEmptyMVar, takeMVar, tryPutMVar) 13 | import Control.Monad (void) 14 | import Data.Foldable (traverse_) 15 | import Marconi.Cardano.ChainIndex.Error (IndexerError (Timeout)) 16 | import Marconi.Core qualified as Core 17 | import System.Posix (Handler (CatchOnce), Signal, installHandler, sigINT, sigTERM) 18 | 19 | -- * Mapping of exceptions to exit codes 20 | 21 | -- | Represents a subset of exit codes 22 | data Exit 23 | = -- | 142 24 | TimedOut 25 | | -- | 1 26 | Errored 27 | | -- | 137 28 | Killed 29 | | -- | 143 30 | Terminated 31 | | -- | 130 32 | Interrupted 33 | 34 | -- | Maps a domain concept to a subset of exit codes 35 | class HasExit e where 36 | toExit :: e -> Exit 37 | 38 | instance HasExit (IndexerError a) where 39 | toExit err = 40 | case err of 41 | Timeout _ -> TimedOut 42 | _ -> Errored 43 | 44 | instance HasExit Core.IndexerError where 45 | toExit _ = Errored 46 | 47 | instance HasExit Signal where 48 | toExit signal 49 | | signal == sigINT = Interrupted 50 | | signal == sigTERM = Terminated 51 | | otherwise = Killed 52 | 53 | -- | Maps anything that satisfies 'HasExit' to an 'Int', to be used as an exit code 54 | toExitCode :: (HasExit a) => a -> Int 55 | toExitCode = exitToInt . toExit 56 | where 57 | exitToInt :: Exit -> Int 58 | exitToInt ex = case ex of 59 | TimedOut -> 142 -- 128 + SIGALRM (14) = 142 60 | Errored -> 1 61 | Killed -> 137 62 | Terminated -> 143 63 | Interrupted -> 130 64 | 65 | -- * Signal handling 66 | 67 | {- | Takes an action and runs it while waiting for either a `SIGINT` or a `SIGTERM` to be thrown. 68 | If one is thrown, it's swallowed, but the value is returned to be interpreted by the caller of 69 | this function. 70 | -} 71 | withSignalHandling :: IO a -> IO (Either Signal a) 72 | withSignalHandling action = do 73 | termVar <- newEmptyMVar 74 | let terminate terminationType = void $ tryPutMVar termVar terminationType 75 | waitForTermination = takeMVar termVar 76 | signals = [sigINT, sigTERM] 77 | traverse_ (\signal -> installHandler signal (CatchOnce (terminate signal)) Nothing) signals 78 | race 79 | waitForTermination 80 | action 81 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Run.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeApplications #-} 2 | 3 | module Marconi.Sidechain.Run where 4 | 5 | import Cardano.BM.Setup qualified as BM 6 | import Cardano.BM.Trace (logInfo) 7 | import Cardano.BM.Tracing qualified as BM 8 | import Control.Exception (finally) 9 | import Control.Lens ((^.)) 10 | import Control.Monad.Reader (runReaderT) 11 | import Data.Text qualified as Text 12 | import Data.Text.Lazy qualified as Text (toStrict) 13 | import Marconi.Cardano.Core.Logger (defaultStdOutLogger) 14 | import Marconi.Core (IndexerError) 15 | import Marconi.Sidechain.Api.HttpServer (runHttpServer) 16 | import Marconi.Sidechain.CLI ( 17 | CliArgs (debugMode), 18 | getVersion, 19 | parseCli, 20 | ) 21 | import Marconi.Sidechain.Concurrency ( 22 | HandledAction (Handled, Unhandled), 23 | raceSignalHandled_, 24 | ) 25 | import Marconi.Sidechain.Env ( 26 | mkSidechainEnvFromCliArgs, 27 | querySecurityParamFromCliArgs, 28 | sidechainHttpServerConfig, 29 | sidechainRunIndexersConfig, 30 | ) 31 | import Marconi.Sidechain.Indexers (runIndexers) 32 | import Text.Pretty.Simple (pShowDarkBg) 33 | 34 | run :: IO () 35 | run = 36 | do 37 | cliArgs <- parseCli 38 | let logLevel = if debugMode cliArgs then BM.Debug else BM.Info 39 | (trace, sb) <- defaultStdOutLogger "marconi-sidechain-experimental" logLevel 40 | 41 | logInfo trace $ "marconi-sidechain-" <> Text.pack getVersion 42 | logInfo trace . Text.toStrict $ pShowDarkBg cliArgs 43 | 44 | -- Create the 'SidechainEnv' from the CLI arguments, 45 | -- with some validity checks on arguments needed to create the environment. 46 | env <- mkSidechainEnvFromCliArgs trace sb cliArgs =<< querySecurityParamFromCliArgs trace cliArgs 47 | 48 | raceSignalHandled_ 49 | (Unhandled (runReaderT runHttpServer (env ^. sidechainHttpServerConfig))) 50 | (Handled @IndexerError (runReaderT runIndexers (env ^. sidechainRunIndexersConfig))) 51 | `finally` BM.shutdown sb 52 | -------------------------------------------------------------------------------- /marconi-sidechain/src/Marconi/Sidechain/Utils.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | 3 | module Marconi.Sidechain.Utils where 4 | 5 | import Control.Concurrent.STM (STM, TMVar, putTMVar, tryTakeTMVar) 6 | 7 | {- | Non-blocking write of a new value to a 'TMVar' 8 | Puts if empty. Replaces if populated. 9 | 10 | Needs a later version of stm than we're using currently. 11 | TODO: Remove this function once we no longer need backwards compatibility. 12 | -} 13 | #if !MIN_VERSION_stm(2,5,1) 14 | writeTMVar :: TMVar a -> a -> STM () 15 | writeTMVar t new = tryTakeTMVar t >> putTMVar t new 16 | #endif 17 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ExplicitNamespaces #-} 2 | 3 | module Main (main) where 4 | 5 | import Spec.Marconi.Sidechain.Api.JsonRpc.Endpoint.BurnTokenEvent qualified as BurnTokenEvent 6 | import Spec.Marconi.Sidechain.Api.JsonRpc.Endpoint.PastAddressUtxo qualified as PastAddressUtxo 7 | import Spec.Marconi.Sidechain.CLI qualified as CLI 8 | import Spec.Marconi.Sidechain.CLIInputValidation qualified as CLIInputValidation 9 | import Spec.Marconi.Sidechain.Routes qualified as Routes 10 | import Test.Tasty (TestTree, defaultMain, testGroup) 11 | 12 | main :: IO () 13 | main = defaultMain tests 14 | 15 | tests :: TestTree 16 | tests = 17 | testGroup 18 | "marconi-sidechain" 19 | [ BurnTokenEvent.tests 20 | , PastAddressUtxo.tests 21 | , CLI.tests 22 | , CLIInputValidation.tests 23 | , Routes.tests 24 | ] 25 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAddress-MissingBech32Prefix.golden: -------------------------------------------------------------------------------- 1 | option --addresses-to-index: Invalid address (not a valid Bech32 address representation): "x8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gt7r0vd4msrxnuwnccdxlhdjar77j6lg0wypcc9uar5d2shskhj42g" 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAddress-badFormat.golden: -------------------------------------------------------------------------------- 1 | option --addresses-to-index: Invalid address (not a valid Bech32 address representation): "notAnAddress" 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAddressPrefix-stake1.golden: -------------------------------------------------------------------------------- 1 | option --addresses-to-index: Invalid address (not a valid Bech32 address representation): "stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw" 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAddressPrefix-stake_test.golden: -------------------------------------------------------------------------------- 1 | option --addresses-to-index: Invalid address (not a valid Bech32 address representation): "stake_test17rphkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcljw6kf" 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAddress_1CharShort.golden: -------------------------------------------------------------------------------- 1 | option --addresses-to-index: Invalid address (not a valid Bech32 address representation): "addr1x8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gt7r0vd4msrxnuwnccdxlhdjar77j6lg0wypcc9uar5d2shskhj42" 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAssetId-assetName-invalidBase16Format.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Expected Base16-encoded bytestring, but got 0000notAToken0; invalid character at offset: 4 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAssetId-assetName-invalidBase16Length.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Expected Base16-encoded bytestring, but got deadbeeef; invalid bytestring size 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAssetId-assetName-tooLong.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Failed to deserialise deadbeef0000000000000000000000000000000000000000000000000000000000 as AssetName. Unable to deserialise AssetName (the bytestring should be no longer than 32 bytes long which corresponds to a hex representation of 64 characters) 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAssetId-policyId-1ByteLong.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Failed to deserialise 1cdc58c3b6d1ab11dd047ac9e3a2ec26aabf0839abe37b791cb810f2f2 as PolicyId. Incorrect number of bytes 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAssetId-policyId-1ByteShort.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Failed to deserialise 1cdc58c3b6d1ab11dd047ac9e3a2ec26aabf0839abe37b791cb810 as PolicyId. Incorrect number of bytes 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAssetId-policyId-1HexCharLong.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Expected Base16-encoded bytestring, but got 1cdc58c3b6d1ab11dd047ac9e3a2ec26aabf0839abe37b791cb810f2f; invalid bytestring size 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAssetId-policyId-1HexCharShort.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Expected Base16-encoded bytestring, but got 1cdc58c3b6d1ab11dd047ac9e3a2ec26aabf0839abe37b791cb810f; invalid bytestring size 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/CLIInputValidation/invalidAssetId-policyId-badBase16Format.golden: -------------------------------------------------------------------------------- 1 | option --match-asset-id: Expected Base16-encoded bytestring, but got 00aPolicyIdOfCorrectLength000000000000000000000000000000000000; invalid character at offset: 3 2 | 3 | Usage: marconi-sidechain [--version] [--debug] (-s|--socket-path FILE-PATH) 4 | --node-config-path ARG (-d|--db-dir DIR) 5 | [--http-port INT] (--mainnet | --testnet-magic NATURAL) 6 | [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain 15 | -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/Cli/marconi-sidechain___socket.help: -------------------------------------------------------------------------------- 1 | Invalid option `--socket' 2 | 3 | Usage: marconi-sidechain-experimental 4 | [--version] [--debug] (-s|--socket-path FILE-PATH) 5 | --node-config-path ARG (-d|--db-dir DIR) [--http-port INT] 6 | (--mainnet | --testnet-magic NATURAL) [--batch-size INT] 7 | [(-a|--addresses-to-index BECH32-ADDRESS)] 8 | [(--match-asset-id POLICY_ID[.ASSET_NAME])] 9 | [--initial-retry-time NATURAL] 10 | [--no-max-retry-time | --max-retry-time NATURAL] 11 | [--start-from-genesis | --start-from-last-sync-points | 12 | --start-from SLOT-NO:BLOCK-HEADER-HASH] 13 | 14 | marconi-sidechain -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/Routes/address-utxo-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "blockHeaderHash": "6161616161616161616161616161616161616161616161616161616161616161", 4 | "blockNo": 1, 5 | "datum": null, 6 | "datumHash": null, 7 | "epochNo": 0, 8 | "slotNo": 1, 9 | "spentBy": null, 10 | "txId": "ec7d3bd7c6a3a31368093b077af0db46ceac77956999eb842373e08c6420f000", 11 | "txIndexInBlock": 0, 12 | "txInputs": [ 13 | { 14 | "txId": "2f1f574c0365afd9865332eec4ff75e599d80c525afc7b7d6e38d27d0a01bf47", 15 | "txIx": 1 16 | } 17 | ], 18 | "txIx": 0, 19 | "value": { 20 | "": { 21 | "": 10 22 | } 23 | } 24 | }, 25 | { 26 | "blockHeaderHash": "6161616161616161616161616161616161616161616161616161616161616161", 27 | "blockNo": 1, 28 | "datum": { 29 | "int": 34 30 | }, 31 | "datumHash": "6d683f9bd0c3b874cdf3da1793b5eb0ea73a074d3e4b66bc62279b09d387fa8d", 32 | "epochNo": 0, 33 | "slotNo": 1, 34 | "spentBy": { 35 | "slotNo": 12, 36 | "txId": "2e19f40cdf462444234d0de049163d5269ee1150feda868560315346dd12807d" 37 | }, 38 | "txId": "ec7d3bd7c6a3a31368093b077af0db46ceac77956999eb842373e08c6420f000", 39 | "txIndexInBlock": 0, 40 | "txInputs": [ 41 | { 42 | "txId": "ec7d3bd7c6a3a31368093b077af0db46ceac77956999eb842373e08c6420f000", 43 | "txIx": 0 44 | } 45 | ], 46 | "txIx": 0, 47 | "value": { 48 | "": { 49 | "": 1 50 | } 51 | } 52 | } 53 | ] -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/Routes/current-synced-point-response-1.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/Routes/current-synced-point-response-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "blockHeaderHash": "6161616161616161616161616161616161616161616161616161616161616161", 3 | "blockNo": 64903, 4 | "blockTimestamp": 0, 5 | "epochNo": 6, 6 | "nodeTip": { 7 | "blockHeaderHash": "6161616161616161616161616161616161616161616161616161616161616161", 8 | "blockNo": 64903, 9 | "slotNo": 1 10 | }, 11 | "slotNo": 1 12 | } -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/Routes/epoch-nonce-response.json: -------------------------------------------------------------------------------- 1 | { 2 | "blockHeaderHash": "fdd5eb1b1e9fc278a08aef2f6c0fe9b576efd76966cc552d8c5a59271dc01604", 3 | "blockNo": 21645, 4 | "nonce": "ce4a80f49c44c21d7114d93fe5f992a2f9de6bad4a03a5df7e7403004ebe16fc", 5 | "slotNo": 518400 6 | } -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/Routes/epoch-stakepooldelegation-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "blockHeaderHash": "578f3cb70f4153e1622db792fea9005c80ff80f83df028210c7a914fb780a6f6", 4 | "blockNo": 64903, 5 | "lovelace": 100000000000000, 6 | "poolId": "pool1z22x50lqsrwent6en0llzzs9e577rx7n3mv9kfw7udwa2rf42fa", 7 | "slotNo": 1382422 8 | }, 9 | { 10 | "blockHeaderHash": "578f3cb70f4153e1622db792fea9005c80ff80f83df028210c7a914fb780a6f6", 11 | "blockNo": 64903, 12 | "lovelace": 100000000000000, 13 | "poolId": "pool1547tew8vmuj0g6vj3k5jfddudextcw6hsk2hwgg6pkhk7lwphe6", 14 | "slotNo": 1382422 15 | }, 16 | { 17 | "blockHeaderHash": "578f3cb70f4153e1622db792fea9005c80ff80f83df028210c7a914fb780a6f6", 18 | "blockNo": 64903, 19 | "lovelace": 100000000000000, 20 | "poolId": "pool174mw7e20768e8vj4fn8y6p536n8rkzswsapwtwn354dckpjqzr8", 21 | "slotNo": 1382422 22 | } 23 | ] -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Golden/Routes/mintingpolicyhash-tx-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "assetName": "", 4 | "blockHeaderHash": "6161616161616161616161616161616161616161616161616161616161616161", 5 | "blockNo": 1047, 6 | "burnAmount": 10, 7 | "isStable": true, 8 | "redeemer": { 9 | "int": 34 10 | }, 11 | "redeemerHash": "6d683f9bd0c3b874cdf3da1793b5eb0ea73a074d3e4b66bc62279b09d387fa8d", 12 | "slotNo": 1, 13 | "txId": "ec7d3bd7c6a3a31368093b077af0db46ceac77956999eb842373e08c6420f000" 14 | } 15 | ] -------------------------------------------------------------------------------- /marconi-sidechain/test/Spec/Marconi/Sidechain/CLI.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | module Spec.Marconi.Sidechain.CLI where 4 | 5 | import Data.ByteString.Lazy (ByteString, fromStrict) 6 | import Data.Text qualified as T 7 | import Data.Text.Encoding (encodeUtf8) 8 | import Marconi.Sidechain.CLI (programParser) 9 | import Options.Applicative ( 10 | ParserResult (CompletionInvoked, Failure, Success), 11 | defaultPrefs, 12 | execParserPure, 13 | renderFailure, 14 | ) 15 | import Test.Tasty (TestTree, testGroup) 16 | import Test.Tasty.Golden (goldenVsStringDiff) 17 | 18 | tests :: TestTree 19 | tests = 20 | testGroup 21 | "marconi-sidechain CLI Specs" 22 | [ genTest commands 23 | | commands <- 24 | [ ["--socket"] -- invalid command 25 | , ["--help"] -- help screen 26 | ] 27 | ] 28 | 29 | -- | Test generate golden tests from the list of commands 30 | genTest :: [T.Text] -> TestTree 31 | genTest commands = do 32 | let goldenFile = 33 | T.unpack $ 34 | "test/Spec/Golden/Cli/" 35 | <> T.intercalate "_" ("marconi-sidechain" : (T.replace "-" "_" <$> commands)) 36 | <> ".help" 37 | 38 | goldenVsStringDiff 39 | (T.unpack $ T.unwords commands) 40 | (\expected actual -> ["diff", "--color=always", expected, actual]) 41 | goldenFile 42 | (generateHelpScreen commands) 43 | 44 | {- | Test generate cli tests and parse the help screen. 45 | Generated tests are incomplete CLI invocations that will result in 46 | - printng the cli produced error 47 | - help screen 48 | -} 49 | generateHelpScreen :: [T.Text] -> IO ByteString 50 | generateHelpScreen commands = do 51 | let parser = programParser 52 | text = case execParserPure defaultPrefs parser (T.unpack <$> commands) of 53 | Failure failure -> failure 54 | Success _ -> error "Parser expected to fail" 55 | CompletionInvoked _ -> error "Parser expected to fail" 56 | pure $ 57 | fromStrict (encodeUtf8 . T.pack <$> fst $ renderFailure text "marconi-sidechain-experimental") 58 | -------------------------------------------------------------------------------- /marconi-starter/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Input Output Global, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /marconi-starter/app/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Marconi.Starter.Run (runApp) 4 | 5 | main :: IO () 6 | main = runApp 7 | -------------------------------------------------------------------------------- /marconi-starter/src/Marconi/Starter/CLI.hs: -------------------------------------------------------------------------------- 1 | module Marconi.Starter.CLI where 2 | 3 | import Marconi.Cardano.ChainIndex.CLI qualified as CLI 4 | import Options.Applicative qualified as Opt 5 | 6 | data Options = Options 7 | { commonOptions :: !CLI.CommonOptions 8 | , optionsDbPath :: !FilePath 9 | , optionsHttpPort :: !Int 10 | } 11 | 12 | parseOptions :: IO Options 13 | parseOptions = Opt.execParser programParser 14 | 15 | programParser :: Opt.ParserInfo Options 16 | programParser = 17 | Opt.info 18 | (Opt.helper <*> optionsParser) 19 | programDescription 20 | 21 | optionsParser :: Opt.Parser Options 22 | optionsParser = 23 | Options 24 | <$> CLI.commonOptionsParser 25 | <*> CLI.commonDbDirParser 26 | <*> CLI.commonPortParser 27 | 28 | programDescription :: Opt.InfoMod a 29 | programDescription = 30 | Opt.fullDesc 31 | <> Opt.progDesc "marconi-starter" 32 | <> Opt.header "Marconi starter executable" 33 | -------------------------------------------------------------------------------- /marconi-starter/src/Marconi/Starter/Env.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | 3 | module Marconi.Starter.Env where 4 | 5 | import Control.Lens (Lens', makeLenses) 6 | import Marconi.Cardano.Core.Logger (MarconiTrace) 7 | import Marconi.Cardano.Core.Types (SecurityParam) 8 | import Marconi.Starter.CLI qualified as CLI 9 | import Marconi.Starter.Indexers.AddressCount (AddressCountStandardWorker) 10 | 11 | data Env = Env 12 | { _envIndexers :: IndexersEnv 13 | -- ^ Access the indexers for querying purposes. 14 | , _envCliArgs :: !CLI.Options 15 | , _envStdoutTrace :: !(MarconiTrace IO) 16 | , _securityParam :: !SecurityParam 17 | } 18 | 19 | -- | Should contain all the indexers required by the app. 20 | newtype IndexersEnv = IndexersEnv 21 | { _addressCountIndexerEnv :: AddressCountIndexerEnv 22 | } 23 | 24 | newtype AddressCountIndexerEnv = AddressCountIndexerEnv 25 | { _addressCountIndexerVar :: AddressCountStandardWorker 26 | } 27 | 28 | makeLenses ''Env 29 | makeLenses ''IndexersEnv 30 | makeLenses ''AddressCountIndexerEnv 31 | 32 | addressCountIndexerWorker :: Lens' Env AddressCountStandardWorker 33 | addressCountIndexerWorker = envIndexers . addressCountIndexerEnv . addressCountIndexerVar 34 | -------------------------------------------------------------------------------- /marconi-starter/src/Marconi/Starter/HttpServer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE RankNTypes #-} 4 | {-# LANGUAGE TypeApplications #-} 5 | {-# LANGUAGE TypeOperators #-} 6 | 7 | module Marconi.Starter.HttpServer where 8 | 9 | import Control.Lens (to, view, (^.)) 10 | import Control.Monad.IO.Class (liftIO) 11 | import Control.Monad.Reader (ReaderT, ask) 12 | import Data.Data (Proxy (Proxy)) 13 | import Marconi.Cardano.Core.Indexer.Worker qualified as Core 14 | import Marconi.Core qualified as Core 15 | import Marconi.Core.JsonRpc qualified as Core 16 | import Marconi.Starter.CLI (optionsHttpPort) 17 | import Marconi.Starter.Env ( 18 | Env, 19 | addressCountIndexerWorker, 20 | envCliArgs, 21 | ) 22 | import Marconi.Starter.Indexers.AddressCount (AddressCountQuery) 23 | import Network.JsonRpc.Server.Types () 24 | import Network.JsonRpc.Types (JsonRpc, RawJsonRpc) 25 | import Network.Wai.Handler.Warp (defaultSettings, runSettings, setPort) 26 | import Servant ((:>)) 27 | import Servant.Server (Application, Server, serve) 28 | 29 | {---------------------- 30 | API specification 31 | -----------------------} 32 | 33 | type API = "api" :> RawJsonRpc RpcMethod 34 | 35 | type RpcMethod = RpcAddressCountMethod 36 | 37 | type RpcAddressCountMethod = 38 | JsonRpc 39 | "getAddressCount" 40 | AddressCountQuery 41 | String 42 | (Core.Result AddressCountQuery) 43 | 44 | {---------------------- 45 | API implementation 46 | -----------------------} 47 | 48 | runHttpServer :: ReaderT Env IO () 49 | runHttpServer = do 50 | env <- ask 51 | let cliOptions = view envCliArgs env 52 | let httpSettings = setPort (optionsHttpPort cliOptions) defaultSettings 53 | liftIO $ runSettings httpSettings (httpApp env) 54 | 55 | httpApp 56 | :: Env 57 | -> Application 58 | httpApp env = serve (Proxy @API) (httpServer env) 59 | 60 | httpServer 61 | :: Env 62 | -> Server API 63 | httpServer env = 64 | Core.queryIndexerVarHttpHandler $ 65 | env ^. addressCountIndexerWorker . to Core.standardWorkerIndexerVar 66 | -------------------------------------------------------------------------------- /marconi-starter/src/Marconi/Starter/Run.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeApplications #-} 2 | 3 | module Marconi.Starter.Run where 4 | 5 | import Cardano.BM.Setup (withTrace) 6 | import Cardano.BM.Tracing (defaultConfigStdout) 7 | import Control.Concurrent.Async (race_) 8 | import Control.Exception (throwIO) 9 | import Control.Monad.Except (runExceptT) 10 | import Control.Monad.IO.Class (liftIO) 11 | import Control.Monad.Reader (runReaderT) 12 | import Data.Void (Void) 13 | import Marconi.Cardano.ChainIndex.CLI qualified as CommonCLI 14 | import Marconi.Cardano.ChainIndex.SecurityParam qualified as SecurityParam 15 | import Marconi.Cardano.Core.Logger (MarconiTrace, mkMarconiTrace) 16 | import Marconi.Cardano.Core.Node.Client.Retry (withNodeConnectRetry) 17 | import Marconi.Cardano.Core.Types (SecurityParam) 18 | import Marconi.Starter.CLI qualified as CLI 19 | import Marconi.Starter.Env (Env (Env)) 20 | import Marconi.Starter.HttpServer qualified as HttpServer 21 | import Marconi.Starter.Indexers (buildIndexersEnv) 22 | import Marconi.Starter.Indexers qualified as Indexers 23 | import System.Directory (createDirectoryIfMissing) 24 | 25 | runApp :: IO () 26 | runApp = do 27 | cliOptions <- CLI.parseOptions 28 | 29 | traceConfig <- defaultConfigStdout 30 | 31 | withTrace traceConfig "marconi-starter" $ \stdoutTrace -> do 32 | let marconiTrace = mkMarconiTrace stdoutTrace 33 | securityParam <- querySecuritParamWithRetry marconiTrace cliOptions 34 | liftIO $ createDirectoryIfMissing True (CLI.optionsDbPath cliOptions) 35 | indexersEnv <- buildIndexersEnv securityParam cliOptions 36 | let env = Env indexersEnv cliOptions marconiTrace securityParam 37 | race_ 38 | (runReaderT Indexers.runIndexers env) 39 | (runReaderT HttpServer.runHttpServer env) 40 | 41 | querySecuritParamWithRetry 42 | :: MarconiTrace IO 43 | -> CLI.Options 44 | -> IO SecurityParam 45 | querySecuritParamWithRetry stdoutTrace cliOptions = do 46 | let retryConfig = CommonCLI.optionsRetryConfig $ CLI.commonOptions cliOptions 47 | networkId = CommonCLI.optionsNetworkId $ CLI.commonOptions cliOptions 48 | socketPath = CommonCLI.optionsSocketPath $ CLI.commonOptions cliOptions 49 | liftIO $ 50 | withNodeConnectRetry stdoutTrace retryConfig socketPath $ 51 | runExceptT (SecurityParam.querySecurityParam @Void networkId socketPath) 52 | >>= either throwIO pure 53 | -------------------------------------------------------------------------------- /marconi-starter/test/Driver.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -F -pgmF tasty-discover #-} 2 | {-# OPTIONS_GHC -Wno-missing-import-lists #-} 3 | -------------------------------------------------------------------------------- /nix/outputs.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, inputs, pkgs, lib, system, ... }: 2 | 3 | let 4 | project = repoRoot.nix.project; 5 | in 6 | [ 7 | ( 8 | project.flake 9 | ) 10 | { 11 | devShells.profiled = project.variants.profiled.devShell; 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /nix/shell.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, inputs, pkgs, ... }: 2 | 3 | _cabalProject: 4 | 5 | let 6 | cardano-cli = inputs.cardano-node.legacyPackages.cardano-cli; 7 | cardano-node = inputs.cardano-node.legacyPackages.cardano-node; 8 | in 9 | 10 | { 11 | name = "marconi"; 12 | 13 | packages = [ 14 | cardano-cli 15 | cardano-node 16 | inputs.mithril.packages.mithril-client 17 | pkgs.ghcid 18 | pkgs.haskellPackages.hoogle 19 | ]; 20 | 21 | scripts = { 22 | start-benchmark-machine = { 23 | enable = false; 24 | group = "benchmarking"; 25 | exec = repoRoot.scripts.start-benchmarking-machine; 26 | description = '' 27 | Start the benchmarking NixOS VM exposing Grafana dashboards and Prometheus metrics for Marconi. 28 | ''; 29 | }; 30 | }; 31 | 32 | env = { 33 | CARDANO_CLI = "${cardano-cli}/bin/cardano-cli"; 34 | CARDANO_NODE = "${cardano-node}/bin/cardano-node"; 35 | CARDANO_NODE_CONFIG = ../config; 36 | }; 37 | 38 | preCommit = { 39 | fourmolu.enable = true; 40 | shellcheck.enable = false; 41 | cabal-fmt.enable = true; 42 | optipng.enable = true; 43 | nixpkgs-fmt.enable = true; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /scripts/babbage/conway-babbage-test-genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "genDelegs": {} 3 | } 4 | -------------------------------------------------------------------------------- /scriv.ini: -------------------------------------------------------------------------------- 1 | [scriv] 2 | format = md 3 | --------------------------------------------------------------------------------