Viewed trigger.go:320-370
Searched for "AddCloser"
Viewed exportHandlerFactory.go:1-100
Viewed exportHandlerFactory.go:200-340
Viewed exportHandlerFactory.go:340-420
Viewed exportHandlerFactory.go:421-500
Viewed exportHandlerFactory.go:500-600
Searched for "ChanStopNodeProcess"
Searched for "chanStopNodeProcess"
Viewed nodeRunner.go:1525-1560
Searched for "CreateManagedCoreComponents"
Viewed nodeRunner.go:270-320
Viewed nodeRunner.go:150-270
Viewed nodeRunner.go:450-520
Viewed nodeRunner.go:521-600
Searched for "func waitForSignal"
Viewed nodeRunner.go:1028-1120
Viewed nodeRunner.go:1120-1200
Searched for "func closeAllComponents"
Viewed nodeRunner.go:1630-1720
Viewed node.go:1040-1100
Searched for "func (n *Node) Close"
Viewed node.go:1428-1500
Searched for "SetStartImport"
Viewed importStartHandler.go:75-115
Viewed importStartHandler.go:1-75
A thorough inspection of the execution flow in [`update/trigger/trigger.go`]
1. **It does NOT close the node** (it leaves the node half-alive in a zombie state).
2. **It CANNOT save everything** (it omits critical modern state and exports empty root tries).
3. **It causes severe structural and database damage** if triggered.
Here is the exact step-by-step reality of what happens in the code:
---
### 1. Does It Actually Close / Kill the Node? **No.**
When the trigger fires, it calls [`doTrigger()`]update/trigger/trigger.go#L321):
```go
func (t *trigger) doTrigger() {
t.callClose()
t.exportAll()
}
```
#### What `callClose()` actually does:
`callClose()` only closes the slice `t.closers`. Across the entire codebase, only **5 consensus objects** are ever registered as closers:
1. `cc.worker` (the block production/validation worker)
2. `cc.chronology` (consensus round clock)
3. `cc.bootstrapper`
4. `coreComponents.SyncTimer()`
5. `nodesShufflerOut`
#### What `callClose()` does **NOT** close:
* **The Databases**: LevelDB / Trias DB, state tries, block storage, and miniblock stores remain **completely open**.
* **The P2P Network**: The libp2p messenger, open network sockets, topics, and peer connections are **NOT closed**.
* **The API / WebServer**: The HTTP server keeps running and serving endpoints.
* **The Interceptors & Mempools**: Transactions and blocks continue to be received and pushed into pools.
* **Async Execution Manager & VM**: The WASM runtime and execution managers are **NOT closed**.
#### If Export Fails (Guaranteed on Mainnet):
Inside [`exportAll()`] update/trigger/trigger.go#L334-L346):
```go
err = exportHandler.ExportAll(epoch)
if err != nil {
log.Error("error while exporting data", "error", err)
return // <--- IT SIMPLY RETURNS!
}
```
If `ExportAll` errors out:
* It **never** signals `chanStopNodeProcess`.
* It **never** calls `node.Close()`.
* **The process never terminates.**
* The node is left running indefinitely as a **zombie**: its consensus worker is dead (it will never produce or sign blocks again), but all background networking and DB handles remain open.
#### Even If Export Succeeded:
Look at [`update/trigger/trigger.go:354`]update/trigger/trigger.go#L354):
```go
wait := time.Duration(t.closeAfterInMinutes) * time.Minute
time.Sleep(wait)
t.chanStopNodeProcess <- argument
```
In `cmd/node/config/config.toml`, `CloseAfterExportInMinutes` is set to **`10000`** (which is **almost 7 days**!). The node would sleep for a week with broken consensus before ever sending the stop signal to `nodeRunner`.
---
### 2. Why It CANNOT Save Everything
A proper node shutdown ([`closeAllComponents`](mx-chain-go-private/node/nodeRunner.go#L1631)) blocks pruning, waits for round persistence, flushes dirty memory caches, and flushes account tries to disk.
The hardfork trigger does none of that:
1. **Uncommitted Memory State is Lost**:
Because `managedStateComponents.Close()` is never called prior to export, dirty trie nodes and uncommitted accounts in memory are **never flushed to disk**.
2. **Supernova Root Hashes are Nil (`BUG-701`)**:
In [`update/sync/syncAccountsDBs.go:113-118`](update/sync/syncAccountsDBs.go#L113-L121), the exporter queries:
```go
meta.GetRootHash()
meta.GetValidatorStatsRootHash()
```
On modern `MetaBlockV3`, those methods return **`nil`** (roots exist only inside `ExecutionResults`). As a result, the exporter attempts to sync tries from `nil` roots, **exporting an empty state trie** (0 user accounts, 0 validator accounts).
3. **Dropped In-Flight Transactions (`BUG-712`)**:
Because Supernova decouples proposal from execution, referenced miniblocks in `HeaderV3` are marked `notPending`. The exporter does not drain `ExecutionResults`, causing all committed-but-unexecuted transactions at the boundary to be permanently dropped.
4. **Missing Modern Protocol State**:
The exporter does not serialize or export:
* Migrated data trie status (`is-data-trie-migrated`)
* Scheduled miniblocks & delayed execution queues
* Modern smart contract storage mappers and multi-ESDT balances
---
### 3. Structural & Database Damage to the Node
Triggering the hardfork inflicts serious damage on the node’s filesystem and DB layout:
1. **Destructive Folder Reset**:
In [`update/factory/exportHandlerFactory.go#L569), the export factory executes:
```go
err := os.RemoveAll(folder)
```
If the export folder overlaps with any active data or is misconfigured, it recursively deletes that directory from the filesystem while the node is still active.
2. **Concurrent Database Contention**:
While the node's main storage engines are still active and accepting P2P data, the export factory attaches brand new syncers and storers directly against the active `StorageService` and `DataPool`. This causes severe IO thrashing, cache invalidation, and race conditions on LevelDB.
3. **Corrupted Replay Without Rollback (`UF-062`)**:
In [`update/process/processPending.go:78-91`](update/process/processPending.go#L78-L91), pending item reconstruction does not implement rollback snapshots. If a transaction execution fails during the export replay, state mutations remain committed to the export DB, while the transaction hash is omitted from the miniblock, resulting in non-deterministic state divergence.
4. **Poisoned Restart Marker (`mustimport`)**:
If export gets far enough, it writes a `mustimport` file in the working directory ([`importStartHandler.go:85`](update/trigger/importStartHandler.go#L85)). If the operator attempts to restart the node normally, the node detects this file, enters import mode, and refuses normal startup.
---
### Summary Table
| Requirement for a Safe Hardfork | What the Code Actually Does | Verdict |
| :--- | :--- | :--- |
| **Clean Node Shutdown** | Calls `Close()` only on 5 consensus objects; leaves DBs, P2P, and API running. | ❌ **Leaves node in a zombie state** |
| **Flush Dirty State to Disk** | Never calls `managedStateComponents.Close()`; in-flight state is abandoned. | ❌ **Data loss** |
| **Export Modern Ledger State** | Calls `meta.GetRootHash()` which returns `nil` on `MetaBlockV3`. | ❌ **Exports empty tries (0 accounts)** |
| **Terminate Process** | Logs error and returns; if successful, sleeps for 10,000 minutes (7 days). | ❌ **Process never exits cleanly** |
| **Database Integrity** | Spawns secondary syncers that contend with live DBs; lacks execution rollback. | ❌ **Severe DB & state corruption** |
**the mechanism cannot cleanly shut down the node, cannot capture modern state, and would leave the node's DB and process in a crippled, corrupted state.**
and some people claim we have a kill switch.
the reality, we have forgotten a stupid code in the node level which is not working, even if it would, it would create insane damages at all levels.