@@ -1820,6 +1820,116 @@ async fn grandpa_environment_checks_if_best_block_is_descendent_of_finality_targ
1820
1820
) ;
1821
1821
}
1822
1822
1823
+ // This is a regression test for an issue that was triggered by a reorg
1824
+ // - https://github.com/paritytech/polkadot-sdk/issues/3487
1825
+ // - https://github.com/humanode-network/humanode/issues/1104
1826
+ #[ tokio:: test]
1827
+ async fn grandpa_environment_uses_round_base_block_for_voting_if_finality_target_errors ( ) {
1828
+ use finality_grandpa:: voter:: Environment ;
1829
+ use sp_consensus:: SelectChain ;
1830
+
1831
+ let peers = & [ Ed25519Keyring :: Alice ] ;
1832
+ let voters = make_ids ( peers) ;
1833
+
1834
+ let mut net = GrandpaTestNet :: new ( TestApi :: new ( voters) , 1 , 0 ) ;
1835
+ let peer = net. peer ( 0 ) ;
1836
+ let network_service = peer. network_service ( ) . clone ( ) ;
1837
+ let sync_service = peer. sync_service ( ) . clone ( ) ;
1838
+ let notification_service =
1839
+ peer. take_notification_service ( & grandpa_protocol_name:: NAME . into ( ) ) . unwrap ( ) ;
1840
+ let link = peer. data . lock ( ) . take ( ) . unwrap ( ) ;
1841
+ let client = peer. client ( ) . as_client ( ) . clone ( ) ;
1842
+ let select_chain = sc_consensus:: LongestChain :: new ( peer. client ( ) . as_backend ( ) ) ;
1843
+
1844
+ // create a chain that is 10 blocks long
1845
+ peer. push_blocks ( 10 , false ) ;
1846
+
1847
+ let env = test_environment_with_select_chain (
1848
+ & link,
1849
+ None ,
1850
+ network_service. clone ( ) ,
1851
+ sync_service,
1852
+ notification_service,
1853
+ select_chain. clone ( ) ,
1854
+ VotingRulesBuilder :: default ( ) . build ( ) ,
1855
+ ) ;
1856
+
1857
+ let hashof7 = client. expect_block_hash_from_id ( & BlockId :: Number ( 7 ) ) . unwrap ( ) ;
1858
+ let hashof8_a = client. expect_block_hash_from_id ( & BlockId :: Number ( 8 ) ) . unwrap ( ) ;
1859
+
1860
+ // finalize the 7th block
1861
+ peer. client ( ) . finalize_block ( hashof7, None , false ) . unwrap ( ) ;
1862
+
1863
+ assert_eq ! ( peer. client( ) . info( ) . finalized_hash, hashof7) ;
1864
+
1865
+ // simulate completed grandpa round
1866
+ env. completed (
1867
+ 1 ,
1868
+ finality_grandpa:: round:: State {
1869
+ prevote_ghost : Some ( ( hashof8_a, 8 ) ) ,
1870
+ finalized : Some ( ( hashof7, 7 ) ) ,
1871
+ estimate : Some ( ( hashof8_a, 8 ) ) ,
1872
+ completable : true ,
1873
+ } ,
1874
+ Default :: default ( ) ,
1875
+ & finality_grandpa:: HistoricalVotes :: new ( ) ,
1876
+ )
1877
+ . unwrap ( ) ;
1878
+
1879
+ // check simulated last completed round
1880
+ assert_eq ! (
1881
+ env. voter_set_state. read( ) . last_completed_round( ) . state,
1882
+ finality_grandpa:: round:: State {
1883
+ prevote_ghost: Some ( ( hashof8_a, 8 ) ) ,
1884
+ finalized: Some ( ( hashof7, 7 ) ) ,
1885
+ estimate: Some ( ( hashof8_a, 8 ) ) ,
1886
+ completable: true
1887
+ }
1888
+ ) ;
1889
+
1890
+ // `hashof8_a` should be finalized next, `best_chain_containing` should return `hashof8_a`
1891
+ assert_eq ! ( env. best_chain_containing( hashof8_a) . await . unwrap( ) . unwrap( ) . 0 , hashof8_a) ;
1892
+
1893
+ // simulate reorg on block 8 by creating a fork starting at block 7 that is 10 blocks long
1894
+ peer. generate_blocks_at (
1895
+ BlockId :: Number ( 7 ) ,
1896
+ 10 ,
1897
+ BlockOrigin :: File ,
1898
+ |mut builder| {
1899
+ builder. push_deposit_log_digest_item ( DigestItem :: Other ( vec ! [ 1 ] ) ) . unwrap ( ) ;
1900
+ builder. build ( ) . unwrap ( ) . block
1901
+ } ,
1902
+ false ,
1903
+ false ,
1904
+ true ,
1905
+ ForkChoiceStrategy :: LongestChain ,
1906
+ ) ;
1907
+
1908
+ // check that new best chain is on longest chain
1909
+ assert_eq ! ( env. select_chain. best_chain( ) . await . unwrap( ) . number, 17 ) ;
1910
+
1911
+ // verify that last completed round has `prevote_ghost` and `estimate` blocks related to
1912
+ // `hashof8_a`
1913
+ assert_eq ! (
1914
+ env. voter_set_state. read( ) . last_completed_round( ) . state,
1915
+ finality_grandpa:: round:: State {
1916
+ prevote_ghost: Some ( ( hashof8_a, 8 ) ) ,
1917
+ finalized: Some ( ( hashof7, 7 ) ) ,
1918
+ estimate: Some ( ( hashof8_a, 8 ) ) ,
1919
+ completable: true
1920
+ }
1921
+ ) ;
1922
+
1923
+ // `hashof8_a` should be finalized next, `best_chain_containing` should still return `hashof8_a`
1924
+ assert_eq ! ( env. best_chain_containing( hashof8_a) . await . unwrap( ) . unwrap( ) . 0 , hashof8_a) ;
1925
+
1926
+ // simulate finalization of the `hashof8_a` block
1927
+ peer. client ( ) . finalize_block ( hashof8_a, None , false ) . unwrap ( ) ;
1928
+
1929
+ // check that best chain is reorged back
1930
+ assert_eq ! ( env. select_chain. best_chain( ) . await . unwrap( ) . number, 10 ) ;
1931
+ }
1932
+
1823
1933
#[ tokio:: test]
1824
1934
async fn grandpa_environment_never_overwrites_round_voter_state ( ) {
1825
1935
use finality_grandpa:: voter:: Environment ;
0 commit comments