Skip to content

Release/1.2.x#71

Open
i-naman wants to merge 13 commits into
mainfrom
release/1.2.x
Open

Release/1.2.x#71
i-naman wants to merge 13 commits into
mainfrom
release/1.2.x

Conversation

@i-naman

@i-naman i-naman commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

What

Why

How

Testing

Checklist

  • make build passes
  • make lint passes
  • make fmt-check passes
  • Commit messages follow conventional commits
  • No secrets or credentials in the diff

i-naman and others added 12 commits June 30, 2026 00:09
github.repository_owner returns JupiterMetaLabs (mixed case);
Docker registry requires all-lowercase repository names.
arm/v7 (Raspberry Pi 2/3, 32-bit ARM) is not a supported exchange node
platform. Removing it to unblock the multi-arch manifest push for
amd64 + arm64.
* fix(rpc): use account nonce for eth_getTransactionCount

* fix: CI fmt and lint
* fix(service): update transaction parsing and fee history logic

- Replaced RLP decoding with UnmarshalBinary for transaction parsing to support all transaction types (legacy, EIP-2930, EIP-1559).
- Simplified fee history calculation by using a constant base fee instead of fetching from blocks, improving performance and reliability.
- Enhanced error handling and logging for better traceability during transaction processing and fee history retrieval.

* feat(service): enhance service interface and transaction handling

- Added GetChainIDValue method to the Service interface for retrieving the chain ID value directly.
- Implemented GetChainIDValue in ServiceImpl to return the chain ID as a big.Int.
- Updated transaction logging to handle nil values for To, Value, ChainID, GasPrice, MaxFee, MaxPriorityFee, and signature fields, improving robustness and clarity in transaction data.
- Enhanced marshalBlock and marshalTx functions to include chain ID and effective gas price calculations, ensuring accurate transaction representation in RPC responses.
- Improved error handling and logging for better traceability during transaction processing.

* fix(security): update signature verification logic for typed transactions

- Enhanced the CheckSignature function to support EIP-2930 and EIP-1559 typed transactions by using LatestSignerForChainID for recovery.
- Adjusted handling of legacy transactions (V=27/28) to ensure compatibility with MetaMask's signing methods.
- Improved logging for better traceability during signature verification processes, including success and failure scenarios.
- Refactored fallback logic to ensure robust handling of different transaction types and chain IDs.

* refactor(security): streamline transaction handling and remove unused binary encoding

- Removed unnecessary binary encoding logic for chain ID conversion in SetExpectedChainIDBig function.
- Updated transaction construction in checkTransactionHash to utilize tx.Type directly, enhancing clarity and maintainability.
- Simplified case handling for transaction types (EIP-1559, EIP-2930, Legacy) to improve readability and reduce complexity.

* refactor(gRPC): simplify transaction type handling in convertToPbTransaction

- Updated the convertToPbTransaction function to directly set the transaction type from config.Transaction.Type, improving clarity.
- Streamlined logic for legacy transactions to use GasPrice as MaxFee when MaxFee is unset, enhancing maintainability and readability.

* refactor(security): enhance gas cost calculation for transactions

- Updated the CheckBalanceWithCache function to use effective gas pricing for transaction cost calculations, accommodating both legacy and EIP-1559 transaction types.
- Introduced logic to determine the effective gas price based on GasPrice and MaxFee fields, improving accuracy in balance checks and transaction processing.

* refactor(security): standardize variable formatting in Security.go

- Adjusted the formatting of cached signers in Security.go for improved readability and consistency.
- Ensured alignment of variable declarations to enhance code clarity.

* fix(security): contract deploy support + race guard on signer globals

- CheckAddressExistWithCache: remove nil-To rejection; skip receiver
  check only for contract deployments (tx.To == nil)
- allChecksWithConn: guard tx.To.Hex() span attr against nil (panic)
- Add signerMu RWMutex around expectedChainID + cached signers
- chainIDStr: default to 'legacy/none' instead of empty string
- TODO: nonce gap acceptance (>= vs == expectedNonce)

* style: fix whitespace alignment for signerMu declaration

---------

Co-authored-by: Naman Agrawal <[email protected]>
* Refactor gas fee calculations and centralize logic in config/gasfee.go. Update effective gas price formulas in deltas.go and related files to use the new centralized constants. Ensure consistent fallback values across the codebase.

* Implement timestamp normalization for account updates in BatchRestoreAccounts and introduce UpdatedAt field in accountUpdateWire. This ensures consistent handling of timestamps across different units (seconds, milliseconds, microseconds, and nanoseconds) during account synchronization. Enhance BatchUpdateAccounts to correctly set UpdatedAt at enqueue time, preventing timestamp forgery from stale entries. Update related logic to maintain data integrity during account merges.

* fix(accounts): unify gas-fee formula and harden account-sync drain worker

Balance-corruption fixes (see RCA_account_sync.md):
- new config/gasfee.go shared by live execution (Processing.go) and
  reconciliation (FastsyncV2/deltas.go) — recon previously priced txs
  differently (no baseFee clamp, wrong fallbacks, zero fee on
  GasLimit==0) and re-corrupted balances on every catchup
- drain worker: LWW timestamp now set by producer (replayed stale
  entries no longer overwrite newer data); updates merge into stored
  account instead of clobbering DID/type/metadata; no bogus did:0x
  refs; eager PEL drain at boot
- facade/RPC: 35-gwei literals -> config.BaseFeeWei (from 8949d06)

Tests: golden old-vs-new gas formula equivalence + drain worker merge/
timestamp/retry behaviour (10 passing)

* fix(accounts): single merge site for update drains + hardened prefetch

Post-cherry-pick reconciliation of the two F2 branches (shape (b) per plan):

- worker: drop buildUpdateEntries point-read merge; updates convert to SPARSE
  objects (updateWiresToEntries, pure) — identity fields zero-valued, producer
  UpdatedAt carried. BatchRestoreAccounts owns the merge: one GetAll prefetch
  per chunk instead of N point reads per drain, and the same protections cover
  every write path (updates, accounts payloads).
- BatchRestoreAccounts: GetAll prefetch failure now fails the batch (PEL retry)
  instead of degrading to 'all accounts are new', which bypassed LWW + identity
  merge and let sparse objects clobber real accounts. Safe: immudb GetAll skips
  missing keys per-key; only real RPC/DB errors surface.
- BatchRestoreAccounts: new-account defaults (AccountType=user,
  CreatedAt=UpdatedAt) for sparse entries with no stored object to merge from.
- tests adapted: parse/timestamp tests kept; merge tests now pin the sparse
  contract (identity fields must be zero-valued, no did: refs from updates).

* refactor(accounts): extract pure mergeAccountForWrite + unit tests

Review follow-up: shape (b) moved LWW/identity-merge/monotonic-guard/new-default
logic into BatchRestoreAccounts, where it needed a live immudb connection to
test. Extract the per-account decision into pure mergeAccountForWrite(existing,
incoming) (merged, write) — no I/O, no logging — and port the dropped merge
tests against it, plus new coverage for unit-safe LWW and monotonic guards.

Behaviour-preserving: BatchRestoreAccounts calls the extracted function; the
LWW debug log and marshal stay at the call site.

* fix(recon): honest applied-anchor in accountsdb with marker-based
double-apply protection (F3)

Replaces the SQLite recon watermark with an accounts-applied anchor that
only ever states what is PROVEN applied. Invariants (RCA_account_sync.md
6b-6d): I1 never skip unapplied effects, I2 never apply twice across the
live executor and reconciliation, I3 anchor co-located with the balances
it describes.

DB_OPs/sync_anchor.go (new):
- anchor key sync:accounts_last_applied_block in accountsdb; restores of
  accountsdb roll anchor + balances back together (I3)
- pure rules: NextLiveAnchor (contiguous-only — a block after a downtime
  gap never advances past missing blocks), NextReconAnchor (monotonic),
  ShouldAdvanceReconAnchor (error + failedAccounts + verification, all
  three), CapAnchorTarget (MaxUint64 poison guard for both advances and
  the legacy-SQLite migration seed)
- single locked read-decide-write cycle (mutex adopted from fix/f3 after
  adversarial comparison: an unlocked interleaving regresses the anchor
  and re-opens recon-applied ranges that carry no markers)
- FilterProcessedTxMarkers: dual-DB tx_processed lookup (current markers
  in defaultdb + historical ~Dec-2025 cluster in accountsdb, verified
  empirically on jmdn-mainnet-6); fail closed
- TransactionOnMainDB: explicit DB selection for marker commits — they
  previously landed in whichever DB the session last selected (H0)

FastsyncV2:
- effectiveReconRange derives from the anchor; legacy SQLite watermark
  read once as a capped migration seed, never written again
- markReconComplete: monotonic advance capped at local tip — the old
  code persisted math.MaxUint64 from the legacy-peer HandleSync path,
  permanently disabling reconciliation
- catchup advances the anchor only at phase-8 PASS with a clean phase 5;
  HandleSync/PoTS verify via buildDataMissingTag before advancing (H2)
- computeAccountDeltas: returns error (fail closed) and EXCLUDES txs
  already applied by the live path via markers — closes the downtime-gap
  double-apply (H1) that anchor contiguity alone cannot

Processing.go:
- UpdatedAt stamped in nanos at source (blockTimestamp * time.Second);
  normalizeUpdatedAtNanos remains the compare-time safety net
- marker commit via TransactionOnMainDB (explicit defaultdb)
- applied anchor advanced contiguously after the atomic marker commit
  (and on the already-processed dedup path); failures log-only — a
  lagging anchor is the safe direction

Tests: anchor rules (contiguity/monotonic/gating/poison-cap) and delta
marker-exclusion (skip set, empty set, fully-live-applied block).

Residuals documented for F4/F5: balances-before-markers crash window,
recon read-add-write vs concurrent live write, latest_block hygiene (F6),
historical repair job.

* fix(recon): tombstone legacy SQLite watermark after anchor seed

Closes the seed re-poison residual: an accountsdb restore to a pre-F3
snapshot (anchor key absent) would re-run the one-time migration and
re-import the stale SQLite value. After a successful seed the SQLite key
is overwritten with an unparseable sentinel (original value preserved for
forensics) so legacySQLiteRecon returns 0 and the migration can never
fire twice.

* fix(blockproc): per-tx atomic apply — balances + marker in one ExecAll (F4 D-B)

Gate: RCA_account_sync.md §6e (D-B + amendments A1/A3, C2).

Pre-F4, one transaction was ≤6 independent commits across two databases:
sender/recipient/coinbase/zkvm each a separate accountsdb SafeCreate
(deductFromSender/addToRecipient → UpdateAccount), then the tx_processed
marker via Create — which lands in defaultdb, and whose failure was
tolerated ('still continue'). A crash anywhere in that sequence left
partially-applied balances with NO marker; replay re-applied the applied
prefix (double-count). This was the primary residual after F3.

Changes:
- txStage (new): per-tx in-memory staging with READ-THROUGH get — later
  steps of the same tx observe earlier mutations (self-transfer,
  sender==coinbase) exactly as under sequential commits. First-touch
  ordering; same address staged once.
- deductFromSender/addToRecipient: validate + stage; no DB writes.
- DB_OPs.ApplyTxAtomic (new): all staged docs + tx_processed marker in ONE
  accountsdb ExecAll. len(docs)+1 entries — the 1024 ExecAll cap (C2) is
  unreachable per-tx by construction. Commit failure now fails the tx
  (nothing landed, so 'continue' would be wrong).
- A1 (blocking amendment): per-tx markers are durable before a later tx
  can fail the block. rollback path revokes the prefix's markers (-1,
  value-aware) BEFORE restoring balances; revocation failure ABORTS the
  rollback (applied+marked is consistent; restored-under-live-markers is
  permanent skip). Crash between revoke and restore fails toward bounded
  double-apply — the repairable direction.
- Block-end marker batch REMOVED: it wrote 2×txs+1 entries in one ExecAll,
  exceeding immudb DefaultMaxTxEntries (1024, embedded/store/options.go:37)
  on any block >511 txs → commit failed → rollback → permanent block
  failure (C2 latent chain-halt on the merged base). Block marker is now a
  single-entry accountsdb write, non-fatal on failure (per-tx markers carry
  the exactly-once guarantee).
- Guards: block-level dual-DB value-aware prefilter (FilterProcessedTxMarkers,
  one GetAll pair per block, fail closed) replaces per-tx Exists reads that
  saw only defaultdb (H0) and flipped the session DB twice per tx; in-tx
  re-check under the tx lock via IsMarkerApplied (defense vs concurrent
  PoTS/pubsub application).
- Marker layer (A3): value-aware (-1 = revoked), accountsdb-authoritative
  precedence — a revocation must never be overridden by a stale legacy
  defaultdb marker; defaultdb consulted only for keys absent in accountsdb.
- tx_processing advisory locks intentionally stay in defaultdb — pulling
  their cleanup into the accountsdb ExecAll would create a new split-brain
  population (the H0 lesson).

Tests: value-aware marker decision + key/encoding freeze; stage
read-through/accumulation/first-touch order.

Known residual for the adversarial pass: rollbackState balance restore is
still per-account UpdateAccount commits (partial restore possible on
crash — fails toward re-apply, never skip, due to revoke-first ordering).

* fix(recon): recon-applied txs get markers via the drain, markers-last (F4 3a/A2)

Gate: RCA_account_sync.md §6e amendment A2.

Reconciliation's balance effects commit asynchronously through the Redis
drain (BatchRestoreAccounts). Without markers for recon-applied txs, a
recon re-run — e.g. after a failed anchor advance — recomputes deltas for
the same range and re-applies them (double-count). Marking synchronously
in ReconcileWithDeltas is wrong in BOTH orders: before drain-apply risks
marking never-applied txs (permanent skip); after is meaningless because
enqueue != applied. The only correct commit point is the drain itself.

Changes:
- computeAccountDeltas additionally returns the tx hashes whose deltas
  were INCLUDED (not marker-excluded).
- After a CLEAN ReconcileWithDeltas (err==nil, zero failed) all three
  recon sites (phase 5, PoTS, catchup) enqueue payloadTypeTxMarkers via
  NodeInfo.EnqueueAppliedTxMarkers — strictly AFTER the balance enqueue,
  so stream FIFO delivers markers no earlier than balances. Independent
  of anchor verification: the deltas are on the queue and will be applied
  either way.
- Drain (processBatch): marker entries commit strictly AFTER every
  account chunk of the batch succeeded (A2 markers-last — markers-first
  plus a later chunk failure would let a recon rerun exclude never-applied
  txs). Marker write failure leaves the batch unACKed → PEL retry;
  marker writes are idempotent.
- DB_OPs.WriteTxProcessedMarkers: chunked accountsdb ExecAlls (500/chunk,
  under the 1024 cap per A2's sizing note — account chunks and marker
  chunks are separate ExecAlls, never combined).
- Wire validation: applied_at must be > 0 — a -1 riding this wire would
  revoke a legitimate live marker at drain time; rejected as poison.
- Redis-unavailable fallback writes markers directly; safe because in
  that mode BatchUpdateAccounts also wrote balances synchronously first.

Ordering residuals for the adversarial pass (documented):
- XAUTOCLAIM reclaim can theoretically deliver a markers message in an
  earlier processBatch than its (still-pending) accounts message across
  PEL pages — markers would precede balances; consequence bounded to
  skip-on-poison of self-encoded payloads, which cannot poison.
- Accounts message poisoned while its markers land → permanent skip;
  self-encoded payloads make this unreachable in practice.

* docs+chore: F4 review follow-ups (§6f)

- docs/RCA_account_sync.md: commit the working RCA into the tree — commit
  messages across F1-F4 cite its sections; reviewers need it versioned.
- DOCKER.md: Redis AOF persistence promoted from tuning to CORRECTNESS
  requirement (§6f PROBE D): the recon anchor advances on verified+enqueued,
  not drained — queue loss = silent permanent skip. Bare-metal redis.conf
  must be checked explicitly.
- markerValueApplied: §6f nit — comment now links the unparseable-value
  branch to the historical repair job.

* fix(recon): anchor advance gated on drain confirmation (F5, PROBE D)

Gate: RCA_account_sync.md §6f PROBE D remedy.

The recon anchor could be AHEAD of the database by design: markReconComplete
ran after data verification, but reconciliation's balance effects (and F4's
tx markers) were still on the Redis queue — enqueue != applied (G5). Redis
queue loss (crash without AOF, eviction, flush) stranded an anchor claiming
ranges whose effects never landed: silent permanent skip (I1). Pre-existing
since F3; F4's marker enqueue made the queue dependency load-bearing.

Mechanism — high-water-mark confirmation (account_sync_drainwait.go):
- producers record the max XADD stream ID (noteEnqueuedID in
  enqueueRecordsChunked — the ID was already returned, just discarded)
- the drain worker records the max stream ID it applied AND ACKed
  (noteDrainedIDs — after Ack success only; an unACKed entry can be
  reclaimed and must not count as drained)
- markReconComplete captures LastAccountEnqueueID() after all of the recon's
  enqueues (balances then markers — every call site runs after both) and
  waits until drained >= target (90s budget, ~6 ImmuDB commit cycles)

HWM, not queue-depth==0: the stream is shared with concurrent sync traffic;
depth==0 waits on unrelated producers and may never come. Stream IDs are
totally ordered and the drain processes in order, so drained >= target
confirms exactly OUR entries.

Fail direction — every failure reports NOT-confirmed and skips the advance
(anchor lags, recon re-covers, markers prevent double-apply): wait timeout,
worker restart (in-memory HWM lost BY DESIGN), queue offline, malformed
stream IDs (compare as not-gte). Empty target = direct-write fallback path,
where balances were written synchronously — trivially confirmed. The live
path's contiguous advance is untouched: F4 made live writes synchronous
ExecAlls; no queue is involved.

Scope note: this gates only the anchor (the skip direction). Marker-message
loss was already skip-safe — next recon re-applies, bounded double-count —
so the marker enqueue is deliberately not blocking.

Accepted residual (documented in module header): poison entries do not
advance the HWM themselves but a later good entry can carry it past them;
all payloads are self-encoded so a poison recon entry is unreachable in
practice.

Tests: stream-ID parse/order (malformed = not-gte), HWM monotonicity
(out-of-order notes never regress), pure confirmation decision (empty
target / behind / exact / restart), wait timeout + confirm paths.

For the adversarial pass: PROBE-D Redis-loss integration scenario remains
on the harness list (§6f test debt); this change makes it a testable
assertion — kill Redis after enqueue, observe anchor NOT advance.

* fix(recon): B1+B2 — entry gate on queue quiescence before delta computation

Review amendments to d78a34c (do-not-merge verdict, concurred):

B1 (blocking): d78a34c gated only the anchor ADVANCE. The exclusion filter
in computeAccountDeltas reads tx_processed markers from the DATABASE —
markers still on the Redis queue are invisible to it. A recon re-run over a
loaded queue therefore re-includes txs whose effects are in flight, and both
applications eventually drain: double-apply. The advance gate made this
ROUTINE rather than exotic — every gate timeout leaves the queue loaded and
the range open, and the next recon run is the re-run. Fix: computeAccountDeltas
now passes WaitForQueueQuiescence before touching the filter; failure = fail
closed (no deltas, recon skipped this round, SyncMonitor retries).

B2: the advance gate's empty-target shortcut is WRONG at the entry gate.
After a restart the in-process HWM is lost while Redis can still hold
pre-restart entries — exactly the blind window. Entry gate fallback when the
HWM is empty: poll XLEN + XPENDING until both zero (Len/PendingCount already
existed on RedisStreamer). The starvation argument against depth checks does
not apply here: the fallback only runs when nothing was enqueued since boot;
concurrent sync phases would have set the HWM and taken the precise path.
The advance gate keeps its shortcut — a wrongly-skipped advance re-opens the
range, which the (now-gated) exclusion filter handles.

Also corrects the false premise in d78a34c's commit message — 'anchor lags,
markers prevent double-apply': markers on the QUEUE prevent nothing; only
markers in the DB do. Same premise-class as the F3 'idempotent absolute
writes' slip: both assumed a property of data that was still in flight. With
B1, the sentence becomes true, because recon never computes over in-flight
markers.

Hardening: note* functions ignore malformed stream IDs — recording one as
the wait target would wedge the gate permanently (malformed compares
not-gte, by design).

Tests: entry-gate-blocks-while-queued (drain catch-up opens it),
boot-fallback (empty HWM + loaded queue = NOT quiescent; empty queue =
quiescent), malformed-ID rejection.

* fix(db): latest_block hygiene — one monotonic writer choke point (F6)

RCA_account_sync.md §5 F6 / §4 secondary. latest_block had four writers, no
guard, and one writer whose job was undoing another's damage:

1. StoreZKBlock wrote the marker blindly per block (immuclient.go:1949) —
   any out-of-order store (PoTS WAL dump, replay, sync worker) regressed
   the tip, and header-sync SKELETON blocks advanced it past the
   data-complete tip (explorer/stateroot/gETH read it as truth).
2. The headers writer therefore snapshot/RESTORED the marker around every
   header batch — and the restore raced concurrent DataSync workers and
   live processing, clobbering their legitimate advances back to a stale
   value. A regression vector built to patch another.
3. The data writer's batch-end update was blind: a stale catchup batch
   committing after newer live blocks moved the marker backwards (the
   exact race txindex.setMetaMonotonicMax already guards against in SQLite).
4. Catchup phase 8 wrote remoteTip blindly — a lagging peer's tip could
   regress the marker below live-committed blocks.

Changes:
- DB_OPs.UpdateLatestBlockMonotonic (new): single choke point, pure
  nextLatestBlock decision + process-local mutex serializing the
  read-decide-write between live/DataSync/catchup writers (same pattern
  and rationale as the applied-anchor mutex).
- StoreZKBlock no longer writes the marker AT ALL. It cannot distinguish
  skeleton from full blocks (zero-tx blocks are legitimate), so ownership
  moves to callers that know: blockPropagation + broadcast (live, full
  blocks) bump monotonically after store+process; the headers writer
  (skeletons) never touches it — its snapshot/restore dance is deleted,
  not fixed, because its reason to exist is gone.
- Data writer batch-end + catchup phase 8: routed through the choke point.
  Genesis note: monotonic-from-0 makes the didWriteBlock genesis special
  case a natural no-op (marker absent reads as 0, which is correct).

AMENDMENT to the RCA F6 spec, from Phase 0: 'delete the per-block write'
was unsafe as written — blockPropagation.go:362 and broadcast.go:788 (both
LIVE paths) relied on StoreZKBlock's internal write for tip tracking.
Deleting without adding explicit caller-side bumps would have frozen the
live tip. Also: HandleStartupSync (RCA item 4) is CALLER-LESS — verified
no references outside its own file — so the catch_up_from_block anchoring
fix is documented as a warning on the function rather than plumbing config
into dead code. ReconcileBlockNumber verified regression-free (scans
forward from the marker, returns >= base, never writes).

Tests: nextLatestBlock monotonic rule (replay, stale batch, genesis,
catchup jump). Full build + suites green.

* chore: remove internal working notes and clean up code comments

Delete the internal working document from docs/ and rewrite code comments
that referenced it or internal shorthand. All technical rationale is
preserved in place — design invariants, failure directions, ordering
requirements, and historical bug context now read as self-contained
engineering documentation.

---------

Co-authored-by: Doc <[email protected]>
* chore: retire fastsync V1 — superseded by FastsyncV2

Deletes the V1 sync engine and its AVRO whole-DB exchange:
- fastsync/ package (fastsync.go, fastsyncNew.go, FileTransfer.go,
  read_avro_test.go)
- DB_OPs/Immudb_AVROfile.go (BackupFromHashMap — only caller was
  fastsyncNew.go)

V1's restore paths were the last writers of the latest_block marker outside
the monotonic choke point (DB_OPs/latest_block.go), and its blind-marker and
wholesale-DB-replace semantics predate the current account-sync invariants
(LWW normalization, marker exclusion, applied anchor, drain confirmation).
Bootstrap-from-snapshot (DOCKER.md) + FastsyncV2 catchup replaced both of
V1's jobs.

Call-site retirement (gRPC/CLI surface PRESERVED — commands return explicit
retirement errors pointing at the V2 equivalents): CLI HandleFastSync,
CLI HandleFirstSync, main.go fastSyncer wiring, CLI sync-info output.

NOT touched (verified distinct): transfer/ package + config.FileProtocol
stream handlers (generic file transfer, also used by CLI SendFile gRPC) —
only V1's usage of them is gone.

Follow-up: go mod tidy may now drop cornelk/hashmap and goavro if nothing
else imports them.

* fix(txindex): close the two live-coverage gaps in the tx-address index

The SQLite address index (txindex.db, serves eth_getTransactionsByAddress)
had exactly three fill paths: sequencer live (broadcast.go IndexBlockAsync),
boot gap-heal (Init), and catchup (EnsureReady). Two paths wrote blocks the
index never saw:

1. HandleSync / PoTS: blocks synced by the non-catchup V2 path and PoTS
   gap-fills stayed unindexed until the next boot or catchup. Fix:
   EnsureReady at the end of handleSyncInternal — covers both phases in one
   call, scans exactly [last_indexed+1 .. latest_block] (both markers
   monotonic), idempotent, no-op when current.

2. blockPropagation (non-sequencer live path): pubsub-received blocks were
   stored + processed but NEVER indexed — only the sequencer's local path
   called IndexBlockAsync. On a non-sequencer node the index drifted
   steadily staler between catchups with IsReady still true (silent stale
   reads, not gated). Fix: IndexBlockAsync after store + marker bump, same
   as broadcast.go. Async, drop-on-overflow; drops heal via the next gap
   scan by design.

Reconciliation/repair unaffected either way — they scan ImmuDB directly,
never the SQLite index. This is a read-API correctness fix only.

* feat(scripts): configure Redis persistence on bare-metal installs

Bare-metal setup only configured requirepass — nodes ran on distro-default
RDB-only persistence (appendonly no), while docker-compose has always set
AOF via CLI flags. A Redis crash between RDB snapshots loses the account
sync stream and the balance effects it carries; persistence is a
correctness requirement for jmdn, not tuning (see DOCKER.md).

- _configure_redis_persistence: appendonly yes, appendfsync everysec,
  maxmemory-policy noeviction — mirrors docker-compose exactly. Idempotent
  per-directive; sets REDIS_CONF_CHANGED so unchanged reruns don't bounce
  the service (and the sync queue with it).
- _verify_redis_persistence: post-start check via redis-cli CONFIG GET —
  the conf edit is a silent no-op if the service unit loads a different
  conf file than the path-candidate scan found; asking the live server
  closes that gap.
- Persistence/verification failures log_error (not warn) — this must not
  be skimmed past on a node install.

* fix: go mod tidy

---------

Co-authored-by: Doc <[email protected]>
…tx index (#67)

TotalTransactions previously came from an immudb native Count over the
'tx:' prefix — a per-request round trip against the main DB. The SQLite
tx-address index already holds every transaction locally; serve the stat
from there.

- txindex.CountTransactions (new): SELECT COUNT(DISTINCT tx_hash).
  DISTINCT is load-bearing: address_txns stores one row per (address, tx)
  pair, so a plain transfer appears under sender AND recipient — COUNT(*)
  would roughly double the reported total. Pinned by test (transfer +
  self-send + replay idempotence).
- Package wrapper errors when the index is uninitialised OR not ready —
  during RebuildIndex the table is truncated, and a partial count must
  never reach the UI.
- explorer getStats: SQLite-first, immudb prefix-Count fallback on any
  txindex error. Freshness note: the index is async on live blocks and
  gap-healed after syncs (see the txindex coverage fix), so the stat can
  trail the immudb count by the async-queue depth for a moment — an
  acceptable trade for removing the immudb scan from every stats request.
…upgrade/reset docs (#68)

Bootstrap source now targets the refreshed snapshot; docker-compose forwards
the part/checksum prefixes it needs (previously only bucket/prefix were
passed, so the Docker path fell back to the old default part name and would
have found no parts for this snapshot).

- docker-compose.yml: jmdn-bootstrap env → jmdn-bootstrap bucket,
  staging/bootstrap-20260709_180202 prefix, data-patched.part parts,
  checksums.md5; PARTS_PREFIX/CHECKSUM_FILE now explicitly forwarded.
- Scripts/bootstrap_sync.sh, .env.docker.example, DOCKER.md: same defaults;
  removed stale jmzk-releases / jmdn_bootstrap_2306 / data_backup_23062026
  references throughout.
- jmdn_exchange.yaml: catch_up_from_block 11605 → 12173 (snapshot tip + 1).

DOCKER.md §13 upgrading:
- de-duplicated the v1.2.0 migration block (was present twice);
- renamed opaque "Option A/B" → "Normal upgrade" / "Building from source";
- dropped a catch_up_from_block note that wrongly implied routine upgrades
  rebootstrap;
- added a single self-contained "One-time chain reset" procedure, framed as
  an exceptional coordinated action (a refreshed snapshot does NOT invalidate
  running nodes) — not a per-snapshot routine.

NOTE: snapshot prefix is still staging/ and must be promoted to a
public-readable location before fleet rollout; version tag in docs is a
placeholder pending the reset release.
* chore(release): prepare v1.2.1

* chore: stamp CHANGELOG for v1.2.1 and update whitepaper
@gitguardian

gitguardian Bot commented Jul 12, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 2 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secrets in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
34631864 Triggered Generic Password fff13a3 config/settings/defaults.go View secret
34631869 Triggered Generic Password fff13a3 Scripts/setup_dependencies.sh View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

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.

2 participants