conformance

package
v0.70.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2026 License: Apache-2.0 Imports: 41 Imported by: 0

README

Ledger Rules Conformance Tests

This package runs the Amaru ledger rules conformance vectors against Dingo's ledger implementation. The shared harness and embedded test data live in github.com/blinklabs-io/ouroboros-mock/conformance; this package provides DingoStateManager, an adapter that drives Dingo's real database.Database and ledger/governance packages -- the same production persistence code the node uses, not a hand-rolled second implementation -- so every vector's UTxO/certificate/governance writes and reads go through a real, configured metadata backend. The same adapter runs against a real, local SQLite backend (the default, no setup required -- see state_manager.go), a real PostgreSQL backend (see PostgreSQL backend), or a real MySQL backend (see MySQL backend).

What the vectors cover

The vectors exercise Conway era ledger rules:

  • UTxO validation — inputs, outputs, fees, collateral
  • Certificate processing — stake, pool, DRep, committee
  • Governance — proposals, voting, enactment
  • Script execution — native scripts, Plutus V1/V2/V3

Running the tests

Run the full suite:

go test ./internal/test/conformance/

Run with verbose vector-level output (useful when investigating a failure). This also prints the per-vector pass/fail statistics, which a separate …WithResults test used to produce from its own second replay of the corpus:

go test -v ./internal/test/conformance/ -run TestRulesConformanceVectors

Run a single vector by substring match (delegated by the harness):

go test -v ./internal/test/conformance/ -run TestRulesConformanceVectors -vector <name>

PostgreSQL backend

By default the tests use a real, local SQLite database (see How it works) and need no setup. A second, build-tag-gated variant runs the identical harness against a real PostgreSQL database, using the same dingo_extra_plugins build tag as database/plugin/metadata/postgres (the actual Postgres metadata store plugin) and the same POSTGRES_HOST/PORT/USER/PASSWORD/DATABASE/SSLMODE environment variables that plugin's tests and CI's go-test-linux job already use.

Bring up a local Postgres and run it:

docker compose -f internal/test/conformance/docker-compose.yml up -d

POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_USER=postgres \
POSTGRES_PASSWORD=postgres POSTGRES_DATABASE=dingo_test \
  go test -tags dingo_extra_plugins -v ./internal/test/conformance/... -run Postgres

Without a POSTGRES_PASSWORD or POSTGRES_DSN set, both Postgres tests skip (they never fail a plain go test ./...). CI's go-test-linux job already runs a postgres:16 service with those exact env vars, so the Postgres variant runs automatically as part of the existing tagged go test -tags dingo_extra_plugins ./... step. Note that .github/workflows/go-test.yml's go-test-linux-race job deliberately runs without those services, so under -race this suite covers the SQLite backend only; the Postgres and MySQL vectors are covered uninstrumented by go-test-linux.

Schema isolation. database/plugin/metadata/postgres's own tests connect to the same dingo_test database. Since go test ./... runs different packages as separate, concurrent processes, and a local run can overlap another go test invocation (or two CI shards) against the same server, sharing one fixed schema across every process would let those processes race on the same tables and truncate each other's in-progress state. NewDingoPostgresStateManager instead migrates into a schema unique to this one test binary process (conformance_<pid>_<timestamp>, computed once at package load and shared by every call within that process -- CREATE SCHEMA IF NOT EXISTS plus a connection-level search_path pinned to that schema via PostgresDSNWithSearchPath, baked into the DSN's connection startup parameters so it applies to every connection the pool opens, not just one).

Local blob directory. The metadata store is remote and persistent for this process's lifetime (the process schema is truncated, never dropped, between ordinary constructions -- see the next paragraph), so the local Badger blob store paired with it must persist for that same lifetime: database.New's commit-timestamp consistency check requires both stores in one Database to have last committed the same timestamp, and a fresh, empty local directory paired with an already-advanced remote schema fails that check. NewDingoPostgresStateManager creates one os.MkdirTemp directory the first time it's called in a process (via sync.Once) and reuses it for every later call in that same process, rather than a new temporary one per call; that directory is never cleared between vectors (stale blob entries are keyed by a truncated vector's own transaction hashes and are simply never looked up again -- harmless, see state_manager_postgres.go's doc comment). TestMain drops the process schema and removes this directory once, after every test in the process has finished -- see conformance_main_test.go.

Reset semantics. Between vectors, DingoStateManager.Reset() does not call the metadata store's own Resettable.Reset (database/plugin/metadata/postgres's resetDatabase): that callback drops tables outright, without recreating them, requiring a fresh migration run before the store is usable again -- and, more importantly, scans and drops tables across every non-system schema in the target database, not just the process-scoped conformance schema, so calling it here would also destroy database/plugin/metadata/postgres's own concurrently running tests' tables in the shared dingo_test database. Reset instead TRUNCATEs every table in that process-scoped schema in place, over a separate admin connection, discovering the table list from information_schema rather than hardcoding it (see state_manager_postgres.go's wipeMetadata). This keeps the already-open store's connection pool live throughout -- no close, no reopen, no re-migration -- which is what keeps the cost of a Reset (and so the whole vector suite, which resets once per vector) from being a real close/reopen/re-migrate network round trip every time.

TestRulesConformanceVectorsPostgres also compares its results against the SQLite baseline rather than asserting a hardcoded number, so the two backends should exercise the identical vector count with identical pass counts, and the comparison stays correct even as the embedded ouroboros-mock vector corpus grows or shrinks. Both backends' replays are memoized per process (see Corpus replay budget), so the comparison reuses the SQLite run the gate already performed instead of rebuilding a baseline.

MySQL backend

Same idea as the PostgreSQL backend above, using the same dingo_extra_plugins build tag as database/plugin/metadata/mysql and the same MYSQL_HOST/PORT environment variables that plugin's tests and CI's go-test-linux job already use.

Bring up a local MySQL and run it:

docker compose -f internal/test/conformance/docker-compose.yml up -d mysql

MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_ROOT_PASSWORD=mysql \
  go test -tags dingo_extra_plugins -v ./internal/test/conformance/... -run Mysql

Without a MYSQL_ROOT_PASSWORD or MYSQL_DSN set, both MySQL tests skip. CI's go-test-linux job already runs a mysql:8 service and sets MYSQL_ROOT_PASSWORD, so the MySQL variant runs automatically as part of the existing tagged go test -tags dingo_extra_plugins ./... step. As with Postgres above, go-test-linux-race runs without these services, so the MySQL variant is covered uninstrumented rather than under -race.

Database isolation. MySQL has no schema/database distinction the way Postgres does — a MySQL "schema" is a database. database/plugin/metadata/mysql's own tests connect to the shared dingo_test database with a user the official mysql image's bootstrap grants access to only that database, so this suite can't reuse that user to carve out an isolated namespace the way the Postgres one does with CREATE SCHEMA. Instead, NewDingoMysqlStateManager authenticates as root (the one account guaranteed to have CREATE DATABASE privileges) and migrates into a database unique to this one test binary process (dingo_conformance_<pid>_<timestamp>, computed once at package load and shared by every call within that process; the mysql metadata plugin's own openStore provisions it automatically -- CREATE DATABASE IF NOT EXISTS, via its ensureDatabaseExists step -- whenever the DSN it's given names a database). This is why the MySQL tests key off MYSQL_ROOT_PASSWORD specifically rather than the MYSQL_PASSWORD the plugin's own tests use.

Local blob directory and reset semantics follow the same reasoning as the Postgres backend above: NewDingoMysqlStateManager creates one os.MkdirTemp directory the first time it's called in a process (via sync.Once) and reuses it for every later call in that same process, paired with the process-scoped database, and Reset() TRUNCATEs every table in that database in place, over a separate admin connection (rather than calling Resettable.Reset, which drops tables individually without recreating them), keeping the already-open store's connection pool live throughout instead of paying for a close/reopen/re-migrate cycle on every vector. TestMain drops the process database and removes this directory once, after every test in the process has finished -- see conformance_main_test.go.

TestRulesConformanceVectorsMysql follows the same count-comparison approach as the Postgres variant, for the same reason, and likewise reuses the memoized SQLite baseline.

When to run them

Conformance tests are mandatory after every ledger-affecting change, not just once at the end of a branch. Specifically, run them after any edit under ledger/, database/plugin/metadata/, database/models/, or any dependency bump of gouroboros, plutigo, or ouroboros-mock. A regression here almost always indicates a correctness bug that CI on unit tests will miss.

Cross-repo change cascades that must re-run this suite:

Changed repo Must run conformance tests in
plutigo plutigo → gouroboros → dingo
gouroboros gouroboros → dingo
dingo dingo

How it works

  1. The test extracts embedded vectors from ouroboros-mock/conformance into a temp directory (ExtractEmbeddedTestdata).
  2. A fresh DingoStateManager composes a real database.Database (a real sqlite/postgres/mysql metadata store plus a local Badger blob store, through the same plugin.Resolve path the production node uses at startup) and runs the versioned SQL-store migrations.
  3. The harness (conformance.NewHarness) walks every vector, feeding transactions through the state manager -- which applies UTxOs, certificates, and governance state to the real backend via database.SetTransactionMetadataOnly and ledger/governance, not an in-memory mirror -- and comparing expected vs. actual ledger state after each step.
  4. Each backend replays the corpus once per process through RunAllVectorsWithResults, which returns structured per-vector results. assertCorpus turns those results back into named per-vector subtests and fails on any mismatch, so the single replay serves as both the pass/fail gate and the progress statistics. See Corpus replay budget.
  5. Between vectors, Reset() clears the real backend (not just in-memory bookkeeping) so each vector starts from a genuinely empty database -- see each backend's own "Reset semantics" above for how.

Dingo validation entry point coverage

A green corpus run is not evidence that Dingo's own transaction validation ran. The shared harness validates each vector with common.VerifyTransaction over conformance.ConformanceValidationRules, a list of upstream gouroboros rule functions. Nothing in that path reaches ledger/eras' EraDesc.ValidateTxFunc -- ValidateTxByron through ValidateTxDijkstra -- which is what the node runs against live transactions and which differs from the upstream list (Conway and Dijkstra substitute Dingo implementations for the committee-certificate, unknown-voter, Plutus, fee and PlutusV1/V2 feature rules; the pre-Alonzo eras replace the upstream fee and max-size rules outright).

Stubbing ValidateTxConway to return nil leaves TestRulesConformanceVectors reporting 315/315, 100%.

entry_points_test.go and entry_points_replay_test.go close that gap:

  • TestConformanceVectorsExerciseDingoEraEntryPoints replays the corpus a second time, routing every vector transaction through the production entry point for the era its protocol parameters select, and asserts per vector that the entry point resolved the inputs that transaction declares through live ledger state. The assertion is independent of the verdict, so it is not satisfied by a validator that returns the vector fixture's own result. Dingo's rule set is a superset of the corpus rule set (it keeps the fee and max-size rules the corpus excludes because the vectors carry Haskell-computed values), so agreement on verdicts is not asserted here.
  • TestDingoEraRegistryExposesValidationEntryPoints fails on a nil ValidateTxFunc or a protocol-major-version gap between adjacent eras.
  • TestDingoEraEntryPointsRejectInputlessTransaction covers every era in the registry, not just Conway. The corpus is Conway-only and validation rules are duplicated per era, so Conway coverage says nothing about ValidateTxShelley or ValidateTxDijkstra.
  • TestEntryPointExecutionFaultDetectsBypassedValidator and TestDingoEraEntryPointsReportsMissingValidator prove the detectors detect, by substituting a no-op validator, a fixture-only verdict, a reject-without-reading validator, and a nil registry entry, and requiring each to be reported.

Corpus replay budget

One full replay of the vector corpus is the single most expensive thing in this package -- 917s of Linux CI time with real Postgres and MySQL attached -- and the corpus exercises gouroboros ledger rules, which do not vary by storage backend. Replaying it more than once per backend therefore buys no additional rule coverage.

Each backend replays the corpus exactly once per go test process, and every consumer reads that one memoized result set: the pass/fail gate, the progress statistics, and the cross-backend comparison. The vector extraction is shared the same way, once rather than once per replay. See corpus_test.go.

The one deliberate exception is the SQLite-only entry-point replay described in Dingo validation entry point coverage. The shared harness offers no hook for a caller-supplied validator, so that coverage is not obtainable from the harness pass at any replay count. It is also memoized once per process, and it runs against SQLite only because entry points do not vary by storage backend. Its cost is the state replay rather than the validation: with the entry-point call removed the pass takes the same wall clock. Measured on one machine, it took the package from 23.8s to 46.1s uninstrumented, and from 585s to 891s under -race.

Before this, a Linux CI run with both DSNs configured replayed the corpus eight times:

Test sqlite postgres mysql
TestRulesConformanceVectors 1
TestRulesConformanceVectorsWithResults 1
TestRulesConformanceVectorsPostgres 1
…WithResultsPostgres 1 1
TestRulesConformanceVectorsMysql 1
…WithResultsMysql 1 1
total 4 2 2

Two redundancies stacked: each …WithResults test re-ran the corpus purely to count rather than assert, and each cross-backend comparison rebuilt its own SQLite baseline from scratch instead of reusing one.

Why any per-backend replay earns its cost

The per-dialect replays are not rule coverage -- they drive Dingo's storage layer through each dialect's semantics, and that has found real bugs. Both came from #3599, and neither is a ledger-rule bug:

  • loadPoolAssociations held a pool_registration cursor open while issuing nested per-row queries on the same connection. SQLite tolerates concurrently open cursors on one connection; MySQL and PostgreSQL do not, so a pool with at least one owner or relay corrupted the connection once returned to the pool, surfacing as a spurious "pool not found" on the next unrelated read.
  • go-sql-driver/mysql reports the number of rows an UPDATE actually changed, not the number its WHERE clause matched, unlike sqlite3 and lib/pq. A DRep voting again in the same epoch with an unchanged activity/expiry epoch matched a real row but changed no column, so MySQL reported affected == 0 -- indistinguishable from the row not existing.

Both were found by driving the storage layer through the corpus's variety of access patterns. That needs one pass per dialect, not several.

Files

File Purpose
conformance_test.go Go test entry point, SQLite backend (TestRulesConformanceVectors)
corpus_test.go Memoized per-backend corpus replay, shared assertion/reporting/comparison helpers, and the shared vector extraction
corpus_main_test.go TestMain for the untagged build — removes the shared vector extraction (!dingo_extra_plugins build tag)
conformance_postgres_test.go Go test entry points, PostgreSQL backend, including restart/rollback/invalid-DSN acceptance tests (dingo_extra_plugins build tag)
conformance_mysql_test.go Go test entry points, MySQL backend, including restart/rollback/invalid-DSN acceptance tests (dingo_extra_plugins build tag)
conformance_main_test.go TestMain — drops this process's Postgres schema and MySQL database and removes their paired blob directories once, after every test in the process has finished (dingo_extra_plugins build tag)
database.go openRealDatabase/closeRealDatabase — composes a real blob+metadata database.Database via plugin.Resolve, shared by all three backend constructors
state_manager.go DingoStateManager — implements conformance.StateManager against a real Dingo database.Database and ledger/governance, reusing production persistence code
state_manager_postgres.go NewDingoPostgresStateManager — same DingoStateManager, real Postgres connection with schema isolation (dingo_extra_plugins build tag)
state_manager_mysql.go NewDingoMysqlStateManager — same DingoStateManager, real MySQL connection with database isolation (dingo_extra_plugins build tag)
state_manager_backend_test.go Real-backend acceptance tests against the default SQLite manager: restart survival, transaction rollback, and an epoch-transition/stake-snapshot test driving ledger/governance.ProcessEpoch and ledger/snapshot.Manager end to end
entry_points_test.go Assertions that Dingo's production era validation entry points actually execute for the corpus, plus the per-era and detector-proving regression tests
entry_points_replay_test.go Entry-point replay machinery: era-registry read, observing ledger state, per-vector routing evidence, and the bypass detector predicate
state_provider.go State-query adapters used by the harness -- every read queries the real backend live (see its type doc comment for the one narrow, documented exception)
docker-compose.yml Local PostgreSQL and MySQL for the SQL-backed tests

Updating vectors

The vectors themselves are embedded in ouroboros-mock, not in this repo. To update the corpus, bump the ouroboros-mock dependency in go.mod and re-run the suite. Do not add or mutate vectors locally.

Debugging a failing vector

  1. Re-run the failing vector in isolation with -v so the harness prints per-step diagnostics.
  2. Check whether the failure is Dingo-side (ledger logic) or state-manager-side (state_manager.go mapping between common.* types and the SQL-store models). State-manager bugs usually surface as the same vector failing identically across multiple eras; ledger bugs are usually era-specific.
  3. If the upstream vector itself looks wrong, file an issue against blinklabs-io/ouroboros-mock rather than patching around it here.

Documentation

Overview

Package conformance provides a DingoStateManager that implements the ouroboros-mock conformance.StateManager interface using dingo's ledger state models.

Index

Constants

This section is empty.

Variables

View Source
var ErrNotFound = errors.New("conformance: not found")

ErrNotFound is returned when a requested item is not found

Functions

This section is empty.

Types

type DingoStateManager

type DingoStateManager struct {
	// contains filtered or unexported fields
}

DingoStateManager implements conformance.StateManager against a real Dingo database.Database (sqlite/postgres/mysql metadata store plus a local Badger blob store), composed the same way the production node composes its storage plugins at startup. UTxOs, certificates, and governance state are all read from and written to this real backend -- see ApplyTransaction, ProcessEpochBoundary, and state_provider.go -- reusing dingo's own production persistence code (database.SetTransactionMetadataOnly, ledger/governance) rather than hand-rolling a second implementation against the raw metadata.MetadataStore interface.

govState mirrors the subset of state (proposal votes/thresholds, pending-committee bookkeeping) the upstream conformance harness reads via GetGovernanceState to pre-validate the *next* event before this manager applies it; it is kept from drifting out of step with the real backend by being driven from the exact same certificate/proposal/vote processing calls that also write to the real database (see updateGovStateForCertificate, recordProposalsInGovState, recordVotesInGovState), and epoch-boundary ratification/enactment decisions are persisted back to the real database via governance.EnactProposal / Database.SetGovernanceProposal rather than mutating only this in-memory mirror.

func NewDingoStateManager

func NewDingoStateManager() (*DingoStateManager, error)

NewDingoStateManager creates a DingoStateManager backed by a real, local SQLite metadata store (plus a local Badger blob store), composed through the same plugin.Resolve path the production node uses at startup.

func (*DingoStateManager) ApplyTransaction

func (m *DingoStateManager) ApplyTransaction(
	tx common.Transaction,
	slot uint64,
) error

ApplyTransaction implements conformance.StateManager.ApplyTransaction.

func (*DingoStateManager) Close

func (m *DingoStateManager) Close() error

Close releases state-manager resources: the database, its provider host, and -- for a manager-owned data directory (the plain NewDingoStateManager constructor) -- the directory itself. It never drops a remote schema or database: NewDingoPostgresStateManager/NewDingoMysqlStateManager share one schema/database across every call in their process (see postgresProcessSchema's doc comment in state_manager_postgres.go and mysqlProcessDatabase's in state_manager_mysql.go), so an individual manager's Close must not drop a resource a sibling manager elsewhere in the same process may still be using -- that cleanup belongs to TestMain (conformance_main_test.go), once, after every test in the process has finished.

func (*DingoStateManager) GetGovernanceState

func (m *DingoStateManager) GetGovernanceState() *conformance.GovernanceState

GetGovernanceState implements conformance.StateManager.GetGovernanceState.

func (*DingoStateManager) GetProtocolParameters

func (m *DingoStateManager) GetProtocolParameters() common.ProtocolParameters

GetProtocolParameters implements conformance.StateManager.GetProtocolParameters.

func (*DingoStateManager) GetStateProvider

func (m *DingoStateManager) GetStateProvider() conformance.StateProvider

GetStateProvider implements conformance.StateManager.GetStateProvider.

func (*DingoStateManager) GetStateSnapshot added in v0.70.13

func (m *DingoStateManager) GetStateSnapshot() *conformance.StateSnapshot

GetStateSnapshot implements conformance.StateSnapshotProvider. The conformance harness compares only observable ledger state; the database remains authoritative for validation while this projection tracks the committed UTxO identities alongside the same writes.

func (*DingoStateManager) LoadInitialState

LoadInitialState implements conformance.StateManager.LoadInitialState.

func (*DingoStateManager) ProcessEpochBoundary

func (m *DingoStateManager) ProcessEpochBoundary(newEpoch uint64) error

ProcessEpochBoundary implements conformance.StateManager.ProcessEpochBoundary.

Pool retirement has no separate real-database write here: a pool's PoolRetirementCertificate is already persisted (pool + pool_retirement rows) at certificate-application time via SetTransactionMetadataOnly in ApplyTransaction, and DingoStateProvider.PoolCurrentState/IsPoolRegistered determine registered-vs-retired status by comparing that stored retirement epoch against the current epoch at read time -- so there is nothing further to persist at the boundary itself.

Ratification/enactment decisions are made by the same vector-validated heuristic the harness has always used (see ratifyProposals/enactProposal below), not by invoking the full governance.ProcessEpoch orchestration: ProcessEpoch's real ratification path performs stake-weighted DRep/SPO/committee tallying against the database's live stake distribution, which synthetic per-vector seed data isn't guaranteed to model with the fidelity that requires, and a mismatch there would show up as vector regressions, not as an isolated persistence gap. Enactment side effects that a ratified proposal must apply (committee membership, protocol parameters, constitution, treasury withdrawal) are instead persisted by calling the real governance.EnactProposal directly against the already-persisted governance_proposal row once this manager's own heuristic decides to enact it -- reusing dingo's production side-effect code without re-deriving its ratification math. governance.ProcessEpoch is exercised directly, end-to-end, by TestProcessEpochAgainstRealBackend in state_manager_backend_test.go.

func (*DingoStateManager) Reset

func (m *DingoStateManager) Reset() error

Reset implements conformance.StateManager.Reset. It clears the pre-validation govState mirror and empties the real backend so the next vector starts from a genuinely clean database, not just clean bookkeeping.

func (*DingoStateManager) SetRewardAccountBalances added in v0.70.2

func (m *DingoStateManager) SetRewardAccountBalances(
	balances map[mockledger.RewardAccountKey]uint64,
)

SetRewardAccountBalances implements conformance.RewardAccountBalanceSetter. It updates registered accounts by full credential identity without creating or removing registrations. See SetRewardBalances's doc comment for why this stays in the govState mirror.

func (*DingoStateManager) SetRewardBalances

func (m *DingoStateManager) SetRewardBalances(
	balances map[common.Blake2b224]uint64,
)

SetRewardBalances implements conformance.StateManager.SetRewardBalances.

Reward-account balances are injected by the harness itself (precomputed from the vector's final_state plus future withdrawals -- see ouroboros-mock/conformance's adjustRewardBalances), not derived from anything Dingo's ApplyTransaction commits from decoded block data the way UTxOs/certificates/governance rows are. There is also no real backend primitive for an absolute reward-balance set (only Database.Add*AccountRewardByCredential, which credits a delta and can't express a decrease). Real reward calculation (ledger/chainsync.go's applyStakeRewards) is explicitly out of scope for this harness. Balances therefore stay in the govState mirror, matching how the upstream harness already treats them as synthetic validation input rather than application state.

type DingoStateProvider

type DingoStateProvider struct {
	// contains filtered or unexported fields
}

DingoStateProvider implements conformance.StateProvider by wrapping DingoStateManager to satisfy all gouroboros state interfaces. Every read method below queries manager.db -- the real, configured backend -- live; none of them read from any in-memory mirror of UTxO/certificate/pool/ DRep/committee state (see state_manager.go's type doc comment for the one narrow, documented exception: reward-account balances, which are harness-injected synthetic validation input, not application state Dingo itself commits).

func NewDingoStateProvider

func NewDingoStateProvider(manager *DingoStateManager) *DingoStateProvider

NewDingoStateProvider creates a new DingoStateProvider.

func (*DingoStateProvider) CalculateRewards

CalculateRewards calculates rewards for the given epoch

func (*DingoStateProvider) CommitteeCredentialMember added in v0.70.5

func (p *DingoStateProvider) CommitteeCredentialMember(
	coldCredential common.Credential,
) (*common.CommitteeMember, error)

func (*DingoStateProvider) CommitteeHotCredentialMember added in v0.70.5

func (p *DingoStateProvider) CommitteeHotCredentialMember(
	hotCredential common.Credential,
) (*common.CommitteeMember, error)

func (*DingoStateProvider) CommitteeMember

func (p *DingoStateProvider) CommitteeMember(
	coldKey common.Blake2b224,
) (*common.CommitteeMember, error)

CommitteeMember looks up a constitutional committee member by credential hash. Enacted (real, committed) members -- including the vector's initial committee, loaded into the backend by LoadInitialState -- are read from the backend directly and never fall back to the govState mirror: govState.CommitteeMembers holds that same initial/enacted set (see LoadFromParsedState and enactProposal), so falling back to it here would let a backend that drops or cannot read a committee_member row still report the vector as passing. A member proposed by a pending (not yet enacted) UpdateCommittee action is the one case with no real committee_member row to read yet, so that case resolves the persisted proposal directly.

func (*DingoStateProvider) CommitteeMembers

func (p *DingoStateProvider) CommitteeMembers() ([]common.CommitteeMember, error)

CommitteeMembers returns every enacted committee member -- including the vector's initial committee, loaded into the backend by LoadInitialState -- read from the backend directly. It never merges in govState.CommitteeMembers: that map holds the same initial/enacted set (see LoadFromParsedState and enactProposal), so merging it here would let a backend that drops or cannot read a committee_member row still report the vector as passing. Unlike CommitteeMember, there is no per-credential caller asking about a specific pending UpdateCommittee proposal here, so there is no commit-free case left to fall back for.

func (*DingoStateProvider) CommitteeStateAvailable added in v0.70.5

func (p *DingoStateProvider) CommitteeStateAvailable() (bool, error)

CommitteeStateAvailable reports that the harness can answer committee queries authoritatively whenever its backend is reachable.

This deliberately differs from LedgerView.CommitteeStateAvailable, which derives authority from the seated member set. The two providers have different knowledge. A conformance vector declares its complete initial committee, and seedGovernanceState writes exactly that set, so zero rows here means the vector declared an empty committee -- authoritatively empty, which must still reject a non-member's certificate. Deriving availability from row count would instead report unavailable and decline to reject, failing any vector that expects NotCommitteeMemberError against an empty committee.

Production instead derives authority from the include-deleted member set, which distinguishes a committee emptied by NoConfidence (soft-deleted rows, authoritative) from one never populated (no rows, ambiguous because Dingo never persists the Conway genesis committee, blinklabs-io/dingo#3785). The harness needs no such inference: it has no genesis-sync path, so reachable already implies complete. Once #3785 lands the two answers converge.

func (*DingoStateProvider) Constitution

func (p *DingoStateProvider) Constitution() (*common.Constitution, error)

Constitution returns the enacted constitution -- anchor URL, anchor hash, and optional guardrails policy hash -- read from the real backend, in the same shape production's ledger.LedgerView.Constitution reports.

It never falls back to the govState mirror: that mirror is seeded from the same vector state LoadInitialState writes to the backend, so falling back to it would let a backend that drops or cannot read a constitution row still report the vector as passing. Missing or malformed constitution state fails closed through governance.ConstitutionFromModel, and a failed read is returned as the wrapped store error.

func (*DingoStateProvider) CostModels

CostModels returns which Plutus language versions have cost models defined. CostModel values are empty markers (struct{} upstream).

func (*DingoStateProvider) DRepDelegation added in v0.67.0

func (p *DingoStateProvider) DRepDelegation(
	cred common.Credential,
) (*common.Drep, error)

DRepDelegation returns the DRep a stake credential is vote-delegated to, or nil if it is not delegated. Used to validate reward withdrawals on protocol versions 10 and 11. Reads the account's real account.drep column through the real backend, matching production's ledger.LedgerView.DRepDelegation, rather than the govState pre-validation mirror: a real backend that never persists or returns account.drep correctly would still pass every vector here if this read the mirror instead, since ApplyTransaction's certificate processing writes delegation through the real SetTransactionMetadataOnly path regardless of what this read side consults.

func (*DingoStateProvider) DRepRegistration

func (p *DingoStateProvider) DRepRegistration(
	credential common.Credential,
) (*common.DRepRegistration, error)

DRepRegistration looks up a DRep registration by its full credential.

func (*DingoStateProvider) DRepRegistrations

func (p *DingoStateProvider) DRepRegistrations() ([]common.DRepRegistration, error)

DRepRegistrations returns all DRep registrations

func (*DingoStateProvider) GetAdaPots

func (p *DingoStateProvider) GetAdaPots() common.AdaPots

GetAdaPots returns the current ADA pots

func (*DingoStateProvider) GetRewardSnapshot

func (p *DingoStateProvider) GetRewardSnapshot(
	epoch uint64,
) (common.RewardSnapshot, error)

GetRewardSnapshot returns the stake snapshot for reward calculation

func (*DingoStateProvider) GovActionById

func (p *DingoStateProvider) GovActionById(
	id common.GovActionId,
) (*common.GovActionState, error)

GovActionById looks up a governance action by its ID against the real backend.

func (*DingoStateProvider) GovActionExists

func (p *DingoStateProvider) GovActionExists(id common.GovActionId) bool

GovActionExists checks if a governance action exists

func (*DingoStateProvider) IsPoolRegistered

func (p *DingoStateProvider) IsPoolRegistered(
	poolKeyHash common.PoolKeyHash,
) bool

IsPoolRegistered checks if a pool is currently active -- see poolIsActive.

func (*DingoStateProvider) IsRewardAccountRegistered

func (p *DingoStateProvider) IsRewardAccountRegistered(
	cred common.Credential,
) bool

IsRewardAccountRegistered checks if a reward account is registered

func (*DingoStateProvider) IsStakeCredentialRegistered

func (p *DingoStateProvider) IsStakeCredentialRegistered(
	cred common.Credential,
) bool

IsStakeCredentialRegistered checks if a stake credential is currently registered

func (*DingoStateProvider) IsVrfKeyInUse

func (p *DingoStateProvider) IsVrfKeyInUse(
	vrfKeyHash common.Blake2b256,
) (bool, common.PoolKeyHash, error)

IsVrfKeyInUse checks if a VRF key hash is registered by another pool. Conformance tests don't currently test VRF key uniqueness.

func (*DingoStateProvider) NetworkId

func (p *DingoStateProvider) NetworkId() uint

NetworkId returns the network identifier

func (*DingoStateProvider) PoolCurrentState

func (p *DingoStateProvider) PoolCurrentState(
	poolKeyHash common.PoolKeyHash,
) (*common.PoolRegistrationCertificate, *uint64, error)

PoolCurrentState returns the current state of a pool. A pool's PoolRetirementCertificate is already persisted (pool + pool_retirement rows) at certificate-application time via SetTransactionMetadataOnly in ApplyTransaction, so there is nothing further to read at epoch-boundary time -- see ProcessEpochBoundary's doc comment. The pending retirement epoch, when any, is derived by pendingPoolRetirementEpoch (matching ledger.LedgerView.PoolCurrentState); whether the pool is still considered actively registered is decided by poolIsActive -- see its doc comment.

func (*DingoStateProvider) RewardAccountBalance

func (p *DingoStateProvider) RewardAccountBalance(
	cred common.Credential,
) (*uint64, error)

RewardAccountBalance returns the current reward balance for a stake credential. Reward balances are harness-injected synthetic validation input (see DingoStateManager.SetRewardBalances's doc comment), so this reads the govState mirror rather than the real backend.

func (*DingoStateProvider) SlotToTime

func (p *DingoStateProvider) SlotToTime(slot uint64) (time.Time, error)

SlotToTime converts a slot number to a time

func (*DingoStateProvider) StakeCredentialDeposit added in v0.70.7

func (p *DingoStateProvider) StakeCredentialDeposit(
	cred common.Credential,
) (*uint64, error)

StakeCredentialDeposit returns the deposit recorded when the stake credential registered, or nil when the credential is not registered or the recorded deposit is unknown.

Without this method the harness does not satisfy common.StakeCredentialDepositState, so UtxoValidateValueNotConservedUtxo's optional type assertion misses and every legacy stake deregistration in the corpus is refunded at the current KeyDeposit. The corpus then cannot distinguish a correct recorded refund from the fallback, which is the gap #3831 covers.

This mirrors ledger.LedgerView.StakeCredentialDeposit: the account lookup gates on the same live registration state as IsStakeCredentialRegistered above, the registration history carries the deposit actually paid, and the import baseline stands in for a credential established by a vector's initial state rather than by a certificate in that vector. A nil return is preserved rather than coerced to zero, because the rule treats any non-nil value as authoritative.

func (*DingoStateProvider) StakeRegistration

func (p *DingoStateProvider) StakeRegistration(
	stakingKey []byte,
) ([]common.StakeRegistrationCertificate, error)

StakeRegistration looks up stake registrations by staking key

func (*DingoStateProvider) TimeToSlot

func (p *DingoStateProvider) TimeToSlot(t time.Time) (uint64, error)

TimeToSlot converts a time to a slot number

func (*DingoStateProvider) TreasuryValue

func (p *DingoStateProvider) TreasuryValue() (uint64, error)

TreasuryValue returns the treasury value from the real backend, in the same shape production's ledger.LedgerView.TreasuryValue reports.

It never answers a synthetic zero. The harness does not seed treasury/pot accounting (see DingoStateManager.persistEnactment), so an unseeded backend has no network-state row at all. Returning 0 for that would make a provider that cannot answer look healthy: the upstream current-treasury-value rule only queries this method once a transaction body actually carries key 21, and it compares for equality, so a synthetic zero silently rejects every vector that declares a non-zero value and silently accepts one declaring zero. Failing closed reports the missing harness state instead.

func (*DingoStateProvider) UpdateAdaPots

func (p *DingoStateProvider) UpdateAdaPots(pots common.AdaPots) error

UpdateAdaPots updates the ADA pots

func (*DingoStateProvider) UtxoById

UtxoById looks up a UTxO by transaction input, reading through the real backend (metadata row plus blob-stored output CBOR -- see DingoStateManager.createUtxo).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL