hyperledger / hyperledger/fabric-x
fxmigrate verify performs count-only checks — corrupted key/value/version data passes verification silently
- Dominant language
- Go
- Stars
- 64
- Forks
- 80
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 15
Description
## The problem
`fxmigrate verify` is the integrity gate between a Fabric snapshot and the genesis file used to bootstrap Fabric-X. Its whole purpose is to guarantee that no data was lost or corrupted during export.
It doesn't do that.
**Step 4 of `runVerify` (`cli/v1/verify.go:102–112`) reads all snapshot entries and all genesis entries, but only compares their counts:**
```go
// Step 4: compare filtered entry count from the snapshot.
snapshotEntries, err := snapshot.ExportState(snapshotDir) // reads full entries
snapshotCount := len(snapshotEntries) // only the count is used
if snapshotCount != actualCount {
return fmt.Errorf("entry count mismatch: ...")
}
fmt.Fprintf(out, " entry counts match OK\n")
// ← snapshotEntries.Keys, .Values, .Versions are never examined
```
Both `entries` (from genesis) and `snapshotEntries` (from snapshot) are fully loaded in memory. The actual key-value-version content of every entry is discarded after counting.
## What passes verification today that shouldn't
- A genesis file where every value has been zeroed out — count matches, verify passes
- A genesis file where the Fabric → Fabric-X version conversion (`(blockNum << 32) | txNum`) produced wrong values for every entry — count matches, verify passes
- A genesis file where keys were written in a different encoding or truncated — count matches, verify passes
- Any silent corruption introduced by a bug in `genesis.Write` or `genesis.ReadAll` — count matches, verify passes
## Why this matters
Issue #21 specifically calls for: *"A method must be created to verify that the state in Fabric-X matches the state from the Fabric snapshot."* Count equality is not state equality.
The migration is one-way and irreversible. If corrupted data passes `verify` and gets loaded into the Fabric-X committer, the ledger starts from wrong state with no recovery path. The whole point of the verify step is to be the last line of defence before that happens — but right now it only checks that the right *number* of entries exist, not that they are the *correct* entries.
## Proposed fix
After the existing count check, add a content-level comparison: sort both slices by `(namespace, key)` and verify that each entry's key, value, and version matches exactly. Both slices are already loaded in memory at that point, so there is no additional I/O cost.
```go
// Step 5: verify entry content matches key-for-key
sort entries and snapshotEntries by (namespace, key)
for i := range entries:
assert entries[i].Key == snapshotEntries[i].Key
assert entries[i].Value == snapshotEntries[i].Value
assert entries[i].Version == snapshotEntries[i].Version
```
This is the verification that `verify` was always supposed to do.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.