commit 5eea7b47ddb49d655d1f552c5aa2518cf6dffece
parent 730d61cc5967fd05fa104a9c6f49a1f84f21418a
Author: Joris Hartog <jorishartog@hotmail.com>
Date: Wed, 22 Jul 2026 15:29:18 +0200
Move mining settings into mining screen
Diffstat:
3 files changed, 19 insertions(+), 31 deletions(-)
diff --git a/README.md b/README.md
@@ -29,7 +29,8 @@ Genesis leaves the starter wallet with 100 spendable coins after the 1-coin boot
The management UI is a small AlpineJS app served from local vendored assets. It polls JSON endpoints every few seconds and includes:
- wallet and transaction controls,
-- runtime mining and peer settings in the Configuration screen,
+- runtime mining settings in the Mining screen,
+- peer setup and status in the P2P screen,
- P2P peer status with gossip send/receive counters and last error,
- a blockchain explorer and mempool view.
@@ -39,7 +40,7 @@ For a second local node joining Alice's chain:
cargo run -- --data-dir .mivora-bob --http 127.0.0.1:18662 --p2p 127.0.0.1:9445 --join 127.0.0.1:9444
```
-`--join` fetches a chain snapshot from the peer before mining starts and announces this node's P2P listener back to that peer, so newly mined blocks can flow back without restarting the first node. If the peer cannot provide a snapshot, the node exits instead of silently starting a separate chain. Additional peers can be added from the Configuration screen.
+`--join` fetches a chain snapshot from the peer before mining starts and announces this node's P2P listener back to that peer, so newly mined blocks can flow back without restarting the first node. If the peer cannot provide a snapshot, the node exits instead of silently starting a separate chain. Additional peers can be added from the P2P screen.
If `<data-dir>/chain.sqlite3` already exists, the node resumes that chain first. That makes restarts boring in the good way: `--genesis` will not create a new genesis over an existing local chain, and `--join` remains useful for reconnecting to peers without replacing local state. Pass `--chain-db path/to/chain.sqlite3` to override the database path.
@@ -78,7 +79,7 @@ Every non-genesis block must consume the selected mature ticket, include at leas
The protocol targets 60-second blocks by retargeting the expected VDF rounds after each block. It uses a rolling average of recent block intervals and only moves the next round count by about 10% per block, so short bursts do not make the delay swing wildly. Every node derives the same next-round count from the validated chain.
-The base block reward is fixed at 100 coins, and miners collect transaction fees on top. The miner includes the best valid burn for liveness, then fills the remaining block space by fee-rate while respecting nonce and balance validity. The default burn is 0 coins per block, so new nodes can join before they own coins. Genesis starters begin at 100 coins per block; after another wallet has coins, raise its burn from the Configuration screen.
+The base block reward is fixed at 100 coins, and miners collect transaction fees on top. The miner includes the best valid burn for liveness, then fills the remaining block space by fee-rate while respecting nonce and balance validity. The default burn is 0 coins per block, so new nodes can join before they own coins. Genesis starters begin at 100 coins per block; after another wallet has coins, raise its burn from the Mining screen.
The measured VDF round count is only the initial delay. After the first blocks, the protocol steers rounds toward the 60-second target.
diff --git a/assets/mivora-ui.js b/assets/mivora-ui.js
@@ -53,11 +53,11 @@ window.mivoraApp = function mivoraApp() {
tabFromHash() {
const hash = window.location.hash.replace(/^#\/?/, "");
- return ["wallet", "mining", "p2p", "chain", "config"].includes(hash) ? hash : "wallet";
+ return ["wallet", "mining", "p2p", "chain"].includes(hash) ? hash : "wallet";
},
setTab(tab) {
- if (!["wallet", "mining", "p2p", "chain", "config"].includes(tab)) return;
+ if (!["wallet", "mining", "p2p", "chain"].includes(tab)) return;
this.tab = tab;
if (window.location.hash !== `#${tab}`) {
window.location.hash = tab;
@@ -70,7 +70,6 @@ window.mivoraApp = function mivoraApp() {
mining: "Mining",
p2p: "P2P",
chain: "Chain",
- config: "Configuration",
}[this.tab] || "Mivora";
},
diff --git a/src/adapters/http.rs b/src/adapters/http.rs
@@ -499,8 +499,7 @@ const INDEX_HTML: &str = r#"<!doctype html>
.setup-status { border: 1px solid #566d25; border-radius: 8px; padding: 10px; background: #1c2516; color: #d5f55f; font-weight: 800; }
.wallet-grid { width: 100%; display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, .8fr); gap: 12px; align-items: start; }
.wallet-actions { display: grid; gap: 12px; }
- .mining-grid { width: 100%; display: grid; grid-template-columns: minmax(0, .95fr) minmax(300px, .7fr); gap: 12px; align-items: start; }
- .config-grid { width: 100%; display: grid; grid-template-columns: minmax(0, .9fr) minmax(300px, .75fr); gap: 12px; align-items: start; }
+ .mining-grid { width: 100%; display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; align-items: start; }
.receive-address { display: grid; gap: 8px; }
.address-box { border: 1px solid #2f363c; border-radius: 8px; padding: 11px; background: #111316; }
.panel-head { display: flex; justify-content: space-between; gap: 12px; align-items: center; margin-bottom: 12px; }
@@ -543,7 +542,7 @@ const INDEX_HTML: &str = r#"<!doctype html>
.pill.transfer { background: #17312a; color: #8de9cd; }
.mempool-strip { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; }
.mempool-item { flex: 0 0 200px; border: 1px solid #2f363c; border-radius: 8px; padding: 10px; background: #111316; }
- @media (max-width: 920px) { .setup-grid, .wallet-grid, .mining-grid, .config-grid, .detail-grid { grid-template-columns: 1fr; } }
+ @media (max-width: 920px) { .setup-grid, .wallet-grid, .mining-grid, .detail-grid { grid-template-columns: 1fr; } }
@media (max-width: 760px) {
.app-shell { grid-template-columns: 1fr; }
.sidebar { position: sticky; z-index: 5; bottom: 0; top: auto; height: auto; flex-direction: row; justify-content: space-between; padding: 8px; border-right: 0; border-bottom: 1px solid #262b2f; }
@@ -552,14 +551,14 @@ const INDEX_HTML: &str = r#"<!doctype html>
.nav-button { width: 52px; min-height: 48px; }
.nav-button span { font-size: 10px; }
.content { padding: 16px 12px 36px; }
- header, .split, .setup-grid, .wallet-grid, .mining-grid, .config-grid, .detail-grid, .wallet-tx-row { grid-template-columns: 1fr; }
+ header, .split, .setup-grid, .wallet-grid, .mining-grid, .detail-grid, .wallet-tx-row { grid-template-columns: 1fr; }
header { display: grid; }
input { min-width: 0; width: 100%; }
.switch input { width: auto; }
.block-card { flex-basis: 108px; }
}
</style>
- <script defer src="/assets/mivora-ui.js?v=26"></script>
+ <script defer src="/assets/mivora-ui.js?v=28"></script>
<script defer src="/assets/alpine.min.js"></script>
</head>
<body x-data="mivoraApp()" x-init="init()" x-cloak>
@@ -583,10 +582,6 @@ const INDEX_HTML: &str = r#"<!doctype html>
<svg class="chain-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="1.5" y="9" width="5.5" height="5.5"></rect><rect x="9.25" y="9" width="5.5" height="5.5"></rect><rect x="17" y="9" width="5.5" height="5.5"></rect></svg>
<span>Chain</span>
</button>
- <button class="nav-button" :class="{ active: tab === 'config' }" @click="setTab('config')" type="button" title="Configuration" aria-label="Configuration">
- <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 21v-7"></path><path d="M4 10V3"></path><path d="M12 21v-9"></path><path d="M12 8V3"></path><path d="M20 21v-5"></path><path d="M20 12V3"></path><path d="M2 14h4"></path><path d="M10 8h4"></path><path d="M18 16h4"></path></svg>
- <span>Config</span>
- </button>
</nav>
</aside>
@@ -665,6 +660,14 @@ const INDEX_HTML: &str = r#"<!doctype html>
<div class="metric"><div class="label">Target</div><div class="value" x-text="targetSecondsLabel()"></div></div>
</div>
</div>
+ <div class="panel">
+ <h3>Mining</h3>
+ <form @submit.prevent="saveBurn">
+ <label>Coins per block<input x-model.number="burnAmountDraft" @input="burnAmountDirty = true" type="number" min="0"></label>
+ <label>Fee<input :value="automaticBurnFeeDraft()" type="number" readonly></label>
+ <button class="primary" type="submit">Save</button>
+ </form>
+ </div>
</div>
</section>
@@ -689,22 +692,6 @@ const INDEX_HTML: &str = r#"<!doctype html>
</div>
</section>
- <section x-show="tab === 'config'">
- <div class="page-title">
- <div class="muted">Runtime node settings</div>
- </div>
- <div class="config-grid">
- <div class="panel">
- <h3>Mining</h3>
- <form @submit.prevent="saveBurn">
- <label>Coins per block<input x-model.number="burnAmountDraft" @input="burnAmountDirty = true" type="number" min="0"></label>
- <label>Fee<input :value="automaticBurnFeeDraft()" type="number" readonly></label>
- <button class="primary" type="submit">Save</button>
- </form>
- </div>
- </div>
- </section>
-
<section x-show="tab === 'chain'">
<div class="explorer-shell">
<div class="block-rail-wrap">
@@ -753,6 +740,7 @@ const INDEX_HTML: &str = r#"<!doctype html>
<template x-for="tx in selectedBlock.transactions" :key="tx.signature">
<div class="tx-card">
<div class="tx-head"><span class="pill" :class="tx.kind" x-text="tx.kind"></span><strong x-text="tx.amount"></strong></div>
+ <div class="muted">fee <span x-text="tx.fee ?? 0"></span></div>
<div><span class="muted">from </span><code x-text="short(tx.from)"></code></div>
<div x-show="tx.to"><span class="muted">to </span><code x-text="short(tx.to)"></code></div>
<div class="muted">nonce <span x-text="tx.nonce"></span></div>