Json multiline reading#3
Merged
5133n merged 1 commit intofrstrtr:origin/sharechain/async_threadfrom Apr 23, 2021
Merged
Conversation
5133n
added a commit
that referenced
this pull request
Aug 10, 2022
5133n
added a commit
that referenced
this pull request
Aug 14, 2022
5133n
added a commit
that referenced
this pull request
Nov 13, 2022
5133n
added a commit
that referenced
this pull request
Feb 1, 2023
5133n
added a commit
that referenced
this pull request
May 24, 2023
5133n
added a commit
that referenced
this pull request
Jul 20, 2023
frstrtr
added a commit
that referenced
this pull request
Apr 17, 2026
…ares
Port p2pool-dash/p2pool/data.py:181-231 generate_transaction tx_outs
formula into c2pool so the coinbase we build byte-matches what the peer
regenerates during Share.check(). The last remaining Phase 5d gap
reported by the peer was:
ValueError: gentx doesn't match hash_link
because our coinbase emitted 100% to the miner (native c2pool PPLNS or
--no-pplns fallback), whereas p2pool-dash's canonical layout at genesis
is 2% → miner pubkey_hash, 98% → DONATION_SCRIPT. When miner==donation
(our test case XdgF55wEHBRWwbuBniNYH4GvvaoYMgL84u), the formula
collapses to 100% → DONATION_SCRIPT with worker_tx empty.
Changes
─────────────────────────────────────────────────────────────────────
coinbase_builder.hpp
+ compute_dash_payouts(subsidy, packed_payments, miner_pubkey_hash,
weights, total_weight, params) → std::vector<MinerPayout>
Emits tx_outs in the exact order: worker_tx (sorted by script,
excluding DONATION, zeros dropped) || payments_tx (GBT order) ||
donation_tx (always one entry). Inner arithmetic uses __uint128_t
to avoid overflow in the weights branch.
~ build(): now takes a pre-built tx_outs_ordered list, validates
that the last entry is DONATION_SCRIPT, and just appends OP_RETURN.
Removed donation_value arg (now baked into tx_outs_ordered).
Removed internal packed_payments loop (caller provides).
share_builder.hpp
~ build(): miner_payouts param now ignored; outputs computed via
compute_dash_payouts with empty weights (genesis path). Works for
the first 1-2 shares in a chain; non-genesis needs WeightsSkipList.
~ Merkle tree: build from [coinbase placeholder] only, not
[coinbase] + work.m_tx_hashes. Since we advertise empty
new_transaction_hashes (until remember_tx ships), p2pool-dash's
Share.check() expects calculate_merkle_link([None] + [], 0) —
an empty branch with merkle_root == coinbase_txid. Our stratum job
must give the miner the same empty branch so its header's
merkle_root also equals coinbase_txid.
~ ctx.tx_data_hex: cleared (coinbase-only blocks on a full hit).
Interim limitations
─────────────────────────────────────────────────────────────────────
- Genesis/first-2-shares only: WeightsSkipList not yet ported, so
chains of 3+ shares will regress to "gentx doesn't match hash_link"
on share #3+ because the PPLNS distribution needs ancestor miner
weights. Phase 5d-continued work.
- Coinbase-only blocks: on a full block hit we'd submit a block
containing just the coinbase (no fee-paying txs). Until remember_tx
lets us ship tx bodies to peers, this is the correct default.
Verification (isolated LAN test)
─────────────────────────────────────────────────────────────────────
P2Pool: 1 shares in chain (12 verified/12 total) Peers: 1 (1 incoming)
Against a p2pool-dash peer patched with MAX_TARGET=2^256-1 +
BOOTSTRAP_ADDRS=[] (both reverted), 12 consecutive shares broadcast by
c2pool-dash were deserialised, check()'d, and added to the peer's
sharechain. Zero rejections after this commit. The isolation patches
are local-only test harness — master branch files restored.
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 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.