Scope block reorg detection to the current chain
Blocks are now stamped with the Bitcoin network name ("main", "test",
"signet") at ingest time. The reconcile loop's Pass 2 skips any stored
block whose chain differs from the node's current chain, preventing
testnet blocks from being falsely orphaned after switching back to
mainnet. Legacy rows with an empty chain field fall through unchanged.
The UI shows a blue "test" / "signet" badge next to blocks from a
non-current network so operators can distinguish cross-chain history
from genuine reorg-orphaned blocks.
This commit is contained in:
@@ -28,6 +28,7 @@ type Block struct {
|
||||
Source string
|
||||
ShareDiff float64
|
||||
OrphanedAt time.Time // zero value = not orphaned
|
||||
Chain string // "main", "test", "signet", or "" for legacy rows
|
||||
}
|
||||
|
||||
const schema = `
|
||||
@@ -38,7 +39,8 @@ CREATE TABLE IF NOT EXISTS blocks (
|
||||
found_at INTEGER NOT NULL,
|
||||
source TEXT NOT NULL DEFAULT '',
|
||||
share_diff REAL NOT NULL DEFAULT 0,
|
||||
orphaned_at INTEGER NOT NULL DEFAULT 0
|
||||
orphaned_at INTEGER NOT NULL DEFAULT 0,
|
||||
chain TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS blocks_found_at_idx ON blocks(found_at);
|
||||
|
||||
@@ -83,6 +85,11 @@ func (s *BlockStore) migrate() error {
|
||||
return fmt.Errorf("store: add orphaned_at: %w", err)
|
||||
}
|
||||
}
|
||||
if !have["chain"] {
|
||||
if _, err := s.db.Exec(`ALTER TABLE blocks ADD COLUMN chain TEXT NOT NULL DEFAULT ''`); err != nil {
|
||||
return fmt.Errorf("store: add chain: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -121,9 +128,9 @@ func (s *BlockStore) Close() error {
|
||||
// unnoticed.
|
||||
func (s *BlockStore) InsertBlock(b Block) (bool, error) {
|
||||
res, err := s.db.Exec(
|
||||
`INSERT OR IGNORE INTO blocks(height, hash, reward_btc, found_at, source, share_diff)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
b.Height, b.Hash, b.RewardBT, b.FoundAt.Unix(), b.Source, b.ShareDiff,
|
||||
`INSERT OR IGNORE INTO blocks(height, hash, reward_btc, found_at, source, share_diff, chain)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||
b.Height, b.Hash, b.RewardBT, b.FoundAt.Unix(), b.Source, b.ShareDiff, b.Chain,
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -201,7 +208,7 @@ func (s *BlockStore) Recent(limit int) ([]Block, error) {
|
||||
limit = 256
|
||||
}
|
||||
rows, err := s.db.Query(
|
||||
`SELECT height, hash, reward_btc, found_at, source, share_diff, orphaned_at
|
||||
`SELECT height, hash, reward_btc, found_at, source, share_diff, orphaned_at, chain
|
||||
FROM blocks ORDER BY height DESC LIMIT ?`,
|
||||
limit,
|
||||
)
|
||||
@@ -213,7 +220,7 @@ func (s *BlockStore) Recent(limit int) ([]Block, error) {
|
||||
for rows.Next() {
|
||||
var b Block
|
||||
var foundUnix, orphanedUnix int64
|
||||
if err := rows.Scan(&b.Height, &b.Hash, &b.RewardBT, &foundUnix, &b.Source, &b.ShareDiff, &orphanedUnix); err != nil {
|
||||
if err := rows.Scan(&b.Height, &b.Hash, &b.RewardBT, &foundUnix, &b.Source, &b.ShareDiff, &orphanedUnix, &b.Chain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.FoundAt = time.Unix(foundUnix, 0).UTC()
|
||||
@@ -234,7 +241,7 @@ func (s *BlockStore) Recent(limit int) ([]Block, error) {
|
||||
// retrying will keep failing.
|
||||
func (s *BlockStore) BlocksNeedingEnrichment(since time.Time) ([]Block, error) {
|
||||
rows, err := s.db.Query(
|
||||
`SELECT height, hash, reward_btc, found_at, source, share_diff, orphaned_at
|
||||
`SELECT height, hash, reward_btc, found_at, source, share_diff, orphaned_at, chain
|
||||
FROM blocks
|
||||
WHERE found_at >= ? AND (hash = '' OR reward_btc = 0)
|
||||
ORDER BY height ASC`,
|
||||
@@ -248,7 +255,7 @@ func (s *BlockStore) BlocksNeedingEnrichment(since time.Time) ([]Block, error) {
|
||||
for rows.Next() {
|
||||
var b Block
|
||||
var foundUnix, orphanedUnix int64
|
||||
if err := rows.Scan(&b.Height, &b.Hash, &b.RewardBT, &foundUnix, &b.Source, &b.ShareDiff, &orphanedUnix); err != nil {
|
||||
if err := rows.Scan(&b.Height, &b.Hash, &b.RewardBT, &foundUnix, &b.Source, &b.ShareDiff, &orphanedUnix, &b.Chain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.FoundAt = time.Unix(foundUnix, 0).UTC()
|
||||
|
||||
Reference in New Issue
Block a user