Skip to content

Fix ledgerutil compare panic on a missing data file - #5615

Open
hmdsefi wants to merge 7 commits into
hyperledger:mainfrom
hmdsefi:fix/ledgerutil-compare-missing-data-file
Open

hmdsefi wants to merge 7 commits into
hyperledger:mainfrom
hmdsefi:fix/ledgerutil-compare-missing-data-file

Conversation

@hmdsefi

@hmdsefi hmdsefi commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Type of change

  • Bug fix

Description

A peer snapshot only has private_state_hashes.data and private_state_hashes.metadata when the channel has private data hashes. ExportPubStateAndPvtStateHashes creates the writer for these files when it sees the first hashed record, so a channel without private data gets no such files. The same is true for the public state files when there is no public state.

privacyenabledstate.NewSnapshotReader returns a nil reader when the data file does not exist. The ledger's own import (worldStateSnapshotReader) checks for that. ledgerutil compare does not. findAndWriteDifferences calls Next() on both readers, so when one snapshot has private data hashes and the other has none, compare panics:

panic: runtime error: invalid memory address or nil pointer dereference
github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/privacyenabledstate.(*SnapshotReader).Next(...)
	core/ledger/kvledger/txmgmt/privacyenabledstate/snapshot.go:505
github.com/hyperledger/fabric/internal/ledgerutil/compare.findAndWriteDifferences(...)
	internal/ledgerutil/compare/compare.go:144

The hashes in _snapshot_signable_metadata.json differ in this case (one side has none), so compare does go on to read the files. A difference like this is the kind of divergence compare is meant to report.

This change reads records through a small helper that treats a nil reader as a reader without records. The records of the other snapshot are then reported as missing, in the same way as records that are missing from one side of a data file.

Additional details

  • The test helper createSnapshot always wrote the private state files, even with no private records, so the tests never had a snapshot without them. It now writes them only when there are private records, like a real snapshot. The existing cases still pass, because in all of them without private records the private hashes are equal and the private comparison is skipped.

Tests:

  • Two new cases in TestCompare: second-missing-pvt-data and first-missing-pvt-data. One snapshot has a private data hash record and the other has no private state files. Compare reports the record as missing on the other side.
  • With compare.go reverted to main, both cases panic as above. With the fix they pass.

Ran:

  • go test -race ./internal/ledgerutil/... ./cmd/ledgerutil/... passes.
  • golangci-lint reports no issues.

Related issues

The same panic was reported in #3878. That issue was closed without a code change, and the panic still happens on main.

A snapshot only has the private state hashes files when the channel
has private data hashes. NewSnapshotReader returns a nil reader when
the data file does not exist, but findAndWriteDifferences calls Next
on both readers without checking. When one snapshot has private data
hashes and the other has none, compare panics with a nil pointer
dereference instead of listing the records that are missing.

Treat a nil reader as a reader without records. The test helper now
writes the private state files only when there are private records,
as a real snapshot does.

Related to hyperledger#3878

Signed-off-by: Hamed Yousefi <hdyousefi@gmail.com>
@hmdsefi
hmdsefi requested a review from a team as a code owner October 13, 2026 13:22
Signed-off-by: Hamed Yousefi <hdyousefi@gmail.com>
Signed-off-by: Hamed Yousefi <hdyousefi@gmail.com>
Signed-off-by: Hamed Yousefi <hdyousefi@gmail.com>
Signed-off-by: Hamed Yousefi <hdyousefi@gmail.com>
Signed-off-by: Hamed Yousefi <hdyousefi@gmail.com>
Signed-off-by: Hamed Yousefi <hdyousefi@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant