Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Avoid sending heartbeat if we are already considered online. #3981

Merged
merged 7 commits into from
Nov 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// and set impl_version to equal spec_version. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 191,
spec_version: 192,
impl_version: 191,
apis: RUNTIME_API_VERSIONS,
};
Expand Down
32 changes: 25 additions & 7 deletions srml/im-online/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ impl<T: Trait> Module<T> {
Err(err) => print(err),
}
} else {
debug::native::trace!(
debug::native::debug!(
target: "imonline",
"Skipping gossip at: {:?} >= {:?} || {:?}",
next_gossip,
Expand All @@ -382,6 +382,7 @@ impl<T: Trait> Module<T> {
fn do_gossip_at(block_number: T::BlockNumber) -> Result<(), OffchainErr> {
// we run only when a local authority key is configured
let authorities = Keys::<T>::get();
let mut results = Vec::new();
let mut local_keys = T::AuthorityId::all();
local_keys.sort();

Expand All @@ -393,6 +394,16 @@ impl<T: Trait> Module<T> {
.map(|location| (index as u32, &local_keys[location]))
})
{
if Self::is_online(authority_index) {
debug::native::info!(
target: "imonline",
"[index: {:?}] Skipping sending heartbeat at block: {:?}. Already online.",
authority_index,
block_number
);
continue;
}

let network_state = runtime_io::network_state().map_err(|_| OffchainErr::NetworkState)?;
let heartbeat_data = Heartbeat {
block_number,
Expand All @@ -410,14 +421,21 @@ impl<T: Trait> Module<T> {
authority_index,
block_number
);
T::SubmitTransaction::submit_unsigned(call)
.map_err(|_| OffchainErr::SubmitTransaction)?;

// once finished we set the worker status without comparing
// if the existing value changed in the meantime. this is
// because at this point the heartbeat was definitely submitted.
Self::set_worker_status(block_number, true);
results.push(
T::SubmitTransaction::submit_unsigned(call)
.map_err(|_| OffchainErr::SubmitTransaction)
);
}

// fail only after trying all keys.
results.into_iter().collect::<Result<Vec<_>, OffchainErr>>()?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(It will still fail after the first error)

Copy link
Contributor Author

@tomusdrw tomusdrw Oct 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's actually intended - I just don't want to break the loop on the first error. Since we now guard against is_online_in_current_session we should not produce a duplicate report , so we don't set the worker as finished - we will retry next time and just produce the ones that are missing.

But to be fair the error shouldn't really ever happen, it would only happen if the pool is full and cannot accept new transactions (although our have the highest priority so they should kick everything else) or the transaction is somehow incorrect.


// once finished we set the worker status without comparing
// if the existing value changed in the meantime. this is
// because at this point the heartbeat was definitely submitted.
Self::set_worker_status(block_number, true);

Ok(())
}

Expand Down
44 changes: 44 additions & 0 deletions srml/im-online/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,47 @@ fn should_mark_online_validator_when_block_is_authored() {
assert!(!ImOnline::is_online(2));
});
}

#[test]
fn should_not_send_a_report_if_already_online() {
use authorship::EventHandler;

let mut ext = new_test_ext();
let (offchain, state) = TestOffchainExt::new();
ext.register_extension(OffchainExt::new(offchain));

ext.execute_with(|| {
advance_session();
// given
VALIDATORS.with(|l| *l.borrow_mut() = Some(vec![1, 2, 3, 4, 5, 6]));
assert_eq!(Session::validators(), Vec::<u64>::new());
// enact the change and buffer another one
advance_session();
assert_eq!(Session::current_index(), 2);
assert_eq!(Session::validators(), vec![1, 2, 3]);
ImOnline::note_author(2);
ImOnline::note_uncle(3, 0);

// when
UintAuthorityId::set_all_keys(vec![0]); // all authorities use session key 0
ImOnline::offchain(4);

// then
let transaction = state.write().transactions.pop().unwrap();
// All validators have `0` as their session key, but we should only produce 1 hearbeat.
assert_eq!(state.read().transactions.len(), 0);
// check stuff about the transaction.
let ex: Extrinsic = Decode::decode(&mut &*transaction).unwrap();
let heartbeat = match ex.1 {
crate::mock::Call::ImOnline(crate::Call::heartbeat(h, _)) => h,
e => panic!("Unexpected call: {:?}", e),
};

assert_eq!(heartbeat, Heartbeat {
block_number: 4,
network_state: runtime_io::network_state().unwrap(),
session_index: 2,
authority_index: 0,
});
});
}