iuna

iuna - experimental devnet protocol
git clone https://iuna.jhx.app/git/iuna.git
Log | Files | Refs | README | LICENSE

commit 765cce8ff8996d96fc8cd3b7584c23b4c4ccae15
parent faed2d5f0579bf1016c0f9c9c949ca87f321efc7
Author: Joris Hartog <jorishartog@hotmail.com>
Date:   Sun,  2 Aug 2026 02:15:34 +0200

Remove required burn mine reward setting

Diffstat:
Msrc/adapters/chain_store.rs | 9+++------
Msrc/adapters/config_store.rs | 18++----------------
Msrc/adapters/http.rs | 96++++++++++++++++++++++++-------------------------------------------------------
Msrc/app.rs | 131++++++++-----------------------------------------------------------------------
Msrc/domain.rs | 527++++++++++---------------------------------------------------------------------
Msrc/main.rs | 5+----
Mtests/iuna.rs | 23++++++++++-------------
Mtests/properties.rs | 29+++++++++--------------------
Mwww/assets/iuna-ui.js | 96++++---------------------------------------------------------------------------
9 files changed, 133 insertions(+), 801 deletions(-)

diff --git a/src/adapters/chain_store.rs b/src/adapters/chain_store.rs @@ -9,7 +9,7 @@ use rusqlite::{Connection, OptionalExtension, params}; use serde::Serialize; -use crate::domain::{Amount, ChainSnapshot, Ledger, Transaction}; +use crate::domain::{Amount, ChainSnapshot, Ledger, MINE_REWARD, Transaction}; const SCHEMA: &str = r#" CREATE TABLE IF NOT EXISTS chain_snapshots ( @@ -331,13 +331,10 @@ fn metrics_from_snapshot(snapshot: &ChainSnapshot) -> Result<Vec<BlockMetricRow> .checked_add(*amount) .context("block metric burns overflow")?; } - Transaction::Mine { - required_burn_amount, - .. - } => { + Transaction::Mine { .. } => { mine_count += 1; mine_issued_amount = mine_issued_amount - .checked_add(*required_burn_amount) + .checked_add(MINE_REWARD) .and_then(|amount| amount.checked_add(transaction.fee())) .context("block metric mine issuance overflow")?; } diff --git a/src/adapters/config_store.rs b/src/adapters/config_store.rs @@ -9,7 +9,7 @@ use std::{ use anyhow::{Context, Result, bail}; use serde::{Deserialize, Serialize}; -use crate::domain::{Amount, DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS, MICRO_IUNA}; +use crate::domain::{Amount, MICRO_IUNA}; const CONFIG_FILE_VERSION: u32 = 1; const AMOUNT_UNIT_MICROIUNA: &str = "microiuna"; @@ -26,7 +26,6 @@ pub struct UiConfig { pub pow_mining_enabled: bool, pub burn_per_block: Amount, pub burn_fee: Amount, - pub mine_required_burn_multiplier_bps: u16, pub keep_track_of_metrics: bool, pub p2p_accept_inbound: bool, pub p2p_announce_addr: Option<String>, @@ -42,7 +41,6 @@ impl Default for UiConfig { pow_mining_enabled: false, burn_per_block: DEFAULT_BURN_AMOUNT, burn_fee: DEFAULT_BURN_FEE, - mine_required_burn_multiplier_bps: DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS, keep_track_of_metrics: false, p2p_accept_inbound: false, p2p_announce_addr: None, @@ -68,8 +66,6 @@ struct ConfigFile { #[serde(default)] burn_fee: Option<Amount>, #[serde(default)] - mine_required_burn_multiplier_bps: Option<u16>, - #[serde(default)] keep_track_of_metrics: bool, #[serde(default)] p2p_accept_inbound: Option<bool>, @@ -104,7 +100,6 @@ pub fn save(path: &Path, config: &UiConfig) -> Result<()> { pow_mining_enabled: config.pow_mining_enabled, burn_per_block: config.burn_per_block, burn_fee: Some(config.burn_fee), - mine_required_burn_multiplier_bps: Some(config.mine_required_burn_multiplier_bps), keep_track_of_metrics: config.keep_track_of_metrics, p2p_accept_inbound: Some(config.p2p_accept_inbound), p2p_announce_addr: config.p2p_announce_addr.clone(), @@ -156,9 +151,6 @@ fn load(path: &Path) -> Result<UiConfig> { .burn_fee .map(|fee| fee.saturating_mul(scale)) .unwrap_or(DEFAULT_BURN_FEE), - mine_required_burn_multiplier_bps: stored - .mine_required_burn_multiplier_bps - .unwrap_or(DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS), keep_track_of_metrics: stored.keep_track_of_metrics, p2p_accept_inbound, p2p_announce_addr: stored.p2p_announce_addr, @@ -204,7 +196,7 @@ mod tests { assert!(stored.contains("\"burn_per_block\": 100")); assert!(stored.contains("\"burn_fee\": 100")); assert!(!stored.contains("\"pow_mine_fee\"")); - assert!(stored.contains("\"mine_required_burn_multiplier_bps\": 8000")); + assert!(!stored.contains("required_burn")); assert!(stored.contains("\"keep_track_of_metrics\": false")); assert!(stored.contains("\"p2p_accept_inbound\": false")); assert!(stored.contains("\"p2p_announce_addr\": null")); @@ -225,7 +217,6 @@ mod tests { pow_mining_enabled: true, burn_per_block: 50 * MICRO_IUNA, burn_fee: 3 * MICRO_IUNA, - mine_required_burn_multiplier_bps: 9_000, keep_track_of_metrics: true, p2p_accept_inbound: true, p2p_announce_addr: Some("203.0.113.10:9444".to_string()), @@ -241,7 +232,6 @@ mod tests { assert!(config.pow_mining_enabled); assert_eq!(config.burn_per_block, 50 * MICRO_IUNA); assert_eq!(config.burn_fee, 3 * MICRO_IUNA); - assert_eq!(config.mine_required_burn_multiplier_bps, 9_000); assert!(config.keep_track_of_metrics); assert!(config.p2p_accept_inbound); assert_eq!( @@ -285,7 +275,6 @@ mod tests { assert!(!config.pow_mining_enabled); assert_eq!(config.burn_per_block, 0); assert_eq!(config.burn_fee, DEFAULT_BURN_FEE); - assert_eq!(config.mine_required_burn_multiplier_bps, 8_000); assert!(!config.keep_track_of_metrics); assert!(!config.p2p_accept_inbound); assert_eq!(config.peers, vec!["127.0.0.1:9444"]); @@ -347,7 +336,6 @@ mod tests { assert!(config.mining_enabled); assert_eq!(config.burn_per_block, 2 * MICRO_IUNA); assert_eq!(config.burn_fee, MICRO_IUNA); - assert_eq!(config.mine_required_burn_multiplier_bps, 8_000); } #[test] @@ -366,7 +354,6 @@ mod tests { "burn_per_block": 2000000, "burn_fee": 3, "pow_mine_fee": 5, - "mine_required_burn_multiplier_bps": 7500, "peers": [] }} "# @@ -379,6 +366,5 @@ mod tests { assert!(config.mining_enabled); assert_eq!(config.burn_per_block, 2 * MICRO_IUNA); assert_eq!(config.burn_fee, 3); - assert_eq!(config.mine_required_burn_multiplier_bps, 7_500); } } diff --git a/src/adapters/http.rs b/src/adapters/http.rs @@ -33,8 +33,8 @@ use crate::{ FeeEstimate, NodeStatus, PeerDirection, PeerInfo, SharedNode, SharedPeerBook, StratumStatus, }, domain::{ - Amount, Block, DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS, Ledger, MINE_FINALIZER_FEE, - OutPoint, Transaction, TxInput, TxOutput, hex_hash, + Amount, Block, Ledger, MINE_FINALIZER_FEE, MINE_REWARD, OutPoint, Transaction, TxInput, + TxOutput, hex_hash, }, }; @@ -138,7 +138,6 @@ struct BurnSettingsForm { #[derive(Debug, Deserialize)] struct PowMiningForm { enabled: bool, - multiplier_bps: Option<u16>, } #[derive(Debug, Deserialize)] @@ -945,9 +944,9 @@ async fn api_burn_fee_estimate_form( async fn api_mine_fee_estimate_form( State(state): State<HttpState>, - Form(form): Form<PowMiningForm>, + Form(_form): Form<BTreeMap<String, String>>, ) -> Json<FeeEstimateResponse> { - fee_estimate_json(estimate_mine_fee(&state, form).await) + fee_estimate_json(estimate_mine_fee(&state).await) } async fn api_burn_per_block_form( @@ -966,15 +965,7 @@ async fn api_pow_mining_form( State(state): State<HttpState>, Form(form): Form<PowMiningForm>, ) -> Json<ActionResponse> { - action_json( - set_pow_mining( - &state, - form.enabled, - form.multiplier_bps - .unwrap_or(DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS), - ) - .await, - ) + action_json(set_pow_mining(&state, form.enabled).await) } async fn api_metrics_settings_form( @@ -1097,29 +1088,21 @@ async fn persist_burn_settings_config( config_store::save(config_path, &config) } -async fn set_pow_mining(state: &HttpState, enabled: bool, multiplier_bps: u16) -> Result<()> { +async fn set_pow_mining(state: &HttpState, enabled: bool) -> Result<()> { { let mut node = state.node.lock().await; - node.set_pow_mining_settings(enabled, multiplier_bps)?; + node.set_pow_mining_enabled(enabled); } - persist_pow_mining_config( - &state.ui_config, - &state.config_path, - enabled, - multiplier_bps, - ) - .await + persist_pow_mining_config(&state.ui_config, &state.config_path, enabled).await } async fn persist_pow_mining_config( ui_config: &Arc<Mutex<UiConfig>>, config_path: &Path, enabled: bool, - multiplier_bps: u16, ) -> Result<()> { let mut config = ui_config.lock().await; config.pow_mining_enabled = enabled; - config.mine_required_burn_multiplier_bps = multiplier_bps; config_store::save(config_path, &config) } @@ -1638,7 +1621,6 @@ fn wallet_transaction_row( }), Transaction::Mine { recipient, - required_burn_amount, difficulty_bits, signature, .. @@ -1646,12 +1628,12 @@ fn wallet_transaction_row( kind: "mine", from: "pow".to_string(), to: Some(recipient.clone()), - amount: *required_burn_amount, + amount: MINE_REWARD, fee: tx.fee(), inputs: Vec::new(), outputs: vec![TxOutput { address: recipient.clone(), - amount: *required_burn_amount, + amount: MINE_REWARD, }], change: Vec::new(), signature: signature.clone(), @@ -1748,7 +1730,6 @@ fn ui_transaction( }, Transaction::Mine { recipient, - required_burn_amount, difficulty_bits, signature, .. @@ -1756,12 +1737,12 @@ fn ui_transaction( kind: "mine", from: "pow".to_string(), to: Some(recipient.clone()), - amount: *required_burn_amount, + amount: MINE_REWARD, fee: transaction.fee(), inputs: Vec::new(), outputs: vec![TxOutput { address: recipient.clone(), - amount: *required_burn_amount, + amount: MINE_REWARD, }], change: Vec::new(), signature: signature.clone(), @@ -1877,13 +1858,9 @@ fn index_transaction_outputs( let created_outputs = match transaction { Transaction::Transfer { outputs, .. } => outputs.clone(), Transaction::Burn { change, .. } => change.clone(), - Transaction::Mine { - recipient, - required_burn_amount, - .. - } => vec![TxOutput { + Transaction::Mine { recipient, .. } => vec![TxOutput { address: recipient.clone(), - amount: *required_burn_amount, + amount: MINE_REWARD, }], Transaction::BurnClaim { .. } => Vec::new(), }; @@ -2047,7 +2024,7 @@ async fn estimate_burn_fee(state: &HttpState, form: BurnSettingsForm) -> Result< .estimate_burn_fee(form.amount, fee_per_byte) } -async fn estimate_mine_fee(state: &HttpState, _form: PowMiningForm) -> Result<FeeEstimate> { +async fn estimate_mine_fee(state: &HttpState) -> Result<FeeEstimate> { state .node .lock() @@ -2858,7 +2835,7 @@ const INDEX_HTML: &str = r#"<!doctype html> .block-card { flex-basis: 108px; } } </style> - <script defer src="/assets/iuna-ui.js?v=70"></script> + <script defer src="/assets/iuna-ui.js?v=71"></script> <script defer src="/assets/alpine.min.js"></script> </head> <body x-data="iunaApp()" x-init="init()" @keydown.window.escape="closeModals()" x-cloak> @@ -3070,17 +3047,13 @@ const INDEX_HTML: &str = r#"<!doctype html> </div> <div class="panel"> <h3>Mine</h3> - <div class="panel-description">Search for PoW actions that mint IUNA when blocks have enough burns.</div> - <form class="mine-settings-form" @submit.prevent="savePowMining"> + <div class="panel-description">Search for PoW actions that mint a fixed IUNA reward.</div> + <div class="mine-settings-form"> <div class="mine-action-row"> <div class="mine-stats" aria-label="PoW issuance settings"> <div class="mine-stat"> <div class="mine-stat-label">You receive</div> - <div class="mine-stat-value money">IUNA <span x-text="amountLabel(powMineNetReward())"></span></div> - </div> - <div class="mine-stat"> - <div class="mine-stat-label">Needs burns</div> - <div class="mine-stat-value">IUNA <span x-text="amountLabel(powMineRequiredBurn())"></span></div> + <div class="mine-stat-value money">IUNA <span x-text="amountLabel(powMineReward())"></span></div> </div> <div class="mine-stat"> <div class="mine-stat-label">Finalizer earns</div> @@ -3097,20 +3070,8 @@ const INDEX_HTML: &str = r#"<!doctype html> <span class="toggle-text" x-text="powMiningEnabled ? 'On' : 'Off'"></span> </label> </div> - <div class="mine-reward-control"> - <div class="mine-reward-head"><span>Reward setting</span><strong>IUNA <span x-text="amountLabel(powMineNetReward())"></span></strong></div> - <input x-model="powRequiredBurnMultiplierDraft" @input="powRequiredBurnMultiplierDirty = true" type="range" min="0.1" max="1" step="0.05" aria-label="PoW reward setting"> - <div class="mine-slider-hints"><span>Easier to include</span><span>Higher reward</span></div> - <div class="mine-include-status" :class="powMineIncludeStatus().kind"> - <span x-text="powMineIncludeStatus().label"></span> - <span x-text="powMineIncludeStatus().recent"></span> - </div> - </div> - <div class="mine-save-row" x-show="powRequiredBurnMultiplierDirty"> - <button class="primary" type="submit">Save</button> - </div> <div class="fee-preview" x-text="autoPowStatusLabel()"></div> - </form> + </div> <div class="panel-separator"></div> <div class="stratum-config"> <div class="stratum-note">Start the node with <code>--stratum 0.0.0.0:3333</code> to expose a Stratum V1 endpoint for ASIC miners. Use the pool URL below in the miner configuration.</div> @@ -4723,7 +4684,7 @@ mod tests { #[test] fn metrics_screen_includes_block_range_filter() { - assert!(super::INDEX_HTML.contains("iuna-ui.js?v=70")); + assert!(super::INDEX_HTML.contains("iuna-ui.js?v=71")); assert!(super::INDEX_HTML.contains("aria-label=\"Metrics block range\"")); assert!(super::INDEX_HTML.contains("setMetricsRange(100)")); assert!(super::INDEX_HTML.contains("setMetricsRange(1000)")); @@ -4731,12 +4692,14 @@ mod tests { } #[test] - fn mine_screen_shows_recent_burn_fit_status() { + fn mine_screen_shows_fixed_pow_reward_without_burn_slider() { let app_js = include_str!("../../www/assets/iuna-ui.js"); - assert!(app_js.contains("Recent high:")); - assert!(app_js.contains("May wait for bigger burn blocks")); - assert!(super::INDEX_HTML.contains("Easier to include")); - assert!(super::INDEX_HTML.contains("Higher reward")); + assert!(app_js.contains("powMineReward()")); + assert!( + super::INDEX_HTML.contains("Search for PoW actions that mint a fixed IUNA reward.") + ); + assert!(super::INDEX_HTML.contains("amountLabel(powMineReward())")); + assert!(!super::INDEX_HTML.contains("Needs burns")); } async fn auth_test_state(config_path: std::path::PathBuf, config: UiConfig) -> HttpState { @@ -4938,13 +4901,12 @@ mod tests { let initial_config = ui_config.lock().await.clone(); config_store::save(&config_path, &initial_config).expect("initial config should save"); - persist_pow_mining_config(&ui_config, &config_path, true, 7_500) + persist_pow_mining_config(&ui_config, &config_path, true) .await .unwrap(); let config = config_store::load_or_create(&config_path).unwrap(); assert!(config.pow_mining_enabled); - assert_eq!(config.mine_required_burn_multiplier_bps, 7_500); } #[test] diff --git a/src/app.rs b/src/app.rs @@ -14,10 +14,9 @@ use tokio::sync::Mutex; use crate::domain::{ Amount, BURN_CLAIM_SEEN_WINDOW_BLOCKS, Block, BurnSeen, ChainSnapshot, ChainStatus, - DEFAULT_FEE_PER_BYTE, DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS, DEFAULT_TRANSACTION_FEE, - Ledger, MAX_PENDING_TRANSACTIONS, MINE_FINALIZER_FEE, OutPoint, PreparedBlock, - StratumMineShare, StratumMineTemplate, Transaction, TransactionSubmitOutcome, - VDF_TARGET_BLOCK_MS, Wallet, hex_hash, run_vdf, + DEFAULT_FEE_PER_BYTE, DEFAULT_TRANSACTION_FEE, Ledger, MAX_PENDING_TRANSACTIONS, + MINE_FINALIZER_FEE, OutPoint, PreparedBlock, StratumMineShare, StratumMineTemplate, + Transaction, TransactionSubmitOutcome, VDF_TARGET_BLOCK_MS, Wallet, hex_hash, run_vdf, }; pub type SharedNode = Arc<Mutex<NodeCore>>; @@ -35,8 +34,6 @@ pub const PEER_MISBEHAVIOR_BAN_SCORE: u32 = 3; pub const PEER_MISBEHAVIOR_BAN_MS: u64 = 10 * 60 * 1_000; pub const PEER_CLOCK_OFFSET_ACCEPTANCE_MS: i64 = 10 * 60 * 1_000; const PEER_CLOCK_OFFSET_STALE_MS: u64 = 20 * 60 * 1_000; -const MIN_MINE_REQUIRED_BURN_MULTIPLIER_BPS: u16 = 1_000; -const MAX_MINE_REQUIRED_BURN_MULTIPLIER_BPS: u16 = 10_000; const AUTO_POW_NONCE_ATTEMPTS_PER_TICK: u64 = 8; static DEBUG_LOGGING: AtomicBool = AtomicBool::new(false); @@ -213,8 +210,6 @@ pub struct MiningStatus { pub burn_per_block: Amount, pub automatic_burn_fee: Amount, pub automatic_pow_mine_fee: Amount, - pub automatic_pow_required_burn_amount: Amount, - pub mine_required_burn_multiplier_bps: u16, pub last_auto_pow_mine_anchor: Option<String>, pub last_auto_pow_mine_status: Option<String>, pub vdf_rounds: u64, @@ -260,7 +255,6 @@ pub struct NodeCore { ledger: Ledger, automatic_mining_enabled: bool, pow_mining_enabled: bool, - mine_required_burn_multiplier_bps: u16, burn_per_block: Amount, burn_fee: Amount, last_auto_burn_height: Option<u64>, @@ -347,7 +341,6 @@ impl NodeCore { ledger, automatic_mining_enabled, pow_mining_enabled: false, - mine_required_burn_multiplier_bps: DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS, burn_per_block, burn_fee, last_auto_burn_height: None, @@ -566,8 +559,6 @@ impl NodeCore { burn_per_block: self.burn_per_block, automatic_burn_fee: self.burn_fee, automatic_pow_mine_fee: MINE_FINALIZER_FEE, - automatic_pow_required_burn_amount: self.pow_required_burn_amount(), - mine_required_burn_multiplier_bps: self.mine_required_burn_multiplier_bps, last_auto_pow_mine_anchor: self.last_auto_pow_mine_anchor.clone(), last_auto_pow_mine_status: if self.pow_mining_enabled && !self.has_real_chain() { Some("waiting for a real chain before PoW mining can start".to_string()) @@ -628,21 +619,6 @@ impl NodeCore { } } - pub fn set_pow_mining_settings(&mut self, enabled: bool, multiplier_bps: u16) -> Result<()> { - validate_mine_required_burn_multiplier(multiplier_bps)?; - self.pow_mining_enabled = enabled; - self.mine_required_burn_multiplier_bps = multiplier_bps; - self.auto_pow_mine_cursor = None; - if !enabled { - self.last_auto_pow_mine_anchor = None; - self.last_auto_pow_mine_status = None; - } else { - self.last_auto_pow_mine_status = - Some("waiting for next automatic PoW mining tick".to_string()); - } - Ok(()) - } - pub fn burn(&mut self, amount: Amount) -> Result<Transaction> { self.burn_with_fee(amount, 0) } @@ -740,7 +716,7 @@ impl NodeCore { } pub fn mine_pow_reward(&mut self) -> Result<Transaction> { - let (tx, _) = self.build_mine_with_required_burn_estimate()?; + let (tx, _) = self.build_mine_estimate()?; if self.ledger.submit_transaction(tx.clone())? { self.outbox.push(GossipEnvelope::Transaction(tx.clone())); } @@ -748,8 +724,7 @@ impl NodeCore { } pub fn estimate_mine_fee(&self, _fee_per_byte: Amount) -> Result<FeeEstimate> { - self.build_mine_with_required_burn_estimate() - .map(|(_, estimate)| estimate) + self.build_mine_estimate().map(|(_, estimate)| estimate) } pub fn external_mine_job( @@ -763,12 +738,9 @@ impl NodeCore { .last() .context("cannot build mine job without a chain tip")?; let difficulty_bits = self.ledger.current_mine_difficulty_bits(); - let required_burn_amount = - self.external_mine_required_burn_amount(&recipient, &tip.hash, difficulty_bits)?; Ok(ExternalMineJob { template: self.ledger.stratum_mine_template( recipient, - required_burn_amount, &tip.hash, salt, difficulty_bits, @@ -940,11 +912,8 @@ impl NodeCore { }) } - fn build_mine_with_required_burn_estimate(&self) -> Result<(Transaction, FeeEstimate)> { - let tx = self.ledger.build_mine_with_required_burn( - self.wallet.address(), - self.pow_required_burn_amount(), - )?; + fn build_mine_estimate(&self) -> Result<(Transaction, FeeEstimate)> { + let tx = self.ledger.build_mine(self.wallet.address())?; Ok(( tx.clone(), FeeEstimate { @@ -954,33 +923,6 @@ impl NodeCore { )) } - fn external_mine_required_burn_amount( - &self, - _recipient: &str, - _anchor: &str, - _difficulty_bits: u32, - ) -> Result<Amount> { - Ok(self.pow_required_burn_amount()) - } - - fn local_mine_required_burn_amount( - &self, - _recipient: &str, - _anchor: &str, - _salt: u64, - _difficulty_bits: u32, - _fee_per_byte: Amount, - ) -> Result<Amount> { - Ok(self.pow_required_burn_amount()) - } - - fn pow_required_burn_amount(&self) -> Amount { - self.ledger - .recommended_mine_required_burn_amount_with_multiplier( - self.mine_required_burn_multiplier_bps, - ) - } - pub fn mine_one(&mut self) -> Result<Block> { self.mine_one_at(now_ms()) } @@ -1202,7 +1144,6 @@ impl NodeCore { .map(|block| block.hash.clone()) .context("ledger has no anchor block")?; let wallet_address = self.wallet.address().to_string(); - let difficulty_bits = self.ledger.current_mine_difficulty_bits(); let needs_cursor = self .auto_pow_mine_cursor .as_ref() @@ -1220,16 +1161,8 @@ impl NodeCore { .as_ref() .context("automatic PoW cursor was not initialized")? .clone(); - let required_burn_amount = self.local_mine_required_burn_amount( - &wallet_address, - &anchor, - cursor.salt, - difficulty_bits, - MINE_FINALIZER_FEE, - )?; - let outcome = self.ledger.search_mine_with_required_burn( + let outcome = self.ledger.search_mine( wallet_address, - required_burn_amount, cursor.salt, cursor.next_nonce, AUTO_POW_NONCE_ATTEMPTS_PER_TICK, @@ -1965,15 +1898,6 @@ fn auto_pow_salt(wallet_address: &str, anchor: &str) -> u64 { u64::from_be_bytes(bytes) } -fn validate_mine_required_burn_multiplier(multiplier_bps: u16) -> Result<()> { - if !(MIN_MINE_REQUIRED_BURN_MULTIPLIER_BPS..=MAX_MINE_REQUIRED_BURN_MULTIPLIER_BPS) - .contains(&multiplier_bps) - { - bail!("mine required burn multiplier must be between 0.1 and 1.0"); - } - Ok(()) -} - fn converge_fee_by_byte( fee_per_byte: Amount, mut build: impl FnMut(Amount) -> Result<Transaction>, @@ -2037,8 +1961,8 @@ mod tests { use std::collections::BTreeMap; use crate::domain::{ - DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS, FinalizerMode, GenesisBurn, Ledger, MICRO_IUNA, - MINE_FINALIZER_FEE, RECOVERY_BLOCK_DELAY_MS, Transaction, Wallet, + FinalizerMode, GenesisBurn, Ledger, MICRO_IUNA, MINE_FINALIZER_FEE, + RECOVERY_BLOCK_DELAY_MS, Transaction, Wallet, }; use super::{GossipEnvelope, NodeConfig, NodeCore}; @@ -2097,7 +2021,6 @@ mod tests { let Transaction::Mine { anchor, recipient, - required_burn_amount, difficulty_bits, .. } = first_mine @@ -2106,7 +2029,6 @@ mod tests { }; assert_eq!(anchor, &node.chain().last().unwrap().hash); assert_eq!(recipient, wallet.address()); - assert!(*required_burn_amount >= crate::domain::MIN_MINE_REQUIRED_BURN); assert_eq!( *difficulty_bits, node.ledger().current_mine_difficulty_bits() @@ -2227,8 +2149,7 @@ mod tests { burn_fee: 0, }); - node.set_pow_mining_settings(true, DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS) - .unwrap(); + node.set_pow_mining_enabled(true); let plan = (1..10_000) .map(|timestamp| node.prepare_automatic_mining(timestamp)) .find(|plan| plan.pow_mined.is_some()) @@ -2236,10 +2157,7 @@ mod tests { let mine = plan.pow_mined.expect("PoW should be queued"); assert_eq!(mine.fee(), MINE_FINALIZER_FEE); - assert_eq!( - mine.amount(), - node.status().mining.automatic_pow_required_burn_amount - ); + assert_eq!(mine.amount(), crate::domain::MINE_REWARD); assert_eq!( node.status().mining.automatic_pow_mine_fee, MINE_FINALIZER_FEE @@ -2247,31 +2165,6 @@ mod tests { } #[test] - fn pow_required_burn_multiplier_must_stay_in_configured_range() { - let wallet = Wallet::from_seed("pow-required-burn-multiplier-wallet"); - let mut node = NodeCore::new(NodeConfig { - wallet, - genesis_allocations: BTreeMap::new(), - vdf_rounds: 10, - burn_per_block: 0, - burn_fee: 0, - }); - - assert!(node.set_pow_mining_settings(true, 999).is_err()); - node.set_pow_mining_settings(true, 1_000).unwrap(); - assert_eq!( - node.status().mining.mine_required_burn_multiplier_bps, - 1_000 - ); - node.set_pow_mining_settings(true, 10_000).unwrap(); - assert_eq!( - node.status().mining.mine_required_burn_multiplier_bps, - 10_000 - ); - assert!(node.set_pow_mining_settings(true, 10_001).is_err()); - } - - #[test] fn status_reports_package_version() { let wallet = Wallet::from_seed("status-version-wallet"); let node = NodeCore::new(NodeConfig { diff --git a/src/domain.rs b/src/domain.rs @@ -13,8 +13,6 @@ pub const MICRO_IUNA: Amount = 1_000_000; pub const BLOCK_REWARD: Amount = 100 * MICRO_IUNA; pub const MINE_REWARD: Amount = MICRO_IUNA; pub const MINE_FINALIZER_FEE: Amount = MICRO_IUNA; -pub const MIN_MINE_REQUIRED_BURN: Amount = MICRO_IUNA / 10; -pub const MAX_MINE_REQUIRED_BURN: Amount = MINE_REWARD; pub const DEFAULT_MINE_FEE: Amount = MINE_FINALIZER_FEE; pub const DEFAULT_TRANSACTION_FEE: Amount = MICRO_IUNA; pub const DEFAULT_FEE_PER_BYTE: Amount = 1; @@ -32,8 +30,6 @@ const MINE_MAX_RETARGET_STEP_BITS: u32 = 2; const MINE_MIN_DIFFICULTY_BITS: u32 = 1; const MINE_MAX_DIFFICULTY_BITS: u32 = 32; const MINE_MAX_ANCHOR_AGE_BLOCKS: u64 = MINE_RETARGET_WINDOW_BLOCKS; -pub const DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS: u16 = 8_000; -pub const MINE_REQUIRED_BURN_WINDOW_BLOCKS: usize = 10; pub const MAX_PENDING_TRANSACTIONS: usize = 10_000; const MAX_ORPHAN_TRANSACTIONS: usize = 1_024; const MAX_BLOCK_TRANSACTIONS: usize = 1_000; @@ -157,7 +153,6 @@ pub enum Transaction { }, Mine { recipient: String, - required_burn_amount: Amount, anchor: String, #[serde(default)] salt: u64, @@ -294,10 +289,7 @@ impl Transaction { outputs.first().map(|output| output.amount).unwrap_or(0) } Self::Burn { amount, .. } => *amount, - Self::Mine { - required_burn_amount, - .. - } => *required_burn_amount, + Self::Mine { .. } => MINE_REWARD, Self::BurnClaim { .. } => 0, } } @@ -331,13 +323,6 @@ impl Transaction { matches!(self, Self::Burn { .. }) } - fn burn_amount(&self) -> Amount { - match self { - Self::Burn { amount, .. } => *amount, - Self::Transfer { .. } | Self::Mine { .. } | Self::BurnClaim { .. } => 0, - } - } - pub fn canonical(&self) -> String { format!("{}:{}", self.signing_payload(), self.signature()) } @@ -380,20 +365,12 @@ impl Transaction { .canonical(), Self::Mine { recipient, - required_burn_amount, anchor, salt, nonce, difficulty_bits, .. - } => mine_payload( - recipient, - *required_burn_amount, - anchor, - *salt, - *nonce, - *difficulty_bits, - ), + } => mine_payload(recipient, anchor, *salt, *nonce, *difficulty_bits), Self::BurnClaim { burn, seen, .. } => burn_claim_payload(burn, seen), } } @@ -401,7 +378,6 @@ impl Transaction { fn verify_signature(&self) -> Result<()> { if let Self::Mine { recipient, - required_burn_amount, anchor, salt, nonce, @@ -411,28 +387,15 @@ impl Transaction { } = self { let expected = if let Some(proof_header) = proof_header { - let header = stratum_mine_header_bytes( - recipient, - *required_burn_amount, - anchor, - *salt, - *nonce, - *difficulty_bits, - )?; + let header = + stratum_mine_header_bytes(recipient, anchor, *salt, *nonce, *difficulty_bits)?; let expected_header = hex_encode(header); if *proof_header != expected_header { bail!("mine transaction proof header is invalid"); } stratum_mine_signature(&header) } else { - mine_signature( - recipient, - *required_burn_amount, - anchor, - *salt, - *nonce, - *difficulty_bits, - ) + mine_signature(recipient, anchor, *salt, *nonce, *difficulty_bits) }; if *signature != expected { bail!("mine transaction proof hash is invalid"); @@ -492,13 +455,9 @@ impl Transaction { match self { Self::Transfer { outputs, .. } => outputs.clone(), Self::Burn { change, .. } => change.clone(), - Self::Mine { - recipient, - required_burn_amount, - .. - } => vec![TxOutput { + Self::Mine { recipient, .. } => vec![TxOutput { address: recipient.clone(), - amount: *required_burn_amount, + amount: MINE_REWARD, }], Self::BurnClaim { .. } => Vec::new(), } @@ -639,20 +598,16 @@ fn canonical_outputs(outputs: &[TxOutput]) -> String { fn mine_payload( recipient: &str, - required_burn_amount: Amount, anchor: &str, salt: u64, nonce: u64, difficulty_bits: u32, ) -> String { - format!( - "iuna-mine:{recipient}:{required_burn_amount}:{anchor}:{salt}:{nonce}:{difficulty_bits}" - ) + format!("iuna-mine:{recipient}:{anchor}:{salt}:{nonce}:{difficulty_bits}") } fn mine_signature( recipient: &str, - required_burn_amount: Amount, anchor: &str, salt: u64, nonce: u64, @@ -660,7 +615,6 @@ fn mine_signature( ) -> String { hex_hash(mine_payload( recipient, - required_burn_amount, anchor, salt, nonce, @@ -719,7 +673,6 @@ const STRATUM_MINE_NTIME: [u8; 4] = [0, 0, 0, 0]; #[derive(Clone, Debug, Eq, PartialEq)] pub struct StratumMineTemplate { pub recipient: String, - pub required_burn_amount: Amount, pub anchor: String, pub salt: u64, pub difficulty_bits: u32, @@ -757,33 +710,22 @@ fn unpack_stratum_nonce(nonce: u64) -> ([u8; 4], [u8; 4]) { fn stratum_coinbase_prefix( recipient: &str, - required_burn_amount: Amount, anchor: &str, salt: u64, difficulty_bits: u32, ) -> Vec<u8> { - format!( - "iuna-stratum-mine:{recipient}:{required_burn_amount}:{anchor}:{salt}:{difficulty_bits}:" - ) - .into_bytes() + format!("iuna-stratum-mine:{recipient}:{anchor}:{salt}:{difficulty_bits}:").into_bytes() } fn stratum_coinbase_bytes( recipient: &str, - required_burn_amount: Amount, anchor: &str, salt: u64, nonce: u64, difficulty_bits: u32, ) -> Vec<u8> { let (extranonce2, _) = unpack_stratum_nonce(nonce); - let mut coinbase = stratum_coinbase_prefix( - recipient, - required_burn_amount, - anchor, - salt, - difficulty_bits, - ); + let mut coinbase = stratum_coinbase_prefix(recipient, anchor, salt, difficulty_bits); coinbase.extend_from_slice(&[0, 0, 0, 0]); coinbase.extend_from_slice(&extranonce2); coinbase @@ -797,7 +739,6 @@ fn double_sha256(bytes: &[u8]) -> [u8; 32] { fn stratum_mine_header_bytes( recipient: &str, - required_burn_amount: Amount, anchor: &str, salt: u64, nonce: u64, @@ -810,7 +751,6 @@ fn stratum_mine_header_bytes( header[4..36].copy_from_slice(&anchor_bytes); let merkle_root = double_sha256(&stratum_coinbase_bytes( recipient, - required_burn_amount, anchor, salt, nonce, @@ -832,7 +772,6 @@ fn stratum_mine_signature(header: &[u8; 80]) -> String { fn stratum_mine_template( recipient: impl Into<String>, - required_burn_amount: Amount, anchor: &str, salt: u64, difficulty_bits: u32, @@ -840,22 +779,14 @@ fn stratum_mine_template( let recipient = recipient.into(); validate_address(&recipient, "mine recipient")?; validate_hash(anchor, "mine transaction anchor")?; - validate_mine_required_burn_amount(required_burn_amount)?; let anchor_bytes = decode_hex_array::<HASH_BYTES>(anchor).context("mine transaction anchor is not hex")?; Ok(StratumMineTemplate { recipient: recipient.clone(), - required_burn_amount, anchor: anchor.to_string(), salt, difficulty_bits, - coinbase_prefix: stratum_coinbase_prefix( - &recipient, - required_burn_amount, - anchor, - salt, - difficulty_bits, - ), + coinbase_prefix: stratum_coinbase_prefix(&recipient, anchor, salt, difficulty_bits), version_hex: hex_encode(STRATUM_MINE_VERSION), prev_hash_hex: hex_encode(anchor_bytes), nbits_hex: hex_encode(difficulty_bits.to_le_bytes()), @@ -1793,34 +1724,6 @@ impl Ledger { self.mine_difficulty_bits_for_anchor_height(self.tip().height) } - pub fn recommended_mine_required_burn_amount(&self) -> Amount { - self.recommended_mine_required_burn_amount_with_multiplier( - DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS, - ) - } - - pub fn recommended_mine_required_burn_amount_with_multiplier( - &self, - multiplier_bps: u16, - ) -> Amount { - let Some(min_burn) = self - .chain - .iter() - .rev() - .filter(|block| block.height > 0) - .take(MINE_REQUIRED_BURN_WINDOW_BLOCKS) - .map(block_burn_amount) - .min() - else { - return MIN_MINE_REQUIRED_BURN; - }; - let scaled = u128::from(min_burn) - .saturating_mul(u128::from(multiplier_bps)) - .saturating_div(10_000) - .min(u128::from(Amount::MAX)) as Amount; - scaled.clamp(MIN_MINE_REQUIRED_BURN, MAX_MINE_REQUIRED_BURN) - } - pub fn mine_difficulty_bits_at_height(&self, height: u64) -> u32 { self.mine_difficulty_bits_for_anchor_height(height.min(self.tip().height)) } @@ -1970,35 +1873,18 @@ impl Ledger { } pub fn build_mine(&self, recipient: impl Into<String>) -> Result<Transaction> { - self.build_mine_with_required_burn(recipient, self.recommended_mine_required_burn_amount()) - } - - pub fn build_mine_with_required_burn( - &self, - recipient: impl Into<String>, - required_burn_amount: Amount, - ) -> Result<Transaction> { let recipient = recipient.into(); validate_address(&recipient, "mine recipient")?; - validate_mine_required_burn_amount(required_burn_amount)?; let anchor = self.tip().hash.clone(); let salt = 1; let difficulty_bits = self.current_mine_difficulty_bits(); for nonce in 0..u64::MAX { - let signature = mine_signature( - &recipient, - required_burn_amount, - &anchor, - salt, - nonce, - difficulty_bits, - ); + let signature = mine_signature(&recipient, &anchor, salt, nonce, difficulty_bits); if !hash_meets_difficulty(&signature, difficulty_bits) { continue; } let transaction = Transaction::Mine { recipient: recipient.clone(), - required_burn_amount, anchor: anchor.clone(), salt, nonce, @@ -2015,36 +1901,26 @@ impl Ledger { bail!("could not find valid mine proof"); } - pub fn search_mine_with_required_burn( + pub fn search_mine( &self, recipient: impl Into<String>, - required_burn_amount: Amount, salt: u64, start_nonce: u64, max_attempts: u64, ) -> Result<MineSearchOutcome> { let recipient = recipient.into(); validate_address(&recipient, "mine recipient")?; - validate_mine_required_burn_amount(required_burn_amount)?; let anchor = self.tip().hash.clone(); let difficulty_bits = self.current_mine_difficulty_bits(); let mut attempts = 0_u64; let mut nonce = start_nonce; while attempts < max_attempts { - let signature = mine_signature( - &recipient, - required_burn_amount, - &anchor, - salt, - nonce, - difficulty_bits, - ); + let signature = mine_signature(&recipient, &anchor, salt, nonce, difficulty_bits); attempts = attempts.saturating_add(1); let next_nonce = nonce.checked_add(1).unwrap_or(0); if hash_meets_difficulty(&signature, difficulty_bits) { let transaction = Transaction::Mine { recipient: recipient.clone(), - required_burn_amount, anchor: anchor.clone(), salt, nonce, @@ -2073,18 +1949,11 @@ impl Ledger { pub fn stratum_mine_template( &self, recipient: impl Into<String>, - required_burn_amount: Amount, anchor: impl AsRef<str>, salt: u64, difficulty_bits: u32, ) -> Result<StratumMineTemplate> { - stratum_mine_template( - recipient, - required_burn_amount, - anchor.as_ref(), - salt, - difficulty_bits, - ) + stratum_mine_template(recipient, anchor.as_ref(), salt, difficulty_bits) } pub fn build_stratum_mine( @@ -2095,7 +1964,6 @@ impl Ledger { let nonce = pack_stratum_nonce(share.extranonce2, share.header_nonce); let header = stratum_mine_header_bytes( &template.recipient, - template.required_burn_amount, &template.anchor, template.salt, nonce, @@ -2103,7 +1971,6 @@ impl Ledger { )?; let transaction = Transaction::Mine { recipient: template.recipient, - required_burn_amount: template.required_burn_amount, anchor: template.anchor, salt: template.salt, nonce, @@ -2179,7 +2046,6 @@ impl Ledger { let transactions = self.select_block_transactions()?; ensure_block_has_burn(&transactions)?; - ensure_mine_actions_have_required_burns(&transactions)?; let tip = self.tip(); let prev_hash = tip.hash.clone(); @@ -2220,7 +2086,6 @@ impl Ledger { let transactions = self.select_recovery_block_transactions(miner)?; ensure_block_has_burn(&transactions)?; ensure_block_has_burn_from(&transactions, miner)?; - ensure_mine_actions_have_required_burns(&transactions)?; let tip = self.tip(); let prev_hash = tip.hash.clone(); @@ -2386,7 +2251,6 @@ impl Ledger { bail!("block exceeds max block size"); } ensure_block_has_burn(&block.transactions)?; - ensure_mine_actions_have_required_burns(&block.transactions)?; self.ensure_due_burn_claims_are_included(block.height, &block.transactions)?; match block.finalizer_mode { FinalizerMode::Ticket => { @@ -2515,7 +2379,6 @@ impl Ledger { let mut utxos = self.utxos.clone(); let mut remaining = self.valid_pending_transactions(); let mut selected = Vec::new(); - let mut selected_burn_amount = 0_u64; for tx in self.due_required_claimed_burns(self.tip().height + 1)? { remaining.retain(|pending| pending.signature() != tx.signature()); @@ -2525,9 +2388,6 @@ impl Ledger { bail!("due claimed burns exceed max block size"); } apply_transaction(&tx, &mut utxos)?; - selected_burn_amount = selected_burn_amount - .checked_add(tx.burn_amount()) - .context("selected block burns overflow")?; selected.push(tx); } @@ -2549,21 +2409,13 @@ impl Ledger { candidate.push(tx.clone()); if estimated_block_size_bytes(&candidate)? <= self.launch_profile.max_block_bytes { apply_transaction(&tx, &mut utxos)?; - selected_burn_amount = selected_burn_amount - .checked_add(tx.burn_amount()) - .context("selected block burns overflow")?; selected.push(tx); } } } while selected.len() < self.launch_profile.max_block_transactions { - let Some(index) = best_selectable_transaction_index_for_burn_amount( - &remaining, - &utxos, - None, - selected_burn_amount, - ) else { + let Some(index) = best_selectable_transaction_index(&remaining, &utxos, None) else { break; }; let tx = remaining.remove(index); @@ -2571,9 +2423,6 @@ impl Ledger { candidate.push(tx.clone()); if estimated_block_size_bytes(&candidate)? <= self.launch_profile.max_block_bytes { apply_transaction(&tx, &mut utxos)?; - selected_burn_amount = selected_burn_amount - .checked_add(tx.burn_amount()) - .context("selected block burns overflow")?; selected.push(tx); } } @@ -2752,7 +2601,6 @@ impl Ledger { } Transaction::Mine { recipient, - required_burn_amount, anchor, difficulty_bits, proof_header, @@ -2760,7 +2608,6 @@ impl Ledger { .. } => { validate_address(recipient, "mine recipient")?; - validate_mine_required_burn_amount(*required_burn_amount)?; validate_hash(anchor, "mine transaction anchor")?; validate_hash(signature, "mine transaction proof hash")?; if let Some(proof_header) = proof_header { @@ -3279,38 +3126,6 @@ fn ensure_block_has_burn_from(transactions: &[Transaction], miner: &str) -> Resu Ok(()) } -fn block_burn_amount(block: &Block) -> Amount { - transactions_burn_amount(&block.transactions).unwrap_or(Amount::MAX) -} - -fn transactions_burn_amount(transactions: &[Transaction]) -> Result<Amount> { - transactions.iter().try_fold(0_u64, |total, tx| { - total - .checked_add(tx.burn_amount()) - .context("burns overflow") - }) -} - -fn ensure_mine_actions_have_required_burns(transactions: &[Transaction]) -> Result<()> { - let burn_amount = transactions_burn_amount(transactions)?; - for transaction in transactions { - if let Transaction::Mine { - required_burn_amount, - .. - } = transaction - { - if burn_amount < *required_burn_amount { - bail!( - "mine transaction requires {} burn but block only includes {}", - required_burn_amount, - burn_amount - ); - } - } - } - Ok(()) -} - fn ensure_valid_recovery_block(block: &Block, parent: &Block) -> Result<()> { if block.finalizer_rank != 0 { bail!("recovery block finalizer rank must be 0"); @@ -3338,23 +3153,13 @@ fn best_selectable_transaction_index( utxos: &BTreeMap<OutPoint, TxOutput>, required_kind: Option<TransactionKind>, ) -> Option<usize> { - best_selectable_transaction_index_for_burn_amount( - transactions, - utxos, - required_kind, - Amount::MAX, - ) -} - -fn best_selectable_burn_from_index( - transactions: &[Transaction], - utxos: &BTreeMap<OutPoint, TxOutput>, - owner: &str, -) -> Option<usize> { transactions .iter() .enumerate() - .filter(|(_, tx)| tx.is_burn() && tx.sender() == owner) + .filter(|(_, tx)| match required_kind { + Some(TransactionKind::Burn) => tx.is_burn(), + None => true, + }) .filter(|(_, tx)| { let mut utxos = utxos.clone(); apply_transaction(tx, &mut utxos).is_ok() @@ -3363,33 +3168,21 @@ fn best_selectable_burn_from_index( fee_rate_key(left) .cmp(&fee_rate_key(right)) .then_with(|| left.fee().cmp(&right.fee())) + .then_with(|| left.is_burn().cmp(&right.is_burn())) .then_with(|| right.signature().cmp(left.signature())) }) .map(|(index, _)| index) } -fn best_selectable_transaction_index_for_burn_amount( +fn best_selectable_burn_from_index( transactions: &[Transaction], utxos: &BTreeMap<OutPoint, TxOutput>, - required_kind: Option<TransactionKind>, - selected_burn_amount: Amount, + owner: &str, ) -> Option<usize> { transactions .iter() .enumerate() - .filter(|(_, tx)| match required_kind { - Some(TransactionKind::Burn) => tx.is_burn(), - None => true, - }) - .filter(|(_, tx)| { - !matches!( - tx, - Transaction::Mine { - required_burn_amount, - .. - } if *required_burn_amount > selected_burn_amount - ) - }) + .filter(|(_, tx)| tx.is_burn() && tx.sender() == owner) .filter(|(_, tx)| { let mut utxos = utxos.clone(); apply_transaction(tx, &mut utxos).is_ok() @@ -3398,7 +3191,6 @@ fn best_selectable_transaction_index_for_burn_amount( fee_rate_key(left) .cmp(&fee_rate_key(right)) .then_with(|| left.fee().cmp(&right.fee())) - .then_with(|| left.is_burn().cmp(&right.is_burn())) .then_with(|| right.signature().cmp(left.signature())) }) .map(|(index, _)| index) @@ -3464,17 +3256,6 @@ fn validate_hash(hash: &str, label: &str) -> Result<()> { Ok(()) } -fn validate_mine_required_burn_amount(amount: Amount) -> Result<()> { - if !(MIN_MINE_REQUIRED_BURN..=MAX_MINE_REQUIRED_BURN).contains(&amount) { - bail!( - "mine required burn amount must be between {} and {}", - MIN_MINE_REQUIRED_BURN, - MAX_MINE_REQUIRED_BURN - ); - } - Ok(()) -} - fn validate_signature(signature: &str, label: &str) -> Result<()> { decode_hex_array::<SIGNATURE_BYTES>(signature).with_context(|| format!("invalid {label}"))?; Ok(()) @@ -3526,7 +3307,6 @@ fn canonical_transaction_size_bytes(transaction: &Transaction) -> usize { } Transaction::Mine { recipient, - required_burn_amount, anchor, salt, nonce, @@ -3535,7 +3315,6 @@ fn canonical_transaction_size_bytes(transaction: &Transaction) -> usize { signature, } => { 1 + address_size_bytes(recipient) - + compact_len(u128::from(*required_burn_amount)) + hash_size_bytes(anchor) + compact_len(u128::from(*salt)) + compact_len(u128::from(*nonce)) @@ -3708,14 +3487,10 @@ fn apply_transaction( ) -> Result<()> { transaction.verify_signature()?; match transaction { - Transaction::Mine { - recipient, - required_burn_amount, - .. - } => { + Transaction::Mine { recipient, .. } => { let output = TxOutput { address: recipient.clone(), - amount: *required_burn_amount, + amount: MINE_REWARD, }; ensure_outputs_do_not_overflow(utxos, std::slice::from_ref(&output))?; utxos.insert( @@ -4286,27 +4061,17 @@ mod tests { block } - fn mine_with_required_burn( - ledger: &Ledger, - recipient: &str, - required_burn_amount: Amount, - ) -> Transaction { + const TEST_BURN_AMOUNT: Amount = MICRO_IUNA / 10; + + fn unsigned_mine(ledger: &Ledger, recipient: &str) -> Transaction { let anchor = ledger.tip().hash.clone(); let difficulty_bits = ledger.current_mine_difficulty_bits(); for nonce in 0..u64::MAX { let salt = 1; - let signature = mine_signature( - recipient, - required_burn_amount, - &anchor, - salt, - nonce, - difficulty_bits, - ); + let signature = mine_signature(recipient, &anchor, salt, nonce, difficulty_bits); if hash_meets_difficulty(&signature, difficulty_bits) { return Transaction::Mine { recipient: recipient.to_string(), - required_burn_amount, anchor, salt, nonce, @@ -4364,9 +4129,7 @@ mod tests { }) }) .expect("expected an eligible finalizer wallet"); - let burn = ledger - .build_burn(wallet, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); + let burn = ledger.build_burn(wallet, TEST_BURN_AMOUNT, 0).unwrap(); ledger.submit_transaction(burn).unwrap(); let prepared = ledger .prepare_next_block(wallet.address(), ledger.tip().timestamp_ms + 1) @@ -4604,9 +4367,7 @@ mod tests { let alice = Wallet::from_seed("bounded-mine-search-alice"); let ledger = Ledger::new(BTreeMap::new(), 1); - let outcome = ledger - .search_mine_with_required_burn(alice.address(), MIN_MINE_REQUIRED_BURN, 1, 0, 0) - .unwrap(); + let outcome = ledger.search_mine(alice.address(), 1, 0, 0).unwrap(); assert!(outcome.transaction.is_none()); assert_eq!(outcome.next_nonce, 0); @@ -5002,32 +4763,13 @@ mod tests { } #[test] - fn mine_required_burn_amount_must_be_in_protocol_range() { - let alice = Wallet::from_seed("mine-required-burn-range-alice"); - let ledger = ledger_with_allocation(&alice, MICRO_IUNA); - - let low = ledger - .build_mine_with_required_burn(alice.address(), MIN_MINE_REQUIRED_BURN - 1) - .unwrap_err(); - let high = ledger - .build_mine_with_required_burn(alice.address(), MAX_MINE_REQUIRED_BURN + 1) - .unwrap_err(); - - assert!(format!("{low:#}").contains("required burn amount")); - assert!(format!("{high:#}").contains("required burn amount")); - } - - #[test] - fn mine_required_burn_amount_is_bound_to_proof_hash() { - let alice = Wallet::from_seed("mine-required-burn-proof-alice"); + fn mine_recipient_is_bound_to_proof_hash() { + let alice = Wallet::from_seed("mine-proof-alice"); + let bob = Wallet::from_seed("mine-proof-bob"); let mut ledger = ledger_with_allocation(&alice, MICRO_IUNA); - let mut forged = mine_with_required_burn(&ledger, alice.address(), MIN_MINE_REQUIRED_BURN); - if let Transaction::Mine { - required_burn_amount, - .. - } = &mut forged - { - *required_burn_amount = required_burn_amount.saturating_add(1); + let mut forged = unsigned_mine(&ledger, alice.address()); + if let Transaction::Mine { recipient, .. } = &mut forged { + *recipient = bob.address().to_string(); } let error = ledger.submit_transaction(forged).unwrap_err(); @@ -5036,69 +4778,18 @@ mod tests { } #[test] - fn mine_action_uses_required_burn_reward_and_fixed_finalizer_fee() { - let alice = Wallet::from_seed("mine-required-burn-reward-alice"); + fn mine_action_uses_fixed_reward_and_fixed_finalizer_fee() { + let alice = Wallet::from_seed("mine-fixed-reward-alice"); let mut ledger = ledger_with_allocation(&alice, MICRO_IUNA); - let mine = ledger - .build_mine_with_required_burn(alice.address(), MIN_MINE_REQUIRED_BURN) - .unwrap(); + let mine = ledger.build_mine(alice.address()).unwrap(); - assert_eq!(mine.amount(), MIN_MINE_REQUIRED_BURN); + assert_eq!(mine.amount(), MINE_REWARD); assert_eq!(mine.fee(), MINE_FINALIZER_FEE); assert!(ledger.submit_transaction(mine).unwrap()); } #[test] - fn recommended_mine_required_burn_uses_multiplier_times_recent_minimum() { - let alice = Wallet::from_seed("mine-required-burn-policy-alice"); - let mut ledger = ledger_with_allocation(&alice, 10 * MICRO_IUNA); - - assert_eq!( - ledger.recommended_mine_required_burn_amount_with_multiplier(8_000), - MIN_MINE_REQUIRED_BURN - ); - - for amount in [MICRO_IUNA, MICRO_IUNA / 2, 3 * MICRO_IUNA / 2] { - let burn = ledger.build_burn(&alice, amount, 0).unwrap(); - ledger.submit_transaction(burn).unwrap(); - let block = ledger.mine_next_block(&alice, ledger.height() + 1).unwrap(); - ledger.apply_locally_mined_block(block).unwrap(); - } - - assert_eq!( - ledger.recommended_mine_required_burn_amount_with_multiplier(8_000), - 400_000 - ); - assert_eq!( - ledger.recommended_mine_required_burn_amount_with_multiplier(100), - MIN_MINE_REQUIRED_BURN - ); - assert_eq!( - ledger.recommended_mine_required_burn_amount_with_multiplier(10_000), - MICRO_IUNA / 2 - ); - } - - #[test] - fn recommended_mine_required_burn_uses_last_ten_non_genesis_blocks() { - let alice = Wallet::from_seed("mine-required-burn-window-alice"); - let mut ledger = ledger_with_allocation(&alice, 20 * MICRO_IUNA); - - for amount in std::iter::once(MIN_MINE_REQUIRED_BURN).chain([MICRO_IUNA; 10]) { - let burn = ledger.build_burn(&alice, amount, 0).unwrap(); - ledger.submit_transaction(burn).unwrap(); - let block = ledger.mine_next_block(&alice, ledger.height() + 1).unwrap(); - ledger.apply_locally_mined_block(block).unwrap(); - } - - assert_eq!( - ledger.recommended_mine_required_burn_amount_with_multiplier(10_000), - MICRO_IUNA - ); - } - - #[test] fn burn_claim_requires_recent_finalizer_quorum() { let finalizers = (0..5) .map(|index| Wallet::from_seed(&format!("burn-claim-quorum-finalizer-{index}"))) @@ -5108,9 +4799,7 @@ mod tests { mine_until_recent_finalizers(&mut ledger, &finalizers, BURN_CLAIM_SEEN_QUORUM); assert!(recent_unique_finalizer_count(&ledger) >= BURN_CLAIM_SEEN_QUORUM); - let burn = ledger - .build_burn(&burner, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); + let burn = ledger.build_burn(&burner, TEST_BURN_AMOUNT, 0).unwrap(); let seen = recent_burn_seen_attestations(&ledger, &finalizers, &burn, BURN_CLAIM_SEEN_QUORUM - 1); @@ -5128,9 +4817,7 @@ mod tests { let mut ledger = ledger_with_finalizers_and_burner(&finalizers, &burner); mine_until_recent_finalizers(&mut ledger, &finalizers, BURN_CLAIM_SEEN_QUORUM); - let burn = ledger - .build_burn(&burner, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); + let burn = ledger.build_burn(&burner, TEST_BURN_AMOUNT, 0).unwrap(); let mut seen = recent_burn_seen_attestations(&ledger, &finalizers, &burn, BURN_CLAIM_SEEN_QUORUM); seen[0].signature = "00".repeat(SIGNATURE_BYTES); @@ -5149,9 +4836,7 @@ mod tests { let burner = Wallet::from_seed("burn-claim-due-burner"); let mut ledger = ledger_with_finalizers_and_burner(&finalizers, &burner); mine_until_recent_finalizers(&mut ledger, &finalizers, BURN_CLAIM_SEEN_QUORUM); - let burn = ledger - .build_burn(&burner, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); + let burn = ledger.build_burn(&burner, TEST_BURN_AMOUNT, 0).unwrap(); let burn_signature = burn.signature().to_string(); confirm_burn_claim(&mut ledger, &finalizers, burn); @@ -5162,9 +4847,7 @@ mod tests { let leader = ledger.expected_leader_for_next_block().unwrap(); let wallet = wallet_for_address(&finalizers, &leader); - let filler_burn = ledger - .build_burn(wallet, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); + let filler_burn = ledger.build_burn(wallet, TEST_BURN_AMOUNT, 0).unwrap(); ledger.submit_transaction(filler_burn.clone()).unwrap(); let mut prepared = ledger .prepare_next_block(wallet.address(), ledger.tip().timestamp_ms + 1) @@ -5202,9 +4885,7 @@ mod tests { let burner = Wallet::from_seed("burn-claim-select-burner"); let mut ledger = ledger_with_finalizers_and_burner(&finalizers, &burner); mine_until_recent_finalizers(&mut ledger, &finalizers, BURN_CLAIM_SEEN_QUORUM); - let burn = ledger - .build_burn(&burner, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); + let burn = ledger.build_burn(&burner, TEST_BURN_AMOUNT, 0).unwrap(); let burn_signature = burn.signature().to_string(); confirm_burn_claim(&mut ledger, &finalizers, burn); @@ -5236,9 +4917,7 @@ mod tests { let recipient = Wallet::from_seed("burn-claim-invalid-recipient"); let mut ledger = ledger_with_finalizers_and_burner(&finalizers, &burner); mine_until_recent_finalizers(&mut ledger, &finalizers, BURN_CLAIM_SEEN_QUORUM); - let burn = ledger - .build_burn(&burner, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); + let burn = ledger.build_burn(&burner, TEST_BURN_AMOUNT, 0).unwrap(); let burn_signature = burn.signature().to_string(); let burn_input = burn.inputs().first().unwrap().outpoint.clone(); @@ -5255,9 +4934,7 @@ mod tests { let leader = ledger.expected_leader_for_next_block().unwrap(); let wallet = wallet_for_address(&finalizers, &leader); - let filler_burn = ledger - .build_burn(wallet, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); + let filler_burn = ledger.build_burn(wallet, TEST_BURN_AMOUNT, 0).unwrap(); ledger.submit_transaction(filler_burn).unwrap(); let block = ledger .prepare_next_block(wallet.address(), ledger.tip().timestamp_ms + 1) @@ -5274,47 +4951,20 @@ mod tests { } #[test] - fn block_with_mine_action_requires_enough_burn_amount() { - let alice = Wallet::from_seed("mine-required-burn-block-alice"); - let mut ledger = ledger_with_allocation(&alice, 10 * MICRO_IUNA); - - let burn = ledger - .build_burn(&alice, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); - ledger.submit_transaction(burn.clone()).unwrap(); - let mine = ledger - .build_mine_with_required_burn(alice.address(), MAX_MINE_REQUIRED_BURN) - .unwrap(); - let mut prepared = ledger.prepare_next_block(alice.address(), 1).unwrap(); - prepared.reward = prepared.reward.checked_add(mine.fee()).unwrap(); - prepared.transactions.push(mine); - let vdf_output = run_vdf(prepared.vdf_seed(), prepared.vdf_rounds()); - let block = prepared.finish(&alice, vdf_output); - - let error = ledger.apply_block(block).unwrap_err(); - - assert!(format!("{error:#}").contains("requires")); - } - - #[test] - fn block_selection_includes_mine_action_when_cumulative_burns_meet_requirement() { - let alice = Wallet::from_seed("mine-cumulative-burn-alice"); + fn block_selection_includes_mine_action_after_required_block_burn() { + let alice = Wallet::from_seed("mine-fixed-reward-select-alice"); let mut ledger = ledger_with_allocation(&alice, 10 * MICRO_IUNA); - for _ in 0..2 { - let burn = ledger.build_burn(&alice, MICRO_IUNA / 2, 0).unwrap(); - ledger.submit_transaction(burn).unwrap(); - } - let mine = ledger - .build_mine_with_required_burn(alice.address(), MICRO_IUNA) - .unwrap(); + let burn = ledger.build_burn(&alice, TEST_BURN_AMOUNT, 0).unwrap(); + ledger.submit_transaction(burn).unwrap(); + let mine = ledger.build_mine(alice.address()).unwrap(); ledger.submit_transaction(mine.clone()).unwrap(); let block = ledger.mine_next_block(&alice, 1).unwrap(); assert_eq!( block.transactions.iter().filter(|tx| tx.is_burn()).count(), - 2 + 1 ); assert!( block @@ -5346,45 +4996,12 @@ mod tests { } #[test] - fn block_selection_keeps_mine_action_pending_when_required_burn_is_not_met() { - let alice = Wallet::from_seed("mine-required-burn-pending-alice"); - let mut ledger = ledger_with_allocation(&alice, 10 * MICRO_IUNA); - - let burn = ledger - .build_burn(&alice, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); - ledger.submit_transaction(burn).unwrap(); - let mine = ledger - .build_mine_with_required_burn(alice.address(), MAX_MINE_REQUIRED_BURN) - .unwrap(); - ledger.submit_transaction(mine.clone()).unwrap(); - - let block = ledger.mine_next_block(&alice, 1).unwrap(); - - assert!(block.transactions.iter().any(Transaction::is_burn)); - assert!( - !block - .transactions - .iter() - .any(|tx| tx.signature() == mine.signature()) - ); - assert!( - ledger - .pending() - .iter() - .any(|tx| tx.signature() == mine.signature()) - ); - } - - #[test] fn pending_mine_outputs_are_not_spendable_until_confirmed() { let alice = Wallet::from_seed("pending-mine-spend-alice"); let bob = Wallet::from_seed("pending-mine-spend-bob"); let mut ledger = ledger_with_allocation(&alice, 10 * MICRO_IUNA); - let mine = ledger - .build_mine_with_required_burn(alice.address(), MIN_MINE_REQUIRED_BURN) - .unwrap(); + let mine = ledger.build_mine(alice.address()).unwrap(); let mine_outpoint = OutPoint { txid: mine.signature().to_string(), index: 0, @@ -5402,16 +5019,14 @@ mod tests { .build_transfer_with_inputs( &alice, bob.address(), - MIN_MINE_REQUIRED_BURN, + TEST_BURN_AMOUNT, 0, std::slice::from_ref(&mine_outpoint), ) .unwrap_err(); assert!(format!("{pending_error:#}").contains("not spendable")); - let burn = ledger - .build_burn(&alice, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); + let burn = ledger.build_burn(&alice, TEST_BURN_AMOUNT, 0).unwrap(); ledger.submit_transaction(burn).unwrap(); let block = ledger.mine_next_block(&alice, 1).unwrap(); assert!( @@ -5433,7 +5048,7 @@ mod tests { .build_transfer_with_inputs( &alice, bob.address(), - MIN_MINE_REQUIRED_BURN, + TEST_BURN_AMOUNT, 0, std::slice::from_ref(&mine_outpoint), ) @@ -5445,18 +5060,14 @@ mod tests { let alice = Wallet::from_seed("pending-mine-burn-alice"); let mut ledger = ledger_with_allocation(&alice, 10 * MICRO_IUNA); - let mine = ledger - .build_mine_with_required_burn(alice.address(), MIN_MINE_REQUIRED_BURN) - .unwrap(); + let mine = ledger.build_mine(alice.address()).unwrap(); let mine_outpoint = OutPoint { txid: mine.signature().to_string(), index: 0, }; ledger.submit_transaction(mine).unwrap(); - let burn = ledger - .build_burn(&alice, MIN_MINE_REQUIRED_BURN, 0) - .unwrap(); + let burn = ledger.build_burn(&alice, TEST_BURN_AMOUNT, 0).unwrap(); let Transaction::Burn { inputs, .. } = &burn else { panic!("expected burn transaction"); @@ -5577,13 +5188,7 @@ mod tests { let anchor = ledger.tip().hash.clone(); let difficulty_bits = ledger.current_mine_difficulty_bits(); let template = ledger - .stratum_mine_template( - alice.address(), - MINE_FINALIZER_FEE, - anchor, - 1, - difficulty_bits, - ) + .stratum_mine_template(alice.address(), anchor, 1, difficulty_bits) .unwrap(); let mut accepted = None; @@ -5623,13 +5228,7 @@ mod tests { for salt in [1, 2] { let template = ledger - .stratum_mine_template( - alice.address(), - MINE_FINALIZER_FEE, - anchor.clone(), - salt, - difficulty_bits, - ) + .stratum_mine_template(alice.address(), anchor.clone(), salt, difficulty_bits) .unwrap(); let mut accepted = None; for nonce in 0_u32..50_000 { diff --git a/src/main.rs b/src/main.rs @@ -87,10 +87,7 @@ async fn main() -> Result<()> { initial_burn_fee, ), }; - node_core.set_pow_mining_settings( - ui_config.pow_mining_enabled, - ui_config.mine_required_burn_multiplier_bps, - )?; + node_core.set_pow_mining_enabled(ui_config.pow_mining_enabled); let node: SharedNode = Arc::new(Mutex::new(node_core)); let ui_config = Arc::new(Mutex::new(ui_config)); let mut peers = ui_config.lock().await.peers.clone(); diff --git a/tests/iuna.rs b/tests/iuna.rs @@ -10,10 +10,9 @@ use iuna::{ PeerDirection, TRANSACTION_BATCH_LIMIT, }, domain::{ - Amount, BLOCK_REWARD, DEFAULT_FEE_PER_BYTE, DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS, - FinalizerMode, GenesisBurn, Ledger, MAX_BLOCK_BYTES, MICRO_IUNA, MIN_MINE_REQUIRED_BURN, - RECOVERY_BLOCK_DELAY_MS, TransactionSubmitOutcome, VDF_TARGET_BLOCK_MS, Wallet, run_vdf, - verify_vdf, + Amount, BLOCK_REWARD, DEFAULT_FEE_PER_BYTE, FinalizerMode, GenesisBurn, Ledger, + MAX_BLOCK_BYTES, MICRO_IUNA, MINE_REWARD, RECOVERY_BLOCK_DELAY_MS, + TransactionSubmitOutcome, VDF_TARGET_BLOCK_MS, Wallet, run_vdf, verify_vdf, }, }; use tempfile::tempdir; @@ -307,7 +306,7 @@ fn block_with_forged_transaction_is_rejected() { } #[test] -fn mine_action_uses_required_burn_reward_and_finalizer_fee() { +fn mine_action_uses_fixed_reward_and_finalizer_fee() { let alice = Wallet::from_seed("alice"); let mut allocations = BTreeMap::new(); allocations.insert(alice.address().to_string(), 2 * MICRO_IUNA); @@ -315,7 +314,7 @@ fn mine_action_uses_required_burn_reward_and_finalizer_fee() { let mut ledger = Ledger::new(allocations, 10); submit_burn(&mut ledger, &alice, MICRO_IUNA); let mine = ledger.build_mine(alice.address()).unwrap(); - assert_eq!(mine.amount(), MIN_MINE_REQUIRED_BURN); + assert_eq!(mine.amount(), MINE_REWARD); assert_eq!(mine.fee(), MICRO_IUNA); ledger.submit_transaction(mine.clone()).unwrap(); let block = ledger.mine_next_block(&alice, 1).unwrap(); @@ -324,12 +323,12 @@ fn mine_action_uses_required_burn_reward_and_finalizer_fee() { ledger.apply_block(block).unwrap(); assert_eq!( ledger.balance_of(alice.address()), - 2 * MICRO_IUNA + MIN_MINE_REQUIRED_BURN + 2 * MICRO_IUNA + MINE_REWARD ); assert!(!ledger.submit_transaction(mine).unwrap()); assert_eq!( ledger.balance_of(alice.address()), - 2 * MICRO_IUNA + MIN_MINE_REQUIRED_BURN + 2 * MICRO_IUNA + MINE_REWARD ); } @@ -342,7 +341,7 @@ fn mine_action_protocol_fee_is_paid_to_block_finalizer() { let mut ledger = Ledger::new(allocations, 10); let mine = ledger.build_mine(alice.address()).unwrap(); - assert_eq!(mine.amount(), MIN_MINE_REQUIRED_BURN); + assert_eq!(mine.amount(), MINE_REWARD); assert_eq!(mine.fee(), MICRO_IUNA); ledger.submit_transaction(mine).unwrap(); submit_burn(&mut ledger, &bob, MICRO_IUNA); @@ -351,7 +350,7 @@ fn mine_action_protocol_fee_is_paid_to_block_finalizer() { assert_eq!(block.reward, MICRO_IUNA); ledger.apply_block(block).unwrap(); - assert_eq!(ledger.balance_of(alice.address()), MIN_MINE_REQUIRED_BURN); + assert_eq!(ledger.balance_of(alice.address()), MINE_REWARD); assert_eq!(ledger.balance_of(bob.address()), 2 * MICRO_IUNA); } @@ -827,9 +826,7 @@ fn pow_only_node_gossips_mine_action_to_pob_only_finalizer() { DEFAULT_BURN_PER_BLOCK, DEFAULT_FEE_PER_BYTE, ); - bob_node - .set_pow_mining_settings(true, DEFAULT_MINE_REQUIRED_BURN_MULTIPLIER_BPS) - .unwrap(); + bob_node.set_pow_mining_enabled(true); let bob_plan = (1..10_000) .map(|timestamp| bob_node.prepare_automatic_mining(timestamp)) diff --git a/tests/properties.rs b/tests/properties.rs @@ -3,8 +3,9 @@ use std::collections::{BTreeMap, BTreeSet}; use iuna::{ app::{InMemoryNetwork, NodeCore}, domain::{ - Amount, ChainSnapshot, GenesisBurn, Ledger, MICRO_IUNA, MINE_FINALIZER_FEE, OutPoint, - Transaction, TxInput, TxOutput, VDF_TARGET_BLOCK_MS, Wallet, hex_hash, verify_vdf, + Amount, ChainSnapshot, GenesisBurn, Ledger, MICRO_IUNA, MINE_FINALIZER_FEE, MINE_REWARD, + OutPoint, Transaction, TxInput, TxOutput, VDF_TARGET_BLOCK_MS, Wallet, hex_hash, + verify_vdf, }, }; @@ -166,12 +167,9 @@ fn expected_confirmed_supply(snapshot: &ChainSnapshot) -> Amount { supply = supply.checked_sub(*amount).expect("burn is funded"); supply = supply.checked_sub(*fee).expect("burn fee is funded"); } - Transaction::Mine { - required_burn_amount, - .. - } => { + Transaction::Mine { .. } => { supply = supply - .checked_add(*required_burn_amount) + .checked_add(MINE_REWARD) .expect("mine output does not overflow supply"); } Transaction::BurnClaim { .. } => {} @@ -264,15 +262,10 @@ fn apply_reference_transaction( transaction: &Transaction, utxos: &mut BTreeMap<OutPoint, TxOutput>, ) { - if let Transaction::Mine { - recipient, - required_burn_amount, - .. - } = transaction - { + if let Transaction::Mine { recipient, .. } = transaction { let output = TxOutput { address: recipient.clone(), - amount: *required_burn_amount, + amount: MINE_REWARD, }; assert_eq!(transaction.fee(), MINE_FINALIZER_FEE); insert_reference_outputs(transaction, &[output], utxos); @@ -345,13 +338,9 @@ fn reference_outputs(transaction: &Transaction) -> Vec<TxOutput> { match transaction { Transaction::Transfer { outputs, .. } => outputs.clone(), Transaction::Burn { change, .. } => change.clone(), - Transaction::Mine { - recipient, - required_burn_amount, - .. - } => vec![TxOutput { + Transaction::Mine { recipient, .. } => vec![TxOutput { address: recipient.clone(), - amount: *required_burn_amount, + amount: MINE_REWARD, }], Transaction::BurnClaim { .. } => Vec::new(), } diff --git a/www/assets/iuna-ui.js b/www/assets/iuna-ui.js @@ -65,9 +65,6 @@ window.iunaApp = function iunaApp() { burnFeeDraft: "0.0001", miningEnabled: false, powMiningEnabled: false, - powRequiredBurnMultiplier: 0.8, - powRequiredBurnMultiplierDraft: "0.8", - powRequiredBurnMultiplierDirty: false, burnAmountDirty: false, transferTo: "", transferAmount: null, @@ -545,15 +542,10 @@ window.iunaApp = function iunaApp() { this.burnFee = status.mining?.automatic_burn_fee ?? this.burnFee; this.miningEnabled = status.mining?.automatic ?? this.miningEnabled; this.powMiningEnabled = status.mining?.pow_mining_enabled ?? this.powMiningEnabled; - this.powRequiredBurnMultiplier = - (status.mining?.mine_required_burn_multiplier_bps ?? Math.round(this.powRequiredBurnMultiplier * 10000)) / 10000; if (!this.burnAmountDirty) { this.burnAmountDraft = this.amountLabel(this.burnAmount); this.burnFeeDraft = this.amountLabel(this.burnFee); } - if (!this.powRequiredBurnMultiplierDirty) { - this.powRequiredBurnMultiplierDraft = this.powRequiredBurnMultiplier.toString(); - } this.lastUpdated = new Date(); this.scheduleFeeEstimates(); } catch (error) { @@ -944,9 +936,7 @@ window.iunaApp = function iunaApp() { }, async refreshMineFeeEstimate() { - this.feeEstimates.mine = await this.fetchFeeEstimate("/api/fee-estimate/mine", { - enabled: this.powMiningEnabled, - }); + this.feeEstimates.mine = await this.fetchFeeEstimate("/api/fee-estimate/mine", {}); }, async refreshTransferFeeEstimate() { @@ -1040,41 +1030,19 @@ window.iunaApp = function iunaApp() { async setPowMiningEnabled(enabled) { const previous = this.powMiningEnabled; - const multiplier = this.powRequiredBurnMultiplierValue(); try { this.powMiningEnabled = enabled; await this.postForm( "/api/settings/pow-mining", - { enabled, multiplier_bps: Math.round(multiplier * 10000) }, + { enabled }, enabled ? "PoW mining turned on" : "PoW mining turned off" ); - this.powRequiredBurnMultiplier = multiplier; - this.powRequiredBurnMultiplierDraft = multiplier.toString(); - this.powRequiredBurnMultiplierDirty = false; } catch (error) { this.powMiningEnabled = previous; this.showFlash(error.message, "error"); } }, - async savePowMining() { - const multiplier = this.powRequiredBurnMultiplierValue(); - try { - await this.postForm( - "/api/settings/pow-mining", - { enabled: this.powMiningEnabled, multiplier_bps: Math.round(multiplier * 10000) }, - this.powMiningEnabled - ? "PoW mining settings saved" - : `Mine settings saved while off` - ); - this.powRequiredBurnMultiplier = multiplier; - this.powRequiredBurnMultiplierDraft = multiplier.toString(); - this.powRequiredBurnMultiplierDirty = false; - } catch (error) { - this.showFlash(error.message, "error"); - } - }, - async setKeepTrackOfMetrics(enabled) { const previous = this.keepTrackOfMetrics; try { @@ -1133,64 +1101,8 @@ window.iunaApp = function iunaApp() { return this.parseiunaAmount(this.burnFeeDraft); }, - powRequiredBurnMultiplierValue() { - const value = Number(this.powRequiredBurnMultiplierDraft); - if (!Number.isFinite(value)) return this.powRequiredBurnMultiplier; - return Math.min(1, Math.max(0.1, value)); - }, - - powRequiredBurnMultiplierBps() { - return Math.round(this.powRequiredBurnMultiplierValue() * 10000); - }, - - powMineRewardLimit() { - return Math.max(100000, Math.trunc(Number(this.status.chain?.mine_reward ?? 1000000))); - }, - - powMineRecentBurnTotals() { - return this.blocks - .filter((block) => block.height > 0) - .slice(0, 10) - .map((block) => this.blockBurnAmount(block)); - }, - - powMineRecentBurnMin() { - const totals = this.powMineRecentBurnTotals(); - if (totals.length === 0) return null; - return Math.min(...totals); - }, - - powMineRequiredBurn() { - const current = Math.max(0, Math.trunc(Number(this.status.mining?.automatic_pow_required_burn_amount ?? 0))); - if (!this.powRequiredBurnMultiplierDirty) return current; - const minimum = 100000; - const maximum = this.powMineRewardLimit(); - const recentMin = this.powMineRecentBurnMin(); - const base = recentMin === null ? maximum : recentMin; - return Math.min(maximum, Math.max(minimum, Math.round(base * this.powRequiredBurnMultiplierValue()))); - }, - - powMineNetReward() { - return this.powMineRequiredBurn(); - }, - - powMineRecentBurnMax() { - const totals = this.powMineRecentBurnTotals(); - if (totals.length === 0) return null; - return Math.max(...totals); - }, - - powMineIncludeStatus() { - const required = this.powMineRequiredBurn(); - const recentMax = this.powMineRecentBurnMax(); - if (recentMax === null) { - return { kind: "muted", label: "Waiting for burn history", recent: "" }; - } - const recent = `Recent high: IUNA ${this.amountLabel(recentMax)}`; - if (recentMax >= required) { - return { kind: "ready", label: "Recent blocks can include this", recent }; - } - return { kind: "waiting", label: "May wait for bigger burn blocks", recent }; + powMineReward() { + return Math.max(0, Math.trunc(Number(this.status.chain?.mine_reward ?? 1000000))); }, autoPowStatusLabel() {