Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5bcf071
memorized lately received transactions to avoid requesting them again…
SurfingNerd Nov 3, 2025
72088bc
Merge pull request #305 from DMDcoin/main
SurfingNerd Nov 3, 2025
76c15ad
Merge pull request #307 from SurfingNerd/i304-lately-received-transac…
SurfingNerd Nov 3, 2025
2c14545
error handling for malicious keys
SurfingNerd Nov 3, 2025
dbf3491
Merge pull request #308 from SurfingNerd/i100-key-gen-panic
SurfingNerd Nov 3, 2025
de99d16
improved cleanup of unfetched_pooled_transactions, depending witch co…
SurfingNerd Nov 4, 2025
442bd01
Merge pull request #310 from SurfingNerd/i304-lately-received-transac…
SurfingNerd Nov 21, 2025
d8713b1
Merge pull request #315 from SurfingNerd/i243-no-reports-on-missing-b…
SurfingNerd Nov 23, 2025
87998dc
Merge pull request #319 from DMDcoin/main
SurfingNerd Nov 24, 2025
14129bf
increased gracefull client shutdown from 5 second to 15 seconds.
SurfingNerd Nov 27, 2025
f93430a
Node shutdown, if a validator becomes a regular node:
SurfingNerd Nov 29, 2025
a27cba2
Error handling for autoshutdown of ex validators https://github.com/D…
SurfingNerd Dec 1, 2025
1150219
Merge pull request #324 from SurfingNerd/i322-autoshutdown-of-ex-vali…
SurfingNerd Dec 1, 2025
b117008
4.0.2-rc1: release notes and version update
SurfingNerd Dec 1, 2025
56a232f
Merge pull request #326 from DMDcoin/dev
SurfingNerd Dec 1, 2025
3e7bc8f
removed empty blocks during key gen phase: https://github.com/DMDcoin…
SurfingNerd Dec 9, 2025
02cb0a8
Remove empty blocks during key gen phases behaviour
SurfingNerd Dec 9, 2025
6e30091
reduced log level of early-epoch-end: HbbftEarlyEpochEndManager created.
SurfingNerd Dec 9, 2025
f76a089
fixed unused import
SurfingNerd Dec 9, 2025
593bcfc
implememtation of gc. https://github.com/DMDcoin/diamond-node/issues/172
SurfingNerd Dec 9, 2025
ec3ed17
removed unused fragment for https://github.com/DMDcoin/diamond-node/i…
SurfingNerd Dec 10, 2025
9af27de
fixed test for https://github.com/DMDcoin/diamond-node/issues/327 (re…
SurfingNerd Dec 10, 2025
8e84bdb
rpc gas price: always use latest gas price: https://github.com/DMDcoi…
SurfingNerd Dec 10, 2025
55b5f65
avoid GC of service transactions twice within the same block.
SurfingNerd Dec 10, 2025
9aa3a25
updated release notes for 4.0.2
SurfingNerd Dec 10, 2025
10c2ecb
Merge pull request #328 from SurfingNerd/i172-service-transactions-ga…
SurfingNerd Dec 10, 2025
d95481f
fixes for garbage collect of inactive service transactions + log leve…
SurfingNerd Dec 10, 2025
9b10f1c
log level for collect garbage
SurfingNerd Dec 10, 2025
2ec4b25
Merge pull request #329 from SurfingNerd/i172-service-transactions-ga…
SurfingNerd Dec 10, 2025
80fbdf8
FIXED not staked nodes write log entries about service transactions. …
SurfingNerd Dec 10, 2025
a3cc2b9
version update and changelog
SurfingNerd Dec 10, 2025
5d14f0c
Merge pull request #330 from SurfingNerd/i172-service-transactions-ga…
SurfingNerd Dec 10, 2025
7cde9bd
cargo fmt --all -- --config imports_granularity=Crate
SurfingNerd Dec 10, 2025
09a3a2a
Merge pull request #331 from SurfingNerd/i172-service-transactions-ga…
SurfingNerd Dec 10, 2025
236d549
cleanup of comments
SurfingNerd Dec 12, 2025
58dfe2b
fixed typos in comments
SurfingNerd Dec 12, 2025
b1eecb9
version Update: 4.0.2-rc3 -> 4.0.2
SurfingNerd Dec 12, 2025
5e6de72
Merge pull request #333 from SurfingNerd/4.0-release
SurfingNerd Dec 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
## Diamond Node Software 4.0.2


### New behavior for validator nodes
- [Autoshutdown if a Node becomes a regular Node](https://github.com/DMDcoin/diamond-node/issues/322)
- [Remove empty blocks during key gen phases behaviour](https://github.com/DMDcoin/diamond-node/issues/327)
- [Service Transaction cleanup (garbage collect)](https://github.com/DMDcoin/diamond-node/issues/172)

### RPC
- [Gas price from contracts](https://github.com/DMDcoin/diamond-node/issues/159)

### Further improvements
- [FIXED: received transactions are getting pooled, if announced by another peer](https://github.com/DMDcoin/diamond-node/issues/304)
- [FIXED: dropped transactions are getting pooled](https://github.com/DMDcoin/diamond-node/issues/303)
- [FIXED: key generation can panic if faulty validators write malicious parts](https://github.com/DMDcoin/diamond-node/issues/100)
- [FIXED: already included transactions are refretched from peers](https://github.com/DMDcoin/diamond-node/issues/196)
- [FIXED:not staked nodes write log entries about service transactions](https://github.com/DMDcoin/diamond-node/issues/323)
- [Gracefull Node Shutdown: increase to 15 seconds](https://github.com/DMDcoin/diamond-node/issues/321)


## Diamond Node Software 4.0.1

First hotfix
OPTIONAL: First hotfix
Mitigates the transaction spam caused by flaws in the transaction management of report disconnectivity transactions.

- [Reduce Intervals for connectivity checks](https://github.com/DMDcoin/diamond-node/issues/313)
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
description = "Diamond Node"
name = "diamond-node"
# NOTE Make sure to update util/version/Cargo.toml as well
version = "4.0.1"
version = "4.0.2"
license = "GPL-3.0"
authors = [
"bit.diamonds developers",
Expand Down
2 changes: 1 addition & 1 deletion bin/oe/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,7 @@ impl RunningClient {
.name("diamond-node-force-quit".to_string())
.spawn(move || {

let duration_soft = 5;
let duration_soft = 15;
// we make a force quit if after 90 seconds, if this shutdown routine
std::thread::sleep(Duration::from_secs(duration_soft));
warn!(target: "shutdown", "shutdown not happened within {duration_soft} seconds, starting force exiting the process.");
Expand Down
13 changes: 13 additions & 0 deletions crates/concensus/miner/src/pool/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ use crate::pool::{
verifier, PendingOrdering, PendingSettings, PrioritizationStrategy,
};

use super::VerifiedTransaction;

type Listener = (
LocalTransactionsList,
(listener::Notifier, listener::Logger),
Expand Down Expand Up @@ -413,6 +415,17 @@ impl TransactionQueue {
.collect()
}

/// Performs garbage collection on the pool of this `TransactionQueue` for free service transactions.
/// Removes transactions that are not valid anymore.
/// The process executes listener calls.
pub fn garbage_collect<F: Fn(&VerifiedTransaction) -> bool>(
&self,
service_transaction_check: F,
) {
let mut pool = self.pool.write();
pool.garbage_collect(service_transaction_check);
}

/// Computes unordered set of pending hashes.
///
/// Since strict nonce-checking is not required, you may get some false positive future transactions as well.
Expand Down
4 changes: 3 additions & 1 deletion crates/ethcore/service/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ impl IoHandler<ClientIoMessage> for ClientIoHandler {
ClientIoMessage::Execute(ref exec) => {
(*exec.0)(&self.client);
}
_ => {} // ignore other messages
ClientIoMessage::NewChainHead => {
self.client.garbage_collect_in_queue();
}
}
}
}
Expand Down
53 changes: 53 additions & 0 deletions crates/ethcore/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,10 @@ pub struct Client {

shutdown: Arc<ShutdownManager>,

/// block number and block hash of latest gc.
/// this information is used to avoid double garbage collection.
garbage_collect_latest_block: Mutex<(u64, H256)>,

statistics: ClientStatistics,
}

Expand Down Expand Up @@ -842,6 +846,8 @@ impl Importer {
warn!("Failed to prune ancient state data: {}", e);
}

client.schedule_garbage_collect_in_queue();

route
}

Expand Down Expand Up @@ -1107,6 +1113,7 @@ impl Client {
importer,
config,
shutdown,
garbage_collect_latest_block: Mutex::new((0, H256::zero())),
statistics,
});

Expand Down Expand Up @@ -1540,6 +1547,52 @@ impl Client {
}
}

/// Schedule garbage collection of invalid service transactions from the transaction queue based on the given block hash.
pub fn schedule_garbage_collect_in_queue(&self) {
let m = ClientIoMessage::execute(|c| c.garbage_collect_in_queue());
if let Err(e) = self.io_channel.read().send(m) {
error!(target: "client", "Failed to schedule garbage collection in transaction queue for block {:?}", e);
}
}

/// Garbage collect invalid service transactions from the transaction queue based on the given block header.
pub fn garbage_collect_in_queue(&self) {
let machine = self.engine().machine();

match &self.block_header_decoded(BlockId::Latest) {
Some(block_header) => {
{
// scope for mutex.
let mut last_gc = self.garbage_collect_latest_block.lock();

if block_header.number() == last_gc.0 && block_header.hash() == last_gc.1 {
// already gced for this block, or gc is ongoing.
// we can return here.
return;
}

// we treat ongoing gc as DONE, to avoid blocking of the message channel
last_gc.0 = block_header.number();
last_gc.1 = block_header.hash();
}

// here hides an accepted race condition.
// latest block could change during long ongoing GCs.
// this could be avoided developing a more complex GC logic.
// but the GC blocks the tx queue, so it has to be blazing fast.
self.importer.miner.collect_garbage(|tx|
match machine.verify_transaction(tx.signed(), block_header, self) {
Ok(_) => true,
Err(e) => {
trace!(target: "client", "collected garbage transaction from {:?}: {:?} reason: {:?}", tx.signed().sender(), tx.signed().hash, e);
false
},
});
}
None => {}
}
}

fn check_garbage(&self) {
self.chain.read().collect_garbage();
self.importer.block_queue.collect_garbage();
Expand Down
23 changes: 18 additions & 5 deletions crates/ethcore/src/engines/hbbft/contracts/keygen_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,25 @@ pub fn acks_of_address(
if serialized_ack.is_empty() {
return Err(CallError::ReturnValueInvalid);
}
let deserialized_ack: Ack = bincode::deserialize(&serialized_ack).unwrap();
let outcome = skg
.handle_ack(vmap.get(&address).unwrap(), deserialized_ack)
.unwrap();
let deserialized_ack: Ack = match bincode::deserialize(&serialized_ack) {
Ok(ack) => ack,
Err(e) => {
error!(target: "engine", "Failed to deserialize Ack #{} for address {}: {:?}", n, address, e);
return Err(CallError::ReturnValueInvalid);
}
};

let outcome = match skg.handle_ack(vmap.get(&address).unwrap(), deserialized_ack) {
Ok(s) => s,
Err(e) => {
error!(target: "engine", "Failed to handle Ack #{} for address {}: {:?}", n, address, e);
return Err(CallError::ReturnValueInvalid);
}
};

if let AckOutcome::Invalid(fault) = outcome {
panic!("Expected Ack Outcome to be valid. {:?}", fault);
error!(target: "engine", "Invalid Ack Outcome for #{} for address {}: {:?}", n, address, fault);
return Err(CallError::ReturnValueInvalid);
}
}

Expand Down
5 changes: 0 additions & 5 deletions crates/ethcore/src/engines/hbbft/contracts/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ pub fn get_posdao_epoch_start(
call_const_staking!(c, staking_epoch_start_block)
}

pub fn start_time_of_next_phase_transition(client: &dyn EngineClient) -> Result<U256, CallError> {
let c = BoundContract::bind(client, BlockId::Latest, *STAKING_CONTRACT_ADDRESS);
call_const_staking!(c, start_time_of_next_phase_transition)
}

pub fn candidate_min_stake(client: &dyn EngineClient) -> Result<U256, CallError> {
let c = BoundContract::bind(client, BlockId::Latest, *STAKING_CONTRACT_ADDRESS);
call_const_staking!(c, candidate_min_stake)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl HbbftEarlyEpochEndManager {
signing_address: signing_address.clone(),
};

info!(target: "engine", "early-epoch-end: HbbftEarlyEpochEndManager created. start_time {now:?}, start_block: {epoch_start_block}");
trace!(target: "engine", "early-epoch-end: HbbftEarlyEpochEndManager created. start_time {now:?}, start_block: {epoch_start_block}");

return Some(result);
}
Expand Down
37 changes: 6 additions & 31 deletions crates/ethcore/src/engines/hbbft/hbbft_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ use super::{
NodeId,
contracts::{
keygen_history::{all_parts_acks_available, initialize_synckeygen},
staking::start_time_of_next_phase_transition,
validator_set::{ValidatorType, get_pending_validators, is_pending_validator},
},
contribution::{unix_now_millis, unix_now_secs},
Expand Down Expand Up @@ -329,9 +328,6 @@ impl TransitionHandler {

// If the minimum block time has passed we are ready to trigger new blocks.
if timer_duration == Duration::from_secs(0) {
// Always create blocks if we are in the keygen phase.
self.engine.start_hbbft_epoch_if_next_phase();

// If the maximum block time has been reached we trigger a new block in any case.
if self.max_block_time_remaining(client.clone()) == Duration::from_secs(0) {
self.engine.start_hbbft_epoch(client);
Expand Down Expand Up @@ -1104,27 +1100,6 @@ impl HoneyBadgerBFT {
self.client.read().as_ref().and_then(Weak::upgrade)
}

fn start_hbbft_epoch_if_next_phase(&self) {
// experimental deactivation of empty blocks.
// see: https://github.com/DMDcoin/diamond-node/issues/160

match self.client_arc() {
None => return,
Some(client) => {
// Get the next phase start time
let genesis_transition_time = match start_time_of_next_phase_transition(&*client) {
Ok(time) => time,
Err(_) => return,
};

// If current time larger than phase start time, start a new block.
if genesis_transition_time.as_u64() < unix_now_secs() {
self.start_hbbft_epoch(client);
}
}
}
}

fn replay_cached_messages(&self) -> Option<()> {
let client = self.client_arc()?;

Expand Down Expand Up @@ -1236,12 +1211,12 @@ impl HoneyBadgerBFT {
return Ok(());
}

self.hbbft_peers_service
.channel()
.send(HbbftConnectToPeersMessage::AnnounceAvailability)?;

self.hbbft_peers_service
.send_message(HbbftConnectToPeersMessage::AnnounceOwnInternetAddress)?;
if self.is_staked() {
self.hbbft_peers_service
.send_message(HbbftConnectToPeersMessage::AnnounceAvailability)?;
self.hbbft_peers_service
.send_message(HbbftConnectToPeersMessage::AnnounceOwnInternetAddress)?;
}

if self.should_connect_to_validator_set() {
// we just keep those variables here, because we need them in the early_epoch_end_manager.
Expand Down
26 changes: 21 additions & 5 deletions crates/ethcore/src/engines/hbbft/hbbft_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,7 @@ impl HbbftState {
// apply DAO updates here.
// update the current minimum gas price.

match get_minimum_gas_from_permission_contract(
client.as_ref(),
BlockId::Number(self.current_posdao_epoch_start_block),
) {
match get_minimum_gas_from_permission_contract(client.as_ref(), BlockId::Latest) {
Ok(min_gas) => {
*current_minimum_gas_price.lock() = Some(min_gas);
}
Expand All @@ -222,10 +219,29 @@ impl HbbftState {
}

if sks.is_none() {
info!(target: "engine", "We are not part of the HoneyBadger validator set - running as regular node.");
info!(target: "engine", "We are not part of the HoneyBadger validator set - Running as regular node.");
peers_service
.send_message(HbbftConnectToPeersMessage::DisconnectAllValidators)
.ok()?;

if self.is_validator() {
let is_syncing = if let Some(full) = client.as_full_client() {
full.is_major_syncing()
} else {
info!(target: "engine", "Node was a validator: cannot be determinated, because client is not a full client. (https://github.com/DMDcoin/diamond-node/issues/322.)");
return Some(());
};

if is_syncing {
debug!(target: "engine", "Node was a validator, and became regular node, but we are syncing, not shutting down Node as defined in https://github.com/DMDcoin/diamond-node/issues/322.");
} else {
info!(target: "engine", "Node was a validator, and became regular node. shutting down Node as defined in https://github.com/DMDcoin/diamond-node/issues/322.");
// for unit tests no problem, demand shutddown won't to anything if its a unit test.
// e2e tests needs adaptation.
// this gracefully shuts down a node, if it was a validator before, but now it is not anymore.
client.demand_shutdown();
}
}
return Some(());
}

Expand Down
9 changes: 1 addition & 8 deletions crates/ethcore/src/engines/hbbft/test/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use super::{
contracts::{
staking::{
get_posdao_epoch, start_time_of_next_phase_transition,
get_posdao_epoch,
tests::{create_staker, is_pool_active},
},
validator_set::{is_pending_validator, mining_by_staking_address},
},
contribution::unix_now_secs,
test::hbbft_test_client::{HbbftTestClient, create_hbbft_client, create_hbbft_clients},
};
use crate::{client::traits::BlockInfo, types::ids::BlockId};
Expand Down Expand Up @@ -129,12 +128,6 @@ fn test_epoch_transition() {
// To avoid performing external transactions with the MoC we create and fund a random address.
let transactor: KeyPair = Random.generate();

let genesis_transition_time = start_time_of_next_phase_transition(moc.client.as_ref())
.expect("start_time_of_next_phase_transition call must succeed");

// Genesis block is at time 0, current unix time must be much larger.
assert!(genesis_transition_time.as_u64() < unix_now_secs());

// We should not be in the pending validator set at the genesis block.
assert!(
!is_pending_validator(moc.client.as_ref(), &moc.address())
Expand Down
11 changes: 11 additions & 0 deletions crates/ethcore/src/miner/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,17 @@ impl Miner {
self.service_transaction_checker.clone()
}

/// Performs garbage collection of the pool for free service transactions.
/// Removes transactions that are not valid anymore.
/// The process executes listener calls.
pub fn collect_garbage<F: Fn(&VerifiedTransaction) -> bool>(
&self,
service_transaction_filter: F,
) {
self.transaction_queue
.garbage_collect(service_transaction_filter);
}

/// Retrieves an existing pending block iff it's not older than given block number.
///
/// NOTE: This will not prepare a new pending block if it's not existing.
Expand Down
Loading
Loading