Skip to content

Commit 6f1d183

Browse files
PeterGRutherfordumweltclaude
authored
feat(lib-blockchain): Add DifficultyConfig struct for adaptive diffic… (#652)
* feat(lib-blockchain): Add DifficultyConfig struct for adaptive difficulty adjustment - Add DifficultyConfig struct with governance-controlled parameters - Fields: target_timespan, adjustment_interval, min/max_adjustment_factor, last_updated_at_height - Default values: 14 days, 2016 blocks, 4x max adjustment (Bitcoin-style) - Add difficulty_config field to Blockchain struct - Initialize with defaults in Blockchain::new() - Implement serialization/deserialization for persistence - Add get_difficulty_config() getter method - Add comprehensive unit tests for validation and serialization - Ensure backward compatibility with existing genesis blocks - Document in lib-blockchain/docs/architecture.md Resolves #605 * fix(lib-blockchain): Improve DifficultyConfig naming and add governance setter - Rename min/max_adjustment_factor to max_decrease/increase_factor for clarity - Add tracing warning when adjustment_interval is zero in target_block_time() - Add adjust_difficulty_with_config() that uses DifficultyConfig parameters - Add set_difficulty_config() setter with validation for governance updates - Update documentation and add test for new adjust function Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: Remove useless port validation on u16 type * fix: Remove redundant DifficultyConfig re-export * fix: Avoid unnecessary cloning of DifficultyConfig in difficulty getter - Add get_difficulty_target_timespan() to retrieve specific field without cloning entire config - Update blockchain.rs to use the new method instead of cloning whole DifficultyConfig - Add documentation to get_difficulty_config() noting that it clones (intended for cases needing full config) - Keep set_difficulty_config() for governance updates as already implemented * fix: Make with_params validate parameters and return Result - Changed DifficultyConfig::with_params() to return Result<Self, String> - Validates all parameters before creating the configuration - Prevents creation of invalid configs that would fail later - Added test_difficulty_config_with_params_invalid() to test validation - Updated existing test to use .expect() for valid parameters * refactor: Rename difficulty adjustment fields for semantic clarity - Renamed max_decrease_factor to max_difficulty_decrease_factor - Renamed max_increase_factor to max_difficulty_increase_factor - Updated all usages: struct fields, with_params, validate, clamp_timespan - Updated all tests to use new field names - Updated blockchain.rs logging to use new field names These names make it explicit that the fields control difficulty changes, not timespan directly, eliminating confusion in clamp_timespan docs and impl. Tests verify all 7 difficulty config tests still pass. --------- Co-authored-by: Hugo <[email protected]> Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent d941f14 commit 6f1d183

File tree

6 files changed

+388
-26
lines changed

6 files changed

+388
-26
lines changed

lib-blockchain/docs/architecture.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,33 @@ Web4 Architecture:
328328
└─────────────────────────────────────────────────┘
329329
```
330330

331+
## Consensus Architecture
332+
333+
### Adaptive Difficulty Adjustment
334+
335+
The blockchain uses governance-controlled difficulty parameters through `DifficultyConfig`:
336+
337+
- **Configurable Parameters**: Target timespan, adjustment interval, and adjustment factors
338+
- **Default Settings**: 14-day timespan, 2016-block interval, 4x max adjustment (Bitcoin-style)
339+
- **Governance Updates**: Parameters can be modified via DAO proposals using `set_difficulty_config()`
340+
- **Audit Trail**: `last_updated_at_height` tracks parameter changes
341+
342+
```rust
343+
pub struct DifficultyConfig {
344+
pub target_timespan: u64, // Default: 14 days (1,209,600 seconds)
345+
pub adjustment_interval: u64, // Default: 2016 blocks
346+
pub max_decrease_factor: u64, // Default: 4 (difficulty can decrease by 4x max)
347+
pub max_increase_factor: u64, // Default: 4 (difficulty can increase by 4x max)
348+
pub last_updated_at_height: u64,
349+
}
350+
```
351+
352+
**Key Methods:**
353+
- `target_block_time()` - Calculates target seconds per block (default: 600s)
354+
- `clamp_timespan()` - Prevents extreme difficulty changes
355+
- `validate()` - Ensures parameters are within safe ranges
356+
- `adjust_difficulty_with_config()` - Calculates new difficulty using config parameters
357+
331358
## Economic Architecture
332359

333360
### Universal Basic Income System

lib-blockchain/src/blockchain.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::collections::{HashMap, HashSet};
77
use anyhow::Result;
88
use serde::{Serialize, Deserialize};
99
use tracing::{info, warn, error, debug};
10-
use crate::types::{Hash, Difficulty};
10+
use crate::types::{Hash, Difficulty, DifficultyConfig};
1111
use crate::transaction::{Transaction, TransactionInput, TransactionOutput, IdentityTransactionData};
1212
use crate::types::transaction_type::TransactionType;
1313
use crate::block::Block;
@@ -40,6 +40,8 @@ pub struct Blockchain {
4040
pub height: u64,
4141
/// Current mining difficulty
4242
pub difficulty: Difficulty,
43+
/// Difficulty adjustment configuration (governance-controlled)
44+
pub difficulty_config: DifficultyConfig,
4345
/// Total work done (cumulative difficulty)
4446
pub total_work: u128,
4547
/// UTXO set for transaction validation
@@ -162,6 +164,7 @@ impl Blockchain {
162164
blocks: vec![genesis_block.clone()],
163165
height: 0,
164166
difficulty: Difficulty::from_bits(crate::INITIAL_DIFFICULTY),
167+
difficulty_config: DifficultyConfig::default(),
165168
total_work: 0,
166169
utxo_set: HashMap::new(),
167170
nullifier_set: HashSet::new(),
@@ -798,8 +801,8 @@ impl Blockchain {
798801
tokio::runtime::Handle::current().block_on(async {
799802
let coord = coordinator.read().await;
800803
let interval = coord.get_difficulty_adjustment_interval().await;
801-
let config = coord.get_difficulty_config().await;
802-
(interval, config.target_timespan)
804+
let target_timespan = coord.get_difficulty_target_timespan().await;
805+
(interval, target_timespan)
803806
})
804807
})
805808
} else {
@@ -913,6 +916,33 @@ impl Blockchain {
913916
self.height
914917
}
915918

919+
/// Get the current difficulty configuration
920+
pub fn get_difficulty_config(&self) -> &DifficultyConfig {
921+
&self.difficulty_config
922+
}
923+
924+
/// Update the difficulty configuration (for governance updates)
925+
///
926+
/// This method validates the new configuration before applying it.
927+
/// The `last_updated_at_height` field will be set to the current blockchain height.
928+
///
929+
/// # Errors
930+
/// Returns an error if the configuration parameters are invalid.
931+
pub fn set_difficulty_config(&mut self, mut config: DifficultyConfig) -> Result<()> {
932+
config.validate().map_err(|e| anyhow::anyhow!("Invalid difficulty config: {}", e))?;
933+
config.last_updated_at_height = self.height;
934+
info!(
935+
"Updating difficulty config at height {}: target_timespan={}, adjustment_interval={}, max_decrease={}, max_increase={}",
936+
self.height,
937+
config.target_timespan,
938+
config.adjustment_interval,
939+
config.max_difficulty_decrease_factor,
940+
config.max_difficulty_increase_factor
941+
);
942+
self.difficulty_config = config;
943+
Ok(())
944+
}
945+
916946
/// Check if a nullifier has been used
917947
pub fn is_nullifier_used(&self, nullifier: &Hash) -> bool {
918948
self.nullifier_set.contains(nullifier)

lib-blockchain/src/integration/consensus_integration.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,11 +260,21 @@ impl BlockchainConsensusCoordinator {
260260
}
261261

262262
/// Get the current difficulty configuration
263+
///
264+
/// **Note**: This clones the entire DifficultyConfig. For better performance,
265+
/// use specific field getters like `get_difficulty_target_timespan()` when you
266+
/// only need individual fields.
263267
pub async fn get_difficulty_config(&self) -> DifficultyConfig {
264268
let manager = self.difficulty_manager.read().await;
265269
manager.config().clone()
266270
}
267-
271+
272+
/// Get the target timespan for difficulty adjustment without cloning the entire config
273+
pub async fn get_difficulty_target_timespan(&self) -> u64 {
274+
let manager = self.difficulty_manager.read().await;
275+
manager.target_timespan()
276+
}
277+
268278
/// Calculate new difficulty using the consensus-owned algorithm
269279
///
270280
/// This is the entry point for blockchain difficulty adjustment.

0 commit comments

Comments
 (0)