Skip to content

devcore_tests created;addrstore, filesystem bugfixes#4

Merged
5133n merged 1 commit intofrstrtr:origin/sharechain/async_threadfrom
Kaieida:filesystem
Apr 23, 2021
Merged

devcore_tests created;addrstore, filesystem bugfixes#4
5133n merged 1 commit intofrstrtr:origin/sharechain/async_threadfrom
Kaieida:filesystem

Conversation

@Kaieida
Copy link
Copy Markdown
Contributor

@Kaieida Kaieida commented Apr 23, 2021

No description provided.

@5133n 5133n merged commit bbf1599 into frstrtr:origin/sharechain/async_thread Apr 23, 2021
frstrtr added a commit that referenced this pull request Apr 20, 2026
Extracts dashboard.html's getColor() (lines 5404-5428) and render()
(lines 4672-4763) paint body into coin-agnostic, pure-function
primitives plus a thin DOM-touching adapter. Palette values preserved
verbatim so M2 pixel-diff holds for LTC once the host page switches.

src/explorer/colors.ts (~110 LOC)

- type ShareClass = 'dead' | 'stale' | 'unverified' | 'fee' |
                    'native' | 'signaling' | 'legacy'
- classifyShare(share, ctx): ShareClass
  Pure classifier with dashboard.html priority preserved:
  dead > stale > unverified > fee > native > signaling > legacy
  Threshold (coin.shareVersion) is a ctx parameter so Dash V16 and
  LTC V36 share the same classifier (M1 D5).
- getColor(share, ctx, palette?): string
  "Mine" variants (nativeMine, signalingMine, legacyMine) apply only
  to the three version-tier classes; dead/stale/unverified/fee are
  terminal-coloured regardless of address match. Empty or undefined
  `ctx.myAddress` never marks a share as mine.
- LTC_COLOR_PALETTE: frozen ColorPalette — byte-identical values to
  dashboard.html's hex strings. Tests assert parity.
- ColorsPlugin: id 'explorer.colors', provides 'explorer.colors'
  capability exposing { classifyShare, getColor, LTC_COLOR_PALETTE }.

src/explorer/grid-paint.ts (~180 LOC)

- type PaintCommand = setTransform | fillBackground | fillCell |
                       strokeRect | textCenter
- buildPaintProgram(opts): PaintCommand[]
  Pure function: takes a GridLayout + shares + userContext + palette
  + dpr, produces the exact ordered sequence of canvas ops that
  dashboard.html's render() issues. Empty-chain path preserved (the
  "Waiting for shares..." text). hoveredIndex appends a strokeRect.
- executePaintProgram(ctx, program): void
  Applies commands to a CanvasLike (narrow interface covering the
  eight context members we use) — easily mocked in Node for tests.
- createGridRenderer(canvas): { paint, buildProgram, destroy }
  DOM-touching adapter. Sizes canvas for dpr per dashboard.html
  (canvas.width = cssW*dpr; ctx.setTransform(dpr,0,0,dpr,0,0));
  destroy() flips a local flag so subsequent paint() is a no-op.
- GridCanvasPlugin: id 'explorer.grid.canvas', provides
  'renderer.grid.canvas', fills slot 'explorer.main.grid'.

Explorer bundle public API (src/explorer/index.ts):

- Re-exports ColorsPlugin + GridCanvasPlugin alongside the existing
  GridLayoutPlugin.
- registerExplorerBaseline(host) now also registers them.

Tests:

- tests/unit/colors.test.ts (~21 tests)
  Priority-order proofs for classifyShare, palette mapping for every
  class including mine variants, threshold-from-context (Dash V16),
  empty myAddress does NOT mark as mine, custom palette override,
  verbatim palette value parity with dashboard.html:5404-5428.

- tests/unit/grid-paint.test.ts (~10 tests)
  buildPaintProgram: empty chain emits setTransform+bg+emptyText;
  one share emits fillCell with expected colour + geometry; mixed
  shares (dead, signaling, native-mine) produce correct palette;
  hoveredIndex appends strokeRect; negative/OOB hover ignored.
  executePaintProgram: mock CanvasLike proves commands map to the
  correct context calls in the correct order. Round-trip stress
  test with 4320 shares + full mix exercises the hot path.

Status: 94/94 tests pass (63 + 21 + 10). Typecheck clean under
strict + exactOptionalPropertyTypes + noUncheckedIndexedAccess.
Bundles: shared-core 25.1 KB / 40 (63%), sharechain-explorer 29.8
KB / 120 (25%). Pipeline green.

Next: Phase B #3 — Delta merger (merge window + new shares with
fork-switch detection and windowSize slice). The algorithmic core
of real-time updates; strong candidate for property-based tests
against fast-check. Then #4 — Animator phases 1-3.
frstrtr added a commit that referenced this pull request Apr 20, 2026
Combines the end-to-end composition proof (demo page) with the
algorithmic core of real-time updates (delta merger). Two small
increments in one commit because they validate each other — the
demo shakes out papercuts that unit tests miss; the delta merger
is what the demo will drive once real-time mode lands.

Demo page (demo/)

- demo.html                 CSP-compliant static shell per delta v1
                            §B.3 (no inline script, no 'unsafe-eval');
                            header toolbar with add/rotate/reset
                            controls; canvas in a scrollable wrap.
- demo.mjs                  Pure ES module import from
                            ../dist/sharechain-explorer.js. Seeded
                            mulberry32 RNG (seed=42) → deterministic
                            synthetic 4320-share chain across the
                            full palette mix (V36 native + V35→V36
                            signalling + V35 legacy + stale/dead/
                            unverified/fee + "mine" address). Wires
                            Host + registerExplorerBaseline + paint
                            + mouse hit-test via cellAtPoint.
                            window.__demo exposes host/renderer/
                            getShares/paint for devtools poking.
- README.md                 How to run (python -m http.server 8080
                            then open demo.html). file:// note
                            because ESM imports require an origin.

Proves the following plugins compose without a c2pool server:
  shared.transport.http, shared.theme.dark, shared.addr.hue-hash,
  shared.treemap.squarified, shared.hashrate.si-units,
  shared.i18n.en, explorer.grid.layout, explorer.colors,
  explorer.grid.canvas — plus the full middleware chain.

Delta merger (src/explorer/delta.ts)

- mergeDelta(current, delta, opts) → MergeResult
    Pure; reconciles §5.3 spec:
      * prepend delta.shares (newest first)
      * dedup by short-hash (both within delta AND against current)
      * slice to windowSize, collecting evicted short-hashes
      * fork_switch: true → rebuild from delta alone, evict whole
        current window (with dedup + cap preserved)
    Returns { shares, added, evicted, forkSwitch, tip } so callers
    can drive animation decisions (Phase B #4).
- windowSize 0 + maxShares undefined = unlimited (useful for demos);
  maxShares override controls the cap independently of windowSize.
- Opaque payload fields pass through — works with DeltaShare<T>
  generic so callers can type their full share shape.
- DeltaMergerPlugin: id 'explorer.delta.merger-default',
  provides 'merger.delta', registered via registerExplorerBaseline.

Tests (tests/unit/delta.test.ts — 19 tests)

Spec contract coverage: empty inputs, normal prepend with order
preservation, multi-share delta, opaque payload passthrough, dedup
both directions (delta vs current; within-delta), eviction math,
windowSize 0 unlimited, maxShares override, fork_switch rebuild
with cap + dedup + empty edge case, immutability of inputs,
1000-iteration saturation stress with duplicate-injection
invariant (no-duplicates after any merge sequence).

Status

- 113/113 tests pass in 2.2s (94 prior + 19 new)
- Typecheck clean
- Bundles: shared-core 25.1 KB / 40, sharechain-explorer 30.6 KB /
  120 — both under budget
- HTTP probe confirms bundle + demo assets serve correctly

Next: Phase B #4 — Animator phases 1-3 (death/wave/birth); will
consume mergeDelta's { added, evicted } to drive transitions.
Or (parallel track) Qt refactor step 1 — CMake deps + option flags
in ui/c2pool-qt/CMakeLists.txt.
frstrtr added a commit that referenced this pull request Apr 20, 2026
Three-phase animation state machine (spec §6, dashboard.html:4866-
5400). This commit ships the core — timing math, stagger schedules,
position interpolation for dying/wave/born tracks, and the
controller (start/tick/queueNext/reset). Scale, colour-lerp,
particle dissolution and card overlays land in subsequent commits;
each is a clearly isolable addition that the pure-function structure
accommodates without redesign.

Phase timing (verbatim from dashboard.html:4977-4982):

  phase1Dur  = 3000         DYING
  phase2Dur  = fast ? 2000 : 4000    WAVE  (spec §6 text has typo,
                                             code is authoritative)
  phase2Start = phase1Dur
  phase3Start = phase2Start + phase2Dur * 0.7   (overlap)
  phase3Dur  = 3000         BORN
  duration   = phase3Start + phase3Dur

Stagger schedules (all preserved verbatim from dashboard.html):

  DYING  — last dying share first, 150 ms per share. Newer end of
           the window dies visibly before older shares (mirrors the
           natural tail-eviction order).
  WAVE   — tail-first per dashboard.html:5146-5151:
               distFromTail = (N-1) - newIndex
               fraction     = distFromTail / (N-1)
               shareStart   = fraction * phase2Dur * 0.7
               shareT       = clamp01((p2elapsed - shareStart) / 600)
               ease         = 1 - (1-shareT)^3   (easeOut-cubic)
           Tail starts first, head last; each share animates over a
           600 ms window regardless of phase2Dur.
  BORN   — newest share first, 150 ms per share, full phase3Dur
           window per share.

Input / output:

  AnimationInput {
    oldShares, newShares, addedHashes, evictedHashes,
    oldLayout, newLayout, userContext, palette,
    hashOf, fast?
  }
  AnimationPlan {
    tEnd, phase1Start/Dur, phase2Start/Dur, phase3Start/Dur,
    frameAt(t): FrameSpec
  }
  FrameSpec {
    cells: CellFrame[],     // per-share {x,y,size,color,alpha,track}
    backgroundColor,
    layout                  // post-merge layout
  }

Controller:

  createAnimationController() → {
    isRunning(), start(plan, now), tick(now), queueNext(plan), reset()
  }
  - tick() returns the frame to paint, or null when idle.
  - queueNext() during running: queued plan starts automatically on
    next tick after current tEnd (§6 threshold rule #2, _animDeferred).
  - queueNext() while idle: queued plan starts on the next tick.
  - reset() drops current + queued.

Also exposed:

  SKIP_ANIMATION_NEW_COUNT_THRESHOLD = 100     (§6 rule #1, callers
                                                skip build/start when
                                                newCount >= 100)
  Helpers: clamp01, lerp, easeInOut, computePhaseTiming(fast?)
  Plugin: explorer.animator.three-phase  (provides 'animator.grid')

Tests (19): phase-timing constants for slow + fast; clamp/lerp/ease
sanity; empty input edge; fast vs slow tEnd; wave position
interpolation at t=0 and t=tEnd; wave stagger (tail moves before
head at early phase2 tick); dying staggered alpha decay; dying at
phase1 end alpha ~= 0; born spawns below grid and lands at (0,0);
born alpha fade-in over first 30%; controller idle tick; controller
start+tick+finish becomes idle; controller queueNext during running;
controller queueNext on idle starts next tick; controller reset
drops both.

Status

- 132/132 tests pass in 2.2s (113 prior + 19 new)
- Typecheck clean
- Bundles: shared-core 25.1 KB / 40, sharechain-explorer 33.9 KB /
  120 — both under budget
- Pipeline green end-to-end via npm run verify

Next commits layered onto this core:

  Phase B #5  scale effects (lift-slide-land wave scale, dying-rise
              scale, born shrink from bornScale→1x). Requires no new
              types — just richer CellFrame.size interpolation and
              paint-program extension for centred-scale fillRect.

  Phase B #6  colour lerp — dying lerps toward palette.dead across
              its stagger window; born coalesces from unverified to
              coin colour.

  Phase B #7  particle effects (ash dissolution, birth coalescence)
              and card overlays (miner addr + PPLNS % text during
              hold frames). Largest increment — may split again.

  Phase B #8  wire mergeDelta + animator + gridRenderer into a
              RealTime plugin (SSE subscription + delta application
              + animation trigger). Unlocks the demo against a live
              c2pool server.
frstrtr added a commit that referenced this pull request Apr 24, 2026
Crash reproducibly hit on first SML sync timer fire (PID dies right
after the [SML] sync request log line, before any mnlistdiff arrives).
Apport core dump bt:

  Thread 1 SIGSEGV
  #0  0x...3f0 (??)               ← jumped to garbage
  #1  initiate_async_wait::operator()<std::function<...>&>(...)
  #2  main::{lambda(...)#3}::operator()(...)   ← timer lambda body
  #3  wait_handler::do_complete(...)
  #4  scheduler::run(error_code&)
  #5  main

Bug: the persistent std::function `sml_sync_tick` was passed BY LVALUE
to async_wait, captured `&sml_sync_tick` for self-reference. boost::
asio's perfect-forwarding into the internal handler queue moved-from
the lvalue on first dispatch (universal-reference deduction +
std::forward semantics), leaving the outer std::function empty. The
copy that ran on first fire then re-armed by passing the now-empty
outer function — second fire dereferenced the empty std::function and
SIGSEGV'd into garbage.

Fix: hold the persistent std::function on the heap via shared_ptr,
and NEVER pass it directly to async_wait. Instead, schedule via a
fresh wrapper lambda that captures the shared_ptr by value and
invokes (*sml_sync_tick)(ec) when fired. Each schedule() call hands
async_wait a brand-new lambda; boost::asio can move-from the
temporary as much as it wants without affecting the persistent
function.

Verified: pattern is the canonical chained-timer idiom. The shared_ptr
keeps the tick function + captures alive across reschedules; each
fresh wrapper lambda holds its own copy of the shared_ptr (refcount++)
so the timer chain is self-sustaining until io_context exits.
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