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

Commit c4dd1be

Browse files
davxyandresilva
andauthored
Grandpa revert procedure (#11162)
* Grandpa revert procedure * Trigger ci pipeline * Test rename * Update client/finality-grandpa/src/authorities.rs Co-authored-by: André Silva <[email protected]>
1 parent 184d29c commit c4dd1be

File tree

7 files changed

+224
-12
lines changed

7 files changed

+224
-12
lines changed

bin/node-template/node/src/command.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,11 @@ pub fn run() -> sc_cli::Result<()> {
9595
runner.async_run(|config| {
9696
let PartialComponents { client, task_manager, backend, .. } =
9797
service::new_partial(&config)?;
98-
Ok((cmd.run(client, backend, None), task_manager))
98+
let aux_revert = Box::new(move |client, _, blocks| {
99+
sc_finality_grandpa::revert(client, blocks)?;
100+
Ok(())
101+
});
102+
Ok((cmd.run(client, backend, Some(aux_revert)), task_manager))
99103
})
100104
},
101105
Some(Subcommand::Benchmark(cmd)) =>

bin/node/cli/src/command.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
// You should have received a copy of the GNU General Public License
1717
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1818

19-
use crate::{chain_spec, service, service::new_partial, Cli, Subcommand};
19+
use crate::{
20+
chain_spec, service,
21+
service::{new_partial, FullClient},
22+
Cli, Subcommand,
23+
};
2024
use node_executor::ExecutorDispatch;
2125
use node_primitives::Block;
2226
use node_runtime::RuntimeApi;
@@ -175,13 +179,12 @@ pub fn run() -> Result<()> {
175179
let runner = cli.create_runner(cmd)?;
176180
runner.async_run(|config| {
177181
let PartialComponents { client, task_manager, backend, .. } = new_partial(&config)?;
178-
let revert_aux = Box::new(|client, backend, blocks| {
179-
sc_consensus_babe::revert(client, backend, blocks)?;
180-
// TODO: grandpa revert
182+
let aux_revert = Box::new(move |client: Arc<FullClient>, backend, blocks| {
183+
sc_consensus_babe::revert(client.clone(), backend, blocks)?;
184+
grandpa::revert(client, blocks)?;
181185
Ok(())
182186
});
183-
184-
Ok((cmd.run(client, backend, Some(revert_aux)), task_manager))
187+
Ok((cmd.run(client, backend, Some(aux_revert)), task_manager))
185188
})
186189
},
187190
#[cfg(feature = "try-runtime")]

client/consensus/babe/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1832,7 +1832,9 @@ where
18321832
Ok(BasicQueue::new(verifier, Box::new(block_import), justification_import, spawner, registry))
18331833
}
18341834

1835-
/// Reverts aux data.
1835+
/// Reverts protocol aux data to at most the last finalized block.
1836+
/// In particular, epoch-changes and block weights announced after the revert
1837+
/// point are removed.
18361838
pub fn revert<Block, Client, Backend>(
18371839
client: Arc<Client>,
18381840
backend: Arc<Backend>,

client/finality-grandpa/src/authorities.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
use std::{cmp::Ord, fmt::Debug, ops::Add};
2222

2323
use finality_grandpa::voter_set::VoterSet;
24-
use fork_tree::ForkTree;
24+
use fork_tree::{FilterAction, ForkTree};
2525
use log::debug;
2626
use parity_scale_codec::{Decode, Encode};
2727
use parking_lot::MappedMutexGuard;
@@ -220,6 +220,37 @@ where
220220
pub(crate) fn current(&self) -> (u64, &[(AuthorityId, u64)]) {
221221
(self.set_id, &self.current_authorities[..])
222222
}
223+
224+
/// Revert to a specified block given its `hash` and `number`.
225+
/// This removes all the authority set changes that were announced after
226+
/// the revert point.
227+
/// Revert point is identified by `number` and `hash`.
228+
pub(crate) fn revert<F, E>(&mut self, hash: H, number: N, is_descendent_of: &F)
229+
where
230+
F: Fn(&H, &H) -> Result<bool, E>,
231+
{
232+
let mut filter = |node_hash: &H, node_num: &N, _: &PendingChange<H, N>| {
233+
if number >= *node_num &&
234+
(is_descendent_of(node_hash, &hash).unwrap_or_default() || *node_hash == hash)
235+
{
236+
// Continue the search in this subtree.
237+
FilterAction::KeepNode
238+
} else if number < *node_num && is_descendent_of(&hash, node_hash).unwrap_or_default() {
239+
// Found a node to be removed.
240+
FilterAction::Remove
241+
} else {
242+
// Not a parent or child of the one we're looking for, stop processing this branch.
243+
FilterAction::KeepTree
244+
}
245+
};
246+
247+
// Remove standard changes.
248+
let _ = self.pending_standard_changes.drain_filter(&mut filter);
249+
250+
// Remove forced changes.
251+
self.pending_forced_changes
252+
.retain(|change| !is_descendent_of(&hash, &change.canon_hash).unwrap_or_default());
253+
}
223254
}
224255

225256
impl<H: Eq, N> AuthoritySet<H, N>

client/finality-grandpa/src/lib.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ use parking_lot::RwLock;
6363
use prometheus_endpoint::{PrometheusError, Registry};
6464
use sc_client_api::{
6565
backend::{AuxStore, Backend},
66+
utils::is_descendent_of,
6667
BlockchainEvents, CallExecutor, ExecutionStrategy, ExecutorProvider, Finalizer, LockImportRun,
6768
StorageProvider, TransactionFor,
6869
};
@@ -71,7 +72,7 @@ use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO};
7172
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver};
7273
use sp_api::ProvideRuntimeApi;
7374
use sp_application_crypto::AppKey;
74-
use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata};
75+
use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata, Result as ClientResult};
7576
use sp_consensus::SelectChain;
7677
use sp_core::crypto::ByteArray;
7778
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
@@ -1162,3 +1163,49 @@ fn local_authority_id(
11621163
.map(|(p, _)| p.clone())
11631164
})
11641165
}
1166+
1167+
/// Reverts protocol aux data to at most the last finalized block.
1168+
/// In particular, standard and forced authority set changes announced after the
1169+
/// revert point are removed.
1170+
pub fn revert<Block, Client>(client: Arc<Client>, blocks: NumberFor<Block>) -> ClientResult<()>
1171+
where
1172+
Block: BlockT,
1173+
Client: AuxStore
1174+
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
1175+
+ HeaderBackend<Block>
1176+
+ ProvideRuntimeApi<Block>,
1177+
{
1178+
let best_number = client.info().best_number;
1179+
let finalized = client.info().finalized_number;
1180+
let revertible = blocks.min(best_number - finalized);
1181+
1182+
let number = best_number - revertible;
1183+
let hash = client
1184+
.block_hash_from_id(&BlockId::Number(number))?
1185+
.ok_or(ClientError::Backend(format!(
1186+
"Unexpected hash lookup failure for block number: {}",
1187+
number
1188+
)))?;
1189+
1190+
let info = client.info();
1191+
let persistent_data: PersistentData<Block> =
1192+
aux_schema::load_persistent(&*client, info.genesis_hash, Zero::zero(), || unreachable!())?;
1193+
1194+
let shared_authority_set = persistent_data.authority_set;
1195+
let mut authority_set = shared_authority_set.inner();
1196+
1197+
let is_descendent_of = is_descendent_of(&*client, None);
1198+
authority_set.revert(hash, number, &is_descendent_of);
1199+
1200+
// The following has the side effect to properly reset the current voter state.
1201+
let (set_id, set_ref) = authority_set.current();
1202+
let new_set = Some(NewAuthoritySet {
1203+
canon_hash: info.finalized_hash,
1204+
canon_number: info.finalized_number,
1205+
set_id,
1206+
authorities: set_ref.to_vec(),
1207+
});
1208+
aux_schema::update_authority_set::<Block, _, _>(&authority_set, new_set.as_ref(), |values| {
1209+
client.insert_aux(values, None)
1210+
})
1211+
}

client/finality-grandpa/src/tests.rs

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ use tokio::runtime::{Handle, Runtime};
5757

5858
use authorities::AuthoritySet;
5959
use communication::grandpa_protocol_name;
60-
use sc_block_builder::BlockBuilderProvider;
60+
use sc_block_builder::{BlockBuilder, BlockBuilderProvider};
6161
use sc_consensus::LongestChain;
6262
use sc_keystore::LocalKeystore;
6363
use sp_application_crypto::key_types::GRANDPA;
@@ -1685,3 +1685,128 @@ fn grandpa_environment_doesnt_send_equivocation_reports_for_itself() {
16851685
let equivocation_proof = sp_finality_grandpa::Equivocation::Prevote(equivocation);
16861686
assert!(environment.report_equivocation(equivocation_proof).is_ok());
16871687
}
1688+
1689+
#[test]
1690+
fn revert_prunes_authority_changes() {
1691+
sp_tracing::try_init_simple();
1692+
let runtime = Runtime::new().unwrap();
1693+
1694+
let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie];
1695+
1696+
type TestBlockBuilder<'a> =
1697+
BlockBuilder<'a, Block, PeersFullClient, substrate_test_runtime_client::Backend>;
1698+
let edit_block = |builder: TestBlockBuilder| {
1699+
let mut block = builder.build().unwrap().block;
1700+
add_scheduled_change(
1701+
&mut block,
1702+
ScheduledChange { next_authorities: make_ids(peers), delay: 0 },
1703+
);
1704+
block
1705+
};
1706+
1707+
let api = TestApi::new(make_ids(peers));
1708+
let mut net = GrandpaTestNet::new(api, 3, 0);
1709+
runtime.spawn(initialize_grandpa(&mut net, peers));
1710+
1711+
let peer = net.peer(0);
1712+
let client = peer.client().as_client();
1713+
1714+
// Test scenario: (X) = auth-change, 24 = revert-point
1715+
//
1716+
// +---------(27)
1717+
// /
1718+
// 0---(21)---23---24---25---(28)---30
1719+
// ^ \
1720+
// revert-point +------(29)
1721+
1722+
// Construct canonical chain
1723+
1724+
// add 20 blocks
1725+
peer.push_blocks(20, false);
1726+
// at block 21 we add an authority transition
1727+
peer.generate_blocks(1, BlockOrigin::File, edit_block);
1728+
// add more blocks on top of it (until we have 24)
1729+
peer.push_blocks(3, false);
1730+
// add more blocks on top of it (until we have 27)
1731+
peer.push_blocks(3, false);
1732+
// at block 28 we add an authority transition
1733+
peer.generate_blocks(1, BlockOrigin::File, edit_block);
1734+
// add more blocks on top of it (until we have 30)
1735+
peer.push_blocks(2, false);
1736+
1737+
// Fork before revert point
1738+
1739+
// add more blocks on top of block 23 (until we have 26)
1740+
let hash = peer.generate_blocks_at(
1741+
BlockId::Number(23),
1742+
3,
1743+
BlockOrigin::File,
1744+
|builder| {
1745+
let mut block = builder.build().unwrap().block;
1746+
block.header.digest_mut().push(DigestItem::Other(vec![1]));
1747+
block
1748+
},
1749+
false,
1750+
false,
1751+
true,
1752+
ForkChoiceStrategy::LongestChain,
1753+
);
1754+
// at block 27 of the fork add an authority transition
1755+
peer.generate_blocks_at(
1756+
BlockId::Hash(hash),
1757+
1,
1758+
BlockOrigin::File,
1759+
edit_block,
1760+
false,
1761+
false,
1762+
true,
1763+
ForkChoiceStrategy::LongestChain,
1764+
);
1765+
1766+
// Fork after revert point
1767+
1768+
// add more block on top of block 25 (until we have 28)
1769+
let hash = peer.generate_blocks_at(
1770+
BlockId::Number(25),
1771+
3,
1772+
BlockOrigin::File,
1773+
|builder| {
1774+
let mut block = builder.build().unwrap().block;
1775+
block.header.digest_mut().push(DigestItem::Other(vec![2]));
1776+
block
1777+
},
1778+
false,
1779+
false,
1780+
true,
1781+
ForkChoiceStrategy::LongestChain,
1782+
);
1783+
// at block 29 of the fork add an authority transition
1784+
peer.generate_blocks_at(
1785+
BlockId::Hash(hash),
1786+
1,
1787+
BlockOrigin::File,
1788+
edit_block,
1789+
false,
1790+
false,
1791+
true,
1792+
ForkChoiceStrategy::LongestChain,
1793+
);
1794+
1795+
revert(client.clone(), 6).unwrap();
1796+
1797+
let persistent_data: PersistentData<Block> = aux_schema::load_persistent(
1798+
&*client,
1799+
client.info().genesis_hash,
1800+
Zero::zero(),
1801+
|| unreachable!(),
1802+
)
1803+
.unwrap();
1804+
let changes_num: Vec<_> = persistent_data
1805+
.authority_set
1806+
.inner()
1807+
.pending_standard_changes
1808+
.iter()
1809+
.map(|(_, n, _)| *n)
1810+
.collect();
1811+
assert_eq!(changes_num, [21, 27]);
1812+
}

client/network/test/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ where
334334

335335
/// Add blocks to the peer -- edit the block before adding. The chain will
336336
/// start at the given block iD.
337-
fn generate_blocks_at<F>(
337+
pub fn generate_blocks_at<F>(
338338
&mut self,
339339
at: BlockId<Block>,
340340
count: usize,

0 commit comments

Comments
 (0)