Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# Copilot Instructions for cardano-node-emulator

## Repository Overview

This is a Haskell project that provides an in-memory blockchain emulator for testing Cardano smart contracts and transactions. The repository is organized as a multi-package Cabal project with five main packages:

- **cardano-node-emulator**: Core emulator providing an in-memory blockchain
- **cardano-node-socket-emulator**: Socket-based wrapper exposing node-to-client protocol over Unix sockets
- **plutus-ledger**: Ledger types and functionality for Plutus scripts and transactions
- **plutus-script-utils**: Utilities for writing and working with Plutus scripts
- **freer-extras**: Extensions to the `simple-freer` effect system

## Development Environment

**Required setup**: Use Nix exclusively to ensure consistent tool versions across all developers.

```bash
# Enter development shell with all dependencies and tools pre-configured
nix develop

# Inside nix develop, you'll see a menu of available commands
```

The development shell provides:
- GHC 9.6.6 (Haskell compiler)
- Cabal 3.14.1.0 (build tool)
- HLint (linter)
- Ormolu (code formatter)
- Hpack (package definition formatter)
- Pre-commit hooks (automatically run on git commit)

**Without Nix** (not recommended): Refer to CONTRIBUTING.md for workarounds, but be aware you may encounter version mismatches.

## Build, Test & Lint Commands

### Building

```bash
# Build entire workspace
cabal build all

# Build a specific package (e.g., cardano-node-emulator)
cabal build cardano-node-emulator

# Build with tests enabled
cabal build all --enable-tests
```

### Testing

```bash
# Run all tests in the workspace
cabal test all --test-show-details=direct

# Run tests for a specific package
cabal test cardano-node-emulator --test-show-details=direct

# Run a single test module
cabal test cardano-node-emulator --test-show-details=direct -- +RTS -N1 -RTS

# Parallel test execution is not supported; tests run sequentially (-j1)
```

### Code Quality

```bash
# Lint code with HLint
hlint .

# Format Haskell code with Ormolu
# This runs automatically via pre-commit hooks on `git commit`
```

### Documentation

```bash
# Generate Haddock documentation for a package
cabal haddock cardano-node-emulator
```

## Key Architecture & Design Patterns

### Multi-Package Structure

Each package is self-contained with its own `package.yaml` (processed by hpack into `.cabal`), dependencies, and tests. Packages can depend on one another but should maintain clear boundaries.

### Dependency Management

Dependencies are pinned to specific versions using `cabal.project`:
- **Hackage index state**: Pinned to a specific timestamp for reproducibility
- **CHaP (Cardano Haskell Packages)**: Additional repository for Cardano-specific packages
- **Source repository packages**: Rarely used; avoid unless necessary (prefer releasing to CHaP)

To update dependencies: Edit the `index-state` in `cabal.project` and run `cabal update`.

### Effect System

The codebase uses `simple-freer` (Freer monad) for effect composition. The `freer-extras` package provides common extensions and utilities for this.

### Testing with Generators

The emulator includes test data generators in `Cardano.Node.Emulator.Generators` for creating realistic test scenarios.

## Code Conventions

### Haskell Style

- **Column limit**: 100 characters
- **Indentation**: 2 spaces
- **Import style**: `ImportQualifiedPost` enabled (imports appear after the module name)
- **Module organization**: Explicit module exports required (`-Wmissing-import-lists`)
- **Type safety**: Enabled `ScopedTypeVariables`, `StandaloneDeriving`, and strict pattern matching checks

### Common Extensions in Use

Packages enable these by default (check individual `.cabal` files):
- `DeriveFoldable`, `DeriveFunctor`, `DeriveGeneric`: Automatic deriving
- `ExplicitForAll`: Explicit type parameters
- `FlexibleContexts`, `GeneralizedNewtypeDeriving`: More expressive type constraints
- `LambdaCase`: Pattern matching in lambda expressions
- `NamedFieldPuns`: Record field punning
- `ImportQualifiedPost`: Qualified imports after module name

### GHC Compiler Flags

All packages compile with:
```
-Wall -Widentities -Wincomplete-record-updates -Wunused-packages
-Wincomplete-uni-patterns -Wnoncanonical-monad-instances -Wredundant-constraints
-Wmissing-import-lists
```

Plugin flags for PlutusTx scripts:
```
-fplugin-opt PlutusTx.Plugin:target-version=1.1.0
-fplugin-opt PlutusTx.Plugin:defer-errors
```

### HLint Rules

See `.hlint.yaml` for exceptions. Notable patterns:
- `Move brackets to avoid $` is ignored (rarely an improvement)
- `Use <$>` is ignored (clarity is preferred)
- `Avoid lambda` is ignored (named parameters aid clarity)

### Code Structure

- **API boundaries**: Core functionality is in `Cardano.Node.Emulator.API` (public interface)
- **Internal modules**: Implementation details use `Internal.` prefix and are not part of the public API
- **Module hierarchy**: Follows the package structure; internal modules are namespaced separately

## Testing Practices

- Tests are located in a `test/` directory within each package
- Use Tasty for test framework (the standard in this codebase)
- Tests require `TMPDIR`/`TMP` environment variables to be set (GitHub Actions uses `runner.temp`)
- Some tests are stateful and sensitive to parallelism; run with `-j1`
- Test output is colored and displayed directly (see `test-show-details: direct` in `cabal.project`)

## Pre-commit Hooks

Configured in `flake.nix`:
- **Ormolu**: Formats Haskell files
- **Nixfmt**: Formats Nix files
- **Hpack**: Validates and can regenerate `.cabal` files from `package.yaml`

These run automatically on commit when inside `nix develop`. To bypass (not recommended): `git commit --no-verify`.

## CI/CD Pipeline

GitHub Actions runs:
1. Dependency resolution with dry-run build plan
2. Cached dependency build
3. Full build of all packages with tests enabled
4. Test execution with direct output
5. Artifact collection and upload

Cache is invalidated via `CABAL_CACHE_VERSION` in `.github/workflows/haskell.yml`.

## File Locations Reference

- **Main source**: `<package>/src/` (Haskell source files)
- **Package definitions**: `<package>/package.yaml` (processed to `.cabal`)
- **Tests**: `<package>/test/`
- **Cabal configuration**: `cabal.project` (workspace), `<package>/<package>.cabal` (per-package)
- **Nix configuration**: `flake.nix`, `nix/` directory
- **Linting**: `.hlint.yaml`
- **CI/CD**: `.github/workflows/`

## Common Tasks

### Adding a new Haskell package

1. Create the package directory: `mkdir my-package`
2. Add `package.yaml` and `src/` structure
3. Add the package to `cabal.project` under `packages:`
4. Run `cabal build my-package --enable-tests` to verify
5. CI will validate the Nix build

### Updating GHC version

1. Edit the GHC version in `nix/project.nix` (IOGX config)
2. Restart `nix develop` shell
3. Compile packages and fix any compilation errors
4. Update CI workflow if needed (currently 9.6.6)

### Managing dependencies

1. Edit the `package.yaml` for your package
2. If you need a newer version available only after the current `index-state`, bump the timestamp in `cabal.project`
3. Run `cabal update` and `cabal build` to verify the plan works
4. If breakage occurs, add upper/lower bounds to fix it

## Useful Resources

- **Cardano Engineering Handbook**: https://input-output-hk.github.io/cardano-engineering-handbook
- **Contributing Guide**: See CONTRIBUTING.md for detailed development instructions
- **Nix Setup**: Refer to https://github.com/input-output-hk/iogx/blob/main/doc/nix-setup-guide.md
- **Cardano Haskell Packages**: https://github.com/input-output-hk/cardano-haskell-packages
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ See [the readthedocs page](https://plutus-apps.readthedocs.io/en/latest/adr/inde
We use `ormolu` for Haskell code formatting, `nixfmt-classic` for nix files and `cabal-fmt` for cabal files.
They are run automatically as pre-commit hooks, but CI will run them again and expect that to be a no-op, so if you somehow don't apply them your PR will not go green.

To run `fourmolu` or `cabal-fmt` manually over your tree, type `pre-commit run fourmolu` or `pre-commit run cabal-fmt` respectively.
To run `ormolu` or `hpack` manually over your tree, type `pre-commit run ormolu` or `pre-commit run hpack` respectively.
They are provided by the `nix develop` environment.

### Compiler warnings
Expand Down
53 changes: 45 additions & 8 deletions cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ repository cardano-haskell-packages
d4a35cd3121aa00d18544bb0ac01c3e1691d618f462c46129271bccf39f7e8ee

index-state:
, hackage.haskell.org 2025-11-05T09:40:54Z
, cardano-haskell-packages 2025-12-16T19:03:49Z
, hackage.haskell.org 2026-09-29T09:27:39Z
, cardano-haskell-packages 2026-09-24T00:08:56Z

packages: plutus-ledger
plutus-script-utils
Expand All @@ -22,14 +22,51 @@ packages: plutus-ledger
freer-extras

constraints:
cardano-api == 10.18.1.0

, cardano-api == 11.7.0.0

-- quickcheck-state-machine-0.10.2 still caps `random <1.3`, but
-- ouroboros-consensus (pulled in transitively) now requires
-- `random ^>=1.3`. quickcheck-state-machine works fine with random
-- 1.3 in practice; its bounds are just stale, so relax them here
-- instead of waiting for an upstream bump.
allow-newer:
, quickcheck-state-machine:random
-- The cardano stack (cardano-api, cardano-ledger-*, ouroboros-consensus)
-- still caps `plutus-ledger-api <1.66`, but Plutus V4's on-chain ledger API
-- only ships from plutus-ledger-api 1.67+. The V1-V3 encodings that the
-- cardano stack actually consumes are unchanged across these releases (only
-- the new V4 API was added), so relax the upper bound instead of waiting for
-- an upstream cardano-api bump.
, *:plutus-ledger-api

-- We never, ever, want this.
write-ghc-environment-files: never

-- Always build tests and benchmarks.
tests: true
benchmarks: true
-- Only build tests and benchmarks for our own packages, not for
-- dependencies. Enabling tests/benchmarks globally forces cabal's
-- dependency solver to also satisfy the (often much stricter, and
-- sometimes conflicting) test-suite/benchmark dependency bounds of
-- every package in the plan, e.g. ouroboros-consensus's test-suites
-- pull in quickcheck-state-machine, which conflicts with the
-- `random` version required elsewhere.
tests: false
benchmarks: false

package plutus-ledger
tests: true
benchmarks: true
package plutus-script-utils
tests: true
benchmarks: true
package cardano-node-emulator
tests: true
benchmarks: true
package cardano-node-socket-emulator
tests: true
benchmarks: true
package freer-extras
tests: true
benchmarks: true

-- The only sensible test display option, since it allows us to have colourized
-- 'tasty' output.
Expand All @@ -41,7 +78,7 @@ test-show-details: direct
-- For dev work we don't care about performance so much, so this is okay.
package cardano-ledger-alonzo
optimization: False
package ouroboros-consensus-cardano
package ouroboros-consensus
optimization: False
package cardano-api
optimization: False
Expand Down
31 changes: 31 additions & 0 deletions cabal.project.hls
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-- Project file used only by HLS/hie-bios (see hie.yaml), not by regular
-- `cabal build`. It layers editor-only GHC options on top of the normal
-- cabal.project.
--
-- Why this exists: the PlutusTx plugin compiles quoted `$$(compile [|| .. ||])`
-- terms and needs the GHC Core unfoldings of referenced INLINABLE bindings.
-- A normal `cabal build` gets these via `-fobject-code` plus the interface
-- pragma flags set in each package. HLS, however, loads modules with the
-- bytecode interpreter, where those unfoldings are absent, so the plugin fails
-- and (because of `-fplugin-opt PlutusTx.Plugin:defer-errors`) surfaces the
-- failures as editor-only diagnostics.
--
-- The fix (GHC 9.6+) is to have GHC persist the simplified Core into the
-- interface files and let the bytecode session restore it, which is exactly
-- what `-fwrite-if-simplified-core` + `-fprefer-byte-code` do.
-- Scope the options to our own packages only. HLS compiles just our source;
-- dependencies are loaded from their prebuilt artifacts, so a `package *`
-- stanza here would needlessly force a rebuild of the whole dependency tree on
-- first HLS load.
import: cabal.project

package plutus-ledger
ghc-options: -fwrite-if-simplified-core -fprefer-byte-code
package plutus-script-utils
ghc-options: -fwrite-if-simplified-core -fprefer-byte-code
package cardano-node-emulator
ghc-options: -fwrite-if-simplified-core -fprefer-byte-code
package cardano-node-socket-emulator
ghc-options: -fwrite-if-simplified-core -fprefer-byte-code
package freer-extras
ghc-options: -fwrite-if-simplified-core -fprefer-byte-code
Loading
Loading