@@ -1685,7 +1685,8 @@ func (boot *baseBootstrap) rollBack(revertUsingForkNonce bool) error {
16851685 var currBody data.BodyHandler
16861686
16871687 defer func () {
1688- if ! roleBackOneBlockExecuted {
1688+ isHeaderV3 := ! check .IfNil (currHeader ) && currHeader .IsHeaderV3 ()
1689+ if ! roleBackOneBlockExecuted && ! isHeaderV3 {
16891690 err = boot .scheduledTxsExecutionHandler .RollBackToBlock (currHeaderHash )
16901691 if err != nil {
16911692 rootHash := boot .chainHandler .GetGenesisHeader ().GetRootHash ()
@@ -1712,7 +1713,9 @@ func (boot *baseBootstrap) rollBack(revertUsingForkNonce bool) error {
17121713 }
17131714
17141715 allowRollBack := boot .shouldAllowRollback (currHeader , currHeaderHash )
1715- if ! revertUsingForkNonce && ! allowRollBack {
1716+ // a header v3 switch must never cross the final checkpoint, not even fork-driven
1717+ isRollBackDenied := ! allowRollBack && (! revertUsingForkNonce || currHeader .IsHeaderV3 ())
1718+ if isRollBackDenied {
17161719 return ErrRollBackBehindFinalHeader
17171720 }
17181721
@@ -1735,13 +1738,22 @@ func (boot *baseBootstrap) rollBack(revertUsingForkNonce bool) error {
17351738 "nonce" , boot .forkDetector .GetHighestFinalBlockNonce (),
17361739 )
17371740
1738- currBody , err = boot .rollBackOneBlock (
1739- currHeaderHash ,
1740- currHeader ,
1741- prevHeaderHash ,
1742- prevHeader ,
1743- )
1744- roleBackOneBlockExecuted = true
1741+ if currHeader .IsHeaderV3 () {
1742+ currBody , err = boot .rollBackOneBlockV3 (
1743+ currHeaderHash ,
1744+ currHeader ,
1745+ prevHeaderHash ,
1746+ prevHeader ,
1747+ )
1748+ } else {
1749+ currBody , err = boot .rollBackOneBlock (
1750+ currHeaderHash ,
1751+ currHeader ,
1752+ prevHeaderHash ,
1753+ prevHeader ,
1754+ )
1755+ roleBackOneBlockExecuted = true
1756+ }
17451757 if err != nil {
17461758 return err
17471759 }
@@ -1765,15 +1777,17 @@ func (boot *baseBootstrap) rollBack(revertUsingForkNonce bool) error {
17651777 return err
17661778 }
17671779
1768- err = boot .scheduledTxsExecutionHandler .RollBackToBlock (prevHeaderHash )
1769- if err != nil {
1770- scheduledInfo := & process.ScheduledInfo {
1771- RootHash : prevHeader .GetRootHash (),
1772- IntermediateTxs : make (map [block.Type ][]data.TransactionHandler ),
1773- GasAndFees : process .GetZeroGasAndFees (),
1774- MiniBlocks : make (block.MiniBlockSlice , 0 ),
1780+ if ! currHeader .IsHeaderV3 () {
1781+ err = boot .scheduledTxsExecutionHandler .RollBackToBlock (prevHeaderHash )
1782+ if err != nil {
1783+ scheduledInfo := & process.ScheduledInfo {
1784+ RootHash : prevHeader .GetRootHash (),
1785+ IntermediateTxs : make (map [block.Type ][]data.TransactionHandler ),
1786+ GasAndFees : process .GetZeroGasAndFees (),
1787+ MiniBlocks : make (block.MiniBlockSlice , 0 ),
1788+ }
1789+ boot .scheduledTxsExecutionHandler .SetScheduledInfo (scheduledInfo )
17751790 }
1776- boot .scheduledTxsExecutionHandler .SetScheduledInfo (scheduledInfo )
17771791 }
17781792
17791793 err = boot .outportHandler .RevertIndexedBlock (& outportcore.HeaderDataWithBody {
@@ -1803,9 +1817,12 @@ func (boot *baseBootstrap) rollBack(revertUsingForkNonce bool) error {
18031817}
18041818
18051819func (boot * baseBootstrap ) shouldAllowRollback (currHeader data.HeaderHandler , currHeaderHash []byte ) bool {
1806- if check .IfNil (currHeader ) || currHeader . IsHeaderV3 () {
1820+ if check .IfNil (currHeader ) {
18071821 return false
18081822 }
1823+ if currHeader .IsHeaderV3 () {
1824+ return boot .shouldAllowRollbackV3 (currHeader )
1825+ }
18091826
18101827 finalBlockNonce := boot .forkDetector .GetHighestFinalBlockNonce ()
18111828 finalBlockHash := boot .forkDetector .GetHighestFinalBlockHash ()
@@ -1831,6 +1848,21 @@ func (boot *baseBootstrap) shouldAllowRollback(currHeader data.HeaderHandler, cu
18311848 return allowRollBack
18321849}
18331850
1851+ // shouldAllowRollbackV3 allows replacing a committed block only while it is not final (R-SWITCH);
1852+ // the state is never reverted through tries, the adopted sibling re-executes asynchronously
1853+ func (boot * baseBootstrap ) shouldAllowRollbackV3 (currHeader data.HeaderHandler ) bool {
1854+ finalBlockNonce := boot .forkDetector .GetHighestFinalBlockNonce ()
1855+ allowRollBack := currHeader .GetNonce () > finalBlockNonce
1856+
1857+ log .Debug ("baseBootstrap.shouldAllowRollbackV3" ,
1858+ "nonce" , currHeader .GetNonce (),
1859+ "final block nonce" , finalBlockNonce ,
1860+ "allowRollBack" , allowRollBack ,
1861+ )
1862+
1863+ return allowRollBack
1864+ }
1865+
18341866func (boot * baseBootstrap ) canRollbackBlock (currHeader data.HeaderHandler ) bool {
18351867 firstCommittedNonce := boot .blockProcessor .NonceOfFirstCommittedBlock ()
18361868
@@ -1889,6 +1921,51 @@ func (boot *baseBootstrap) rollBackOneBlock(
18891921 return currBlockBody , nil
18901922}
18911923
1924+ // rollBackOneBlockV3 reverts a committed, not yet final header so a same-nonce sibling can be
1925+ // adopted; the trie state is not reverted, the sibling's execution results are produced async
1926+ func (boot * baseBootstrap ) rollBackOneBlockV3 (
1927+ currHeaderHash []byte ,
1928+ currHeader data.HeaderHandler ,
1929+ prevHeaderHash []byte ,
1930+ prevHeader data.HeaderHandler ,
1931+ ) (data.BodyHandler , error ) {
1932+ err := boot .chainHandler .SetCurrentBlockHeaderAndHash (prevHeaderHash , prevHeader )
1933+ if err != nil {
1934+ return nil , err
1935+ }
1936+
1937+ defer func () {
1938+ if err != nil {
1939+ errNotCritical := boot .chainHandler .SetCurrentBlockHeaderAndHash (currHeaderHash , currHeader )
1940+ if errNotCritical != nil {
1941+ log .Warn ("rollBackOneBlockV3: cannot restore current block info" , "error" , errNotCritical )
1942+ }
1943+ }
1944+ }()
1945+
1946+ err = boot .executionManager .RemoveAtNonceAndHigher (currHeader .GetNonce ())
1947+ if err != nil {
1948+ return nil , err
1949+ }
1950+
1951+ currBlockBody , errNotCritical := boot .blockBootstrapper .getBlockBody (currHeader )
1952+ if errNotCritical != nil {
1953+ log .Debug ("rollBackOneBlockV3 getBlockBody error" , "error" , errNotCritical )
1954+ }
1955+
1956+ err = boot .blockProcessor .RestoreBlockIntoPools (currHeader , currBlockBody )
1957+ if err != nil {
1958+ return nil , err
1959+ }
1960+
1961+ hash := boot .removeHeaderFromPools (currHeader )
1962+ boot .forkDetector .RemoveCommittedHeader (currHeader .GetNonce (), hash )
1963+ nonceToByteSlice := boot .uint64Converter .ToByteSlice (currHeader .GetNonce ())
1964+ _ = boot .headerNonceHashStore .Remove (nonceToByteSlice )
1965+
1966+ return currBlockBody , nil
1967+ }
1968+
18921969func (boot * baseBootstrap ) getRootHashFromBlock (hdr data.HeaderHandler , hdrHash []byte ) []byte {
18931970 hdrRootHash := hdr .GetRootHash ()
18941971 scheduledHdrRootHash , err := boot .scheduledTxsExecutionHandler .GetScheduledRootHashForHeader (hdrHash )
0 commit comments