<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: BTC PoW Lab</title>
    <description>The latest articles on DEV Community by BTC PoW Lab (@btcpowlab).</description>
    <link>https://dev.to/btcpowlab</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4108565%2F24c32ef9-7183-4e88-a962-366894b02085.png</url>
      <title>DEV Community: BTC PoW Lab</title>
      <link>https://dev.to/btcpowlab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/btcpowlab"/>
    <language>en</language>
    <item>
      <title>Reproducing a mining dashboard’s hashrate from share records</title>
      <dc:creator>BTC PoW Lab</dc:creator>
      <pubDate>Sat, 05 Sep 2026 22:44:53 +0000</pubDate>
      <link>https://dev.to/btcpowlab/reproducing-a-mining-dashboards-hashrate-from-share-records-417</link>
      <guid>https://dev.to/btcpowlab/reproducing-a-mining-dashboards-hashrate-from-share-records-417</guid>
      <description>&lt;p&gt;A share counter alone cannot reproduce a mining pool’s hashrate estimate when assigned difficulty changes. You need the difficulty credited to each accepted submission, its timestamp, and the estimator’s time-window rules.&lt;/p&gt;

&lt;p&gt;I operate BTC PoW Lab through Power CM Software. We have published an early browser-local tool, &lt;a href="https://share-work-audit.vercel.app/" rel="noopener noreferrer"&gt;Share Work Audit&lt;/a&gt;, to make this arithmetic inspectable. The &lt;a href="https://github.com/monzncarlos-droid/share-work-audit" rel="noopener noreferrer"&gt;source and method are public&lt;/a&gt;. This article was prepared with AI assistance and checked against the implementation and its tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the work, not the share count
&lt;/h2&gt;

&lt;p&gt;The calculation implemented is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TH/s = sum(accepted assigned difficulty) * 2^32
       / window_duration_seconds / 10^12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a synthetic example, ten accepted shares with assigned difficulty 4,096 during a complete 300-second window represent about 0.5864 TH/s. Counting those shares as ten equal units would lose the information needed to compare them with shares assigned a different difficulty.&lt;/p&gt;

&lt;p&gt;Use assigned difficulty, not the unusually high difficulty a lucky hash happened to achieve. Rejected and stale submissions do not enter the accepted-work sum.&lt;/p&gt;

&lt;h2&gt;
  
  
  The denominator is part of the method
&lt;/h2&gt;

&lt;p&gt;Our tool implements 5-, 15- and 60-minute windows, with the interval &lt;code&gt;(evaluation time - window, evaluation time]&lt;/code&gt;. It excludes future submissions.&lt;/p&gt;

&lt;p&gt;Full-window mode withholds a result when the declared recording history is too short. A separate shortened-startup policy is appropriate only when you know the estimator’s actual initialization time. The first share in a file does not establish that time.&lt;/p&gt;

&lt;p&gt;A disconnected miner’s earlier accepted work remains in the calculation until it leaves the window. The clock continues running. A capture gap, however, is missing evidence: it must not silently become a period of zero work.&lt;/p&gt;

&lt;h2&gt;
  
  
  A minimal input record
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id,timestamp,assigned_difficulty,status
session1-1,2026-09-01T00:01:00Z,4096,accepted
session1-2,2026-09-01T00:02:00Z,8192,rejected
session1-3,2026-09-01T00:03:00Z,4096,stale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IDs must be unique across sessions. Statuses are mutually exclusive. The tool reports both submission counts and difficulty-weighted non-accepted work, because those percentages can differ. No wallet address, password or connection credentials are required.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the output does—and does not—establish
&lt;/h2&gt;

&lt;p&gt;The CSV stays in your browser. The export records the selected policy, interval, difficulty sums, denominator and estimate, plus a SHA-256 digest of the canonical parsed records. That digest identifies a particular parsed dataset; it does not prove that the original submissions occurred or that a pool accepted them.&lt;/p&gt;

&lt;p&gt;The built-in example is synthetic and stays labeled as such. JavaScript floating-point arithmetic is used. This is an arithmetic and reproducibility aid, not payout accounting, cryptographic share validation or a completed controlled trial of BTC PoW Lab.&lt;/p&gt;

&lt;p&gt;Clock differences, timestamp precision, smoothing and interval conventions need investigation before calling a dashboard discrepancy an error. An estimate alone also does not establish a miner’s physical hashrate or prove a payment.&lt;/p&gt;

&lt;p&gt;Tests cover weighted work, time boundaries, disconnections, incomplete history, invalid inputs and exported provenance. Browser checks covered the actual CSV download and mobile layout. Minimal, non-sensitive counterexamples with an expected calculation are welcome in the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we built it
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.btcpowlab.com/" rel="noopener noreferrer"&gt;Lab&lt;/a&gt; continuously researches potential improvements across the mining process; experimental ideas are not demonstrated advantages. This small tool gives that work a public, reproducible starting point.&lt;/p&gt;

&lt;p&gt;For context, the associated &lt;a href="https://btcpowlab-pool.com/community" rel="noopener noreferrer"&gt;pool’s Hybrid Solo rules&lt;/a&gt; allocate 85% to the block finder, 10% to other eligible community miners and 5% to the Lab. The Lab allocation also funds rule-based bonuses, infrastructure and research. Those economics are separate from this calculator, and the tool can be used with any pool whose required records and calculation policy are available.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>A Mining-Pool Payout Promise Is Not an Accounting System</title>
      <dc:creator>BTC PoW Lab</dc:creator>
      <pubDate>Thu, 03 Sep 2026 18:29:00 +0000</pubDate>
      <link>https://dev.to/btcpowlab/a-mining-pool-payout-promise-is-not-an-accounting-system-33al</link>
      <guid>https://dev.to/btcpowlab/a-mining-pool-payout-promise-is-not-an-accounting-system-33al</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclosure: I operate BTC PoW Lab Pool, which is used below as a case study. This draft was prepared with AI assistance and reviewed against the live public API and project records. The pool has not found a block and has no payout history.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A percentage on a mining-pool landing page is not an accounting system.&lt;/p&gt;

&lt;p&gt;“The finder receives 85%” sounds precise. It is not enough to answer the questions that appear after a real block:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which miners were eligible at the exact block height?&lt;/li&gt;
&lt;li&gt;Which accepted shares were inside the reward window?&lt;/li&gt;
&lt;li&gt;Was the finder excluded from the community allocation?&lt;/li&gt;
&lt;li&gt;How were rounding and dust handled?&lt;/li&gt;
&lt;li&gt;Which exchange rate produced the USD estimate shown in a screenshot?&lt;/li&gt;
&lt;li&gt;Can the operator reproduce the result after a restart or database restore?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If those answers are calculated from mutable live state, two honest queries made seconds apart can disagree. A restart can make the disagreement harder to explain. A screenshot can then look like stronger evidence than the database.&lt;/p&gt;

&lt;p&gt;The fix is to treat the block as an accounting event and create an immutable snapshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate the economic identity from the connection
&lt;/h2&gt;

&lt;p&gt;A miner may use several workers, reconnect repeatedly, or run more than one device. Those are operational sessions, not necessarily separate economic participants.&lt;/p&gt;

&lt;p&gt;The payout identity should be the Bitcoin address or another explicit settlement identifier. Workers and sessions roll up into that identity. This prevents a participant from manufacturing additional reward weight by splitting one hashrate source into many worker names.&lt;/p&gt;

&lt;p&gt;The raw events should still preserve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;session ID;&lt;/li&gt;
&lt;li&gt;worker ID;&lt;/li&gt;
&lt;li&gt;payout identity;&lt;/li&gt;
&lt;li&gt;accepted difficulty;&lt;/li&gt;
&lt;li&gt;block height;&lt;/li&gt;
&lt;li&gt;server timestamp;&lt;/li&gt;
&lt;li&gt;job and extranonce identifiers;&lt;/li&gt;
&lt;li&gt;rejection reason, when applicable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aggregation should change the accounting view, not erase the evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Freeze rules and inputs together
&lt;/h2&gt;

&lt;p&gt;At block detection, the snapshot should capture more than the final totals. It should include the inputs needed to reproduce them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"block_height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;965350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rules_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hybrid-solo-tiered-community-7d-v5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eligibility_window_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;604800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"finder_identity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bc1q..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"finder_excluded"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"network_reward_sats"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allocations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-03T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input_digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;rules_version&lt;/code&gt; matters because software changes. Re-running today’s code against last month’s data is not necessarily a valid historical reconstruction.&lt;/p&gt;

&lt;p&gt;An &lt;code&gt;input_digest&lt;/code&gt; matters because it binds the snapshot to the accepted-work set used by the calculation. If a later audit imports events from backup, the digest can establish whether it reconstructed the same input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Account in satoshis, display in fiat
&lt;/h2&gt;

&lt;p&gt;Settlement amounts should be integers in satoshis. Fiat values are presentation-layer estimates.&lt;/p&gt;

&lt;p&gt;A user may capture “US$ 120.20” and see “US$ 117.80” later even when the underlying reward remains exactly 158,323 sats. That is normal if the BTC/USD quote changed. The interface should show both values and explain which one is authoritative.&lt;/p&gt;

&lt;p&gt;For every allocation, persist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exact &lt;code&gt;total_sats&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;allocation components;&lt;/li&gt;
&lt;li&gt;remainder policy;&lt;/li&gt;
&lt;li&gt;dust state;&lt;/li&gt;
&lt;li&gt;fiat quote and quote timestamp used for display;&lt;/li&gt;
&lt;li&gt;status such as &lt;code&gt;PROJECTED&lt;/code&gt;, &lt;code&gt;FROZEN&lt;/code&gt;, &lt;code&gt;PAYABLE&lt;/code&gt;, &lt;code&gt;DEFERRED_DUST&lt;/code&gt;, &lt;code&gt;BROADCAST&lt;/code&gt;, or &lt;code&gt;CONFIRMED&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Never overwrite the satoshi amount merely because the display quote changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make rounding deterministic
&lt;/h2&gt;

&lt;p&gt;If proportional weights do not divide the reward exactly, the system needs a published remainder rule. Examples include largest remainder or deterministic assignment ordered by a stable identity hash.&lt;/p&gt;

&lt;p&gt;The important part is not which reasonable rule is selected. The important part is that the rule is versioned, deterministic, and applied before the snapshot is sealed.&lt;/p&gt;

&lt;p&gt;The sum must satisfy an invariant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;finder_sats + community_sats + lab_sats + explicit_remainders
  = distributable_block_reward_sats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the invariant fails, payout preparation must fail closed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calculation and signing are different trust boundaries
&lt;/h2&gt;

&lt;p&gt;An automatic calculator does not need access to private keys. A signer does.&lt;/p&gt;

&lt;p&gt;That difference is operationally important. A safer pipeline is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;detect a candidate block;&lt;/li&gt;
&lt;li&gt;verify it against the Bitcoin node;&lt;/li&gt;
&lt;li&gt;freeze the accounting snapshot;&lt;/li&gt;
&lt;li&gt;wait for the configured confirmation threshold;&lt;/li&gt;
&lt;li&gt;construct a batch transaction or PSBT from the frozen plan;&lt;/li&gt;
&lt;li&gt;validate outputs against the snapshot;&lt;/li&gt;
&lt;li&gt;sign in a separate, constrained environment;&lt;/li&gt;
&lt;li&gt;broadcast and record the transaction ID;&lt;/li&gt;
&lt;li&gt;reconcile every output and fee.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is also where “automatic payouts” can become custody. If community funds first arrive at an operator-controlled address, the operator controls those funds until the distribution transaction is signed. Automation reduces manual work; it does not remove the custody boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design the appeal process before the dispute
&lt;/h2&gt;

&lt;p&gt;Suppose a miner says, “I should have received more,” and sends a screenshot.&lt;/p&gt;

&lt;p&gt;The operator should be able to produce an audit bundle without editing the production database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;block and confirmation evidence;&lt;/li&gt;
&lt;li&gt;rules version;&lt;/li&gt;
&lt;li&gt;participant identity and eligibility interval;&lt;/li&gt;
&lt;li&gt;accepted-work rows or a verifiable aggregate;&lt;/li&gt;
&lt;li&gt;weight calculation;&lt;/li&gt;
&lt;li&gt;exact satoshi allocation;&lt;/li&gt;
&lt;li&gt;fiat quote metadata;&lt;/li&gt;
&lt;li&gt;snapshot digest;&lt;/li&gt;
&lt;li&gt;payout transaction and reconciliation state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The screenshot is then useful context, not the sole source of truth. It may reveal a stale UI, a changed exchange rate, a different worker aggregation, or a real accounting defect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Survive restarts and machine loss
&lt;/h2&gt;

&lt;p&gt;The snapshot and event journal should not live only on the machine running Stratum.&lt;/p&gt;

&lt;p&gt;A practical minimum is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an atomic local snapshot;&lt;/li&gt;
&lt;li&gt;an encrypted off-site copy;&lt;/li&gt;
&lt;li&gt;a manifest containing hashes and schema versions;&lt;/li&gt;
&lt;li&gt;a restore test on a separate path;&lt;/li&gt;
&lt;li&gt;documented recovery-point and recovery-time objectives.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;“Backup completed” is weaker than “backup downloaded, decrypted, hashed, and restored successfully.” Verification is part of the backup.&lt;/p&gt;

&lt;h2&gt;
  
  
  What transparency can and cannot prove
&lt;/h2&gt;

&lt;p&gt;Before its first block, a pool can prove that its rules are published, its telemetry exists, its snapshot code is deterministic, and its backups restore.&lt;/p&gt;

&lt;p&gt;It cannot prove a successful payout history that does not exist.&lt;/p&gt;

&lt;p&gt;That distinction is healthy. Engineering evidence should make future claims testable without turning preparation into a claim of completed performance.&lt;/p&gt;

&lt;p&gt;The BTC PoW Lab Pool accounting model and live limitations are public here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://btcpowlab-pool.com/model?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=phase3_targeted_outreach_2026_09&amp;amp;utm_content=auditable_snapshots" rel="noopener noreferrer"&gt;Accounting model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://btcpowlab-pool.com/status?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=phase3_targeted_outreach_2026_09&amp;amp;utm_content=auditable_snapshots" rel="noopener noreferrer"&gt;Operational status&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The broader lesson applies beyond mining: if money is distributed from changing state, freeze the rules, freeze the inputs, record exact units, and separate calculation from signing.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>backend</category>
      <category>bitcoin</category>
    </item>
  </channel>
</rss>
