Problem
Several data corruption bugs exist in the event mapping handlers (src/mappings/). These cause silent data loss or incorrect state in the indexed database.
1. Farm public IPs lost on creation
When a farm is created with multiple public IPs via FarmStored, all IPs are saved with the same database ID (item.event.id). Since ctx.store.save() is an upsert on primary key, each IP overwrites the previous one. Only the last IP survives. In practice, IPs are usually added individually via add_farm_ip (which emits FarmUpdated), so the data self-corrects for most farms. The bug affects farms that were created with multiple IPs in the initial FarmStored event and never received a subsequent update.
2. Node interfaces lost on creation
Same ID collision pattern in nodeStored — nodes created with multiple interfaces only retain the last one. nodeUpdated correctly appends the interface name to the ID, so the data self-corrects on the first update event. The bug affects the window between creation and first update.
3. Race conditions in farm updates
farmUpdated uses async/await inside .forEach() callbacks. Since .forEach() doesn't await the returned promises, all IP save/removal operations execute concurrently. This causes race conditions where reads return stale data and writes can overwrite each other.
4. Ghost PublicConfig records
In handlePublicConfigV105, when a node's public config is removed (chain emits NodePublicConfigStored with None), the function should clean up and return. Instead, if no existing record is found, it falls through and creates a new PublicConfig record with all null fields — a phantom record.
5. Undefined contract ID in grace period handlers
contractGracePeriodStarted and contractGracePeriodEnded parse the contract ID from versioned events but have no guard if the version is unrecognized. The contract ID stays undefined, causing ctx.store.get() to query with WHERE contractID = undefined — unpredictable behavior.
6. Inline PublicConfig never saved for spec v9 nodes
In the earliest chain spec (v9, pallet 0.1.0-b1), create_node accepted a public_config parameter. The NodeStored event carried this config. The indexer's nodeStored handler extracts it via getNodePublicConfig() but never calls ctx.store.save() on the result. Public configs set during spec v9 node creation are silently dropped. From spec 11 onward, create_node hardcodes public_config: None, so this only affects historical spec v9 data.
7. Cross-farm duplicate IP overwrites
When farmUpdated processes an IP that already exists on a different farm, the code logs an error saying "skipped" but then saves anyway — silently overwriting the original farm's IP gateway value. The chain does not validate IP uniqueness across farms (add_farm_ip only checks within the same farm), so this scenario can occur in production.
Impact
- Data loss is mostly transient for bugs 1 and 2 — subsequent update events correct the state for most entities. Only entities that were created with multiple IPs/interfaces and never updated remain affected.
- Bug 3 (race conditions) can cause intermittent data inconsistency that is hard to reproduce.
- Bug 4 creates phantom records that pollute query results.
- Bug 6 affects historical spec v9 data only.
- Bug 7 is an ongoing risk whenever two farms register the same IP.
Problem
Several data corruption bugs exist in the event mapping handlers (
src/mappings/). These cause silent data loss or incorrect state in the indexed database.1. Farm public IPs lost on creation
When a farm is created with multiple public IPs via
FarmStored, all IPs are saved with the same database ID (item.event.id). Sincectx.store.save()is an upsert on primary key, each IP overwrites the previous one. Only the last IP survives. In practice, IPs are usually added individually viaadd_farm_ip(which emitsFarmUpdated), so the data self-corrects for most farms. The bug affects farms that were created with multiple IPs in the initialFarmStoredevent and never received a subsequent update.2. Node interfaces lost on creation
Same ID collision pattern in
nodeStored— nodes created with multiple interfaces only retain the last one.nodeUpdatedcorrectly appends the interface name to the ID, so the data self-corrects on the first update event. The bug affects the window between creation and first update.3. Race conditions in farm updates
farmUpdatedusesasync/awaitinside.forEach()callbacks. Since.forEach()doesn't await the returned promises, all IP save/removal operations execute concurrently. This causes race conditions where reads return stale data and writes can overwrite each other.4. Ghost PublicConfig records
In
handlePublicConfigV105, when a node's public config is removed (chain emitsNodePublicConfigStoredwithNone), the function should clean up and return. Instead, if no existing record is found, it falls through and creates a newPublicConfigrecord with all null fields — a phantom record.5. Undefined contract ID in grace period handlers
contractGracePeriodStartedandcontractGracePeriodEndedparse the contract ID from versioned events but have no guard if the version is unrecognized. The contract ID staysundefined, causingctx.store.get()to query withWHERE contractID = undefined— unpredictable behavior.6. Inline PublicConfig never saved for spec v9 nodes
In the earliest chain spec (v9, pallet 0.1.0-b1),
create_nodeaccepted apublic_configparameter. TheNodeStoredevent carried this config. The indexer'snodeStoredhandler extracts it viagetNodePublicConfig()but never callsctx.store.save()on the result. Public configs set during spec v9 node creation are silently dropped. From spec 11 onward,create_nodehardcodespublic_config: None, so this only affects historical spec v9 data.7. Cross-farm duplicate IP overwrites
When
farmUpdatedprocesses an IP that already exists on a different farm, the code logs an error saying "skipped" but then saves anyway — silently overwriting the original farm's IP gateway value. The chain does not validate IP uniqueness across farms (add_farm_iponly checks within the same farm), so this scenario can occur in production.Impact